Compare commits

...

19 Commits

Author SHA1 Message Date
a8381e875b feat: change all occurrences when a object name is changed (#1252) 2022-11-02 00:17:38 +08:00
4c81fd7d16 feat: fix generating wrong x.509 private key file header (#1253)
According to the [official x509 documentation](https://pkg.go.dev/crypto/x509#MarshalPKCS1PrivateKey), the private key generated using `x509.MarshalPKCS1PrivateKey` starts with `-----BEGIN RSA PRIVATE KEY-----` instead of `-----BEGIN PRIVATE KEY-----`. Otherwise, it will not be parsed by most tools (like OpenSSL, [jwt.io](https://jwt.io/), etc.) because it does not conform to the specification.
2022-11-01 22:19:38 +08:00
25ee4226d3 feat: clear the session of a signin but non-existent user (#1246) 2022-10-29 20:18:02 +08:00
9d5b019243 fix: nil error if init data is empty (#1247) 2022-10-29 20:04:43 +08:00
6bb7b545b4 feat: restrict DingTalk user log in who is under the DingTalk Org(which ClientId belong) (#1241)
* feat: fix bug in GetAcceptLanguage()

* feat: add appName when logging in with DingTalk

* fix review problems

* format code

* delete useless printf

* modify display name

Co-authored-by: Gucheng Wang <nomeguy@qq.com>
2022-10-28 22:14:05 +08:00
25d56ee8d5 feat: allow captcha to be enabled when logging in (#1211)
* Fix bug in GetAcceptLanguage()

* feat: allow captcha to be enabled when logging in

* feat: when the login password is wrong, enable captcha

* feat: Restrict captcha from frontend

* fix: modify CaptchaModal component

* fix: modify the words of i18n

* Update data.json

Co-authored-by: Gucheng Wang <nomeguy@qq.com>
Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-28 13:38:14 +08:00
7e5952c804 fix: login / signin frontend router (#1244)
* fix: go to link

* fix: remove gotologin

* fix: redirect to login page

* fix: redirect to login page

* remove comments

* fix: formats

* fix: formats

* Update Setting.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-28 02:23:57 +08:00
80bf29d79a feat: fix showing wrong error message: "Please sign in first" (#1245) 2022-10-27 23:50:45 +08:00
971e53dfd8 fix: fix duplicated user bug in user list page (#1243)
* fix: user list repititon errer

* Update UserListPage.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-27 22:51:05 +08:00
654b903d7a feat: fix multi-platform docker image (#1242) 2022-10-26 23:31:00 +08:00
2f72e6971b fix: make the app list in homepage have the same height (#1239)
* fix: make the app list in homepage have the same height

* fix: make the app list in homepage have the same height

* Update SingleCard.js

Co-authored-by: hsluoyz <hsluoyz@qq.com>
2022-10-25 19:27:24 +08:00
d4b587b93e feat: fix bug in GetAcceptLanguage() (#1237)
Co-authored-by: Gucheng Wang <nomeguy@qq.com>
2022-10-25 10:50:10 +08:00
ac7a510949 Fix go.mod 2022-10-23 16:14:49 +08:00
d86f3c88c7 feat: support i18n in backend err messages (#1232)
* feat: support i18n in backend err messages

* use gofumpt to fmt code

* fix review problems

* support auto generate err message

* delete beego/i18n moudle

* fix Github action test problems

* fix review problems

* use gofumpt to format code

* use gofumpt to fmt code
2022-10-23 15:16:24 +08:00
7c77519069 Fix formPosition typo 2022-10-23 02:26:50 +08:00
2bdf467e3a Update formCss default value 2022-10-23 01:27:01 +08:00
52b692c8ad Refactor to renderLink() 2022-10-22 23:48:59 +08:00
304643736b fix: forget password and sign up router (#1227)
* fix: forget password and sign up router

* fix: link

* fix: jump logic

* fix: signup link

* fix: signup link

* fix: login and signup router

* remove comments

* fix: normal router

* fix: link abstraction

* rename jump component

* fix: session storage

* fix: store signin url

* fix: jumplink props

* fix: simplify link

* fix: path join

* fix: remove unused functions
2022-10-22 23:17:50 +08:00
b0f572c51a feat: add left-side image and improve login page (#1226) 2022-10-22 21:43:41 +08:00
102 changed files with 3158 additions and 517 deletions

View File

@ -14,6 +14,9 @@ RUN ./build.sh
FROM alpine:latest AS STANDARD FROM alpine:latest AS STANDARD
LABEL MAINTAINER="https://casdoor.org/" LABEL MAINTAINER="https://casdoor.org/"
ARG USER=casdoor ARG USER=casdoor
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN sed -i 's/https/http/' /etc/apk/repositories RUN sed -i 's/https/http/' /etc/apk/repositories
RUN apk add --update sudo RUN apk add --update sudo
@ -28,7 +31,7 @@ RUN adduser -D $USER -u 1000 \
USER 1000 USER 1000
WORKDIR / WORKDIR /
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server ./server COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger
COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/conf/app.conf ./conf/app.conf
COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build
@ -46,12 +49,15 @@ RUN apt update \
FROM db AS ALLINONE FROM db AS ALLINONE
LABEL MAINTAINER="https://casdoor.org/" LABEL MAINTAINER="https://casdoor.org/"
ARG TARGETOS
ARG TARGETARCH
ENV BUILDX_ARCH="${TARGETOS:-linux}_${TARGETARCH:-amd64}"
RUN apt update RUN apt update
RUN apt install -y ca-certificates && update-ca-certificates RUN apt install -y ca-certificates && update-ca-certificates
WORKDIR / WORKDIR /
COPY --from=BACK /go/src/casdoor/server ./server COPY --from=BACK /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger COPY --from=BACK /go/src/casdoor/swagger ./swagger
COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh COPY --from=BACK /go/src/casdoor/docker-entrypoint.sh /docker-entrypoint.sh
COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf COPY --from=BACK /go/src/casdoor/conf/app.conf ./conf/app.conf

View File

@ -8,4 +8,5 @@ else
echo "Google is blocked, Go proxy is enabled: GOPROXY=https://goproxy.cn,direct" echo "Google is blocked, Go proxy is enabled: GOPROXY=https://goproxy.cn,direct"
export GOPROXY="https://goproxy.cn,direct" export GOPROXY="https://goproxy.cn,direct"
fi fi
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server . CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-w -s" -o server_linux_amd64 .
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-w -s" -o server_linux_arm64 .

View File

@ -14,6 +14,8 @@
package captcha package captcha
import "fmt"
type CaptchaProvider interface { type CaptchaProvider interface {
VerifyCaptcha(token, clientSecret string) (bool, error) VerifyCaptcha(token, clientSecret string) (bool, error)
} }
@ -32,3 +34,12 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
} }
return nil return nil
} }
func VerifyCaptchaByCaptchaType(captchaType, token, clientSecret string) (bool, error) {
provider := GetCaptchaProvider(captchaType)
if provider == nil {
return false, fmt.Errorf("invalid captcha provider: %s", captchaType)
}
return provider.VerifyCaptcha(token, clientSecret)
}

View File

@ -20,3 +20,4 @@ staticBaseUrl = "https://cdn.casbin.org"
isDemoMode = false isDemoMode = false
batchSize = 100 batchSize = 100
ldapServerPort = 389 ldapServerPort = 389
languages = en,zh,es,fr,de,ja,ko,ru

View File

@ -64,6 +64,10 @@ type RequestForm struct {
RelayState string `json:"relayState"` RelayState string `json:"relayState"`
SamlRequest string `json:"samlRequest"` SamlRequest string `json:"samlRequest"`
SamlResponse string `json:"samlResponse"` SamlResponse string `json:"samlResponse"`
CaptchaType string `json:"captchaType"`
CaptchaToken string `json:"captchaToken"`
ClientSecret string `json:"clientSecret"`
} }
type Response struct { type Response struct {
@ -98,7 +102,7 @@ type Captcha struct {
// @router /signup [post] // @router /signup [post]
func (c *ApiController) Signup() { func (c *ApiController) Signup() {
if c.GetSessionUsername() != "" { if c.GetSessionUsername() != "" {
c.ResponseError("Please sign out first before signing up", c.GetSessionUsername()) c.ResponseError(c.T("SignUpErr.SignOutFirst"), c.GetSessionUsername())
return return
} }
@ -111,21 +115,21 @@ func (c *ApiController) Signup() {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if !application.EnableSignUp { if !application.EnableSignUp {
c.ResponseError("The application does not allow to sign up new account") c.ResponseError(c.T("SignUpErr.DoNotAllowSignUp"))
return return
} }
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization)) organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", form.Organization))
msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation) msg := object.CheckUserSignup(application, organization, form.Username, form.Password, form.Name, form.FirstName, form.LastName, form.Email, form.Phone, form.Affiliation, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
} }
if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" { if application.IsSignupItemVisible("Email") && application.GetSignupItemRule("Email") != "No verification" && form.Email != "" {
checkResult := object.CheckVerificationCode(form.Email, form.EmailCode) checkResult := object.CheckVerificationCode(form.Email, form.EmailCode, c.GetAcceptLanguage())
if len(checkResult) != 0 { if len(checkResult) != 0 {
c.ResponseError(fmt.Sprintf("Email: %s", checkResult)) c.ResponseError(c.T("EmailErr.EmailCheckResult"), checkResult)
return return
} }
} }
@ -133,9 +137,9 @@ func (c *ApiController) Signup() {
var checkPhone string var checkPhone string
if application.IsSignupItemVisible("Phone") && form.Phone != "" { if application.IsSignupItemVisible("Phone") && form.Phone != "" {
checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone) checkPhone = fmt.Sprintf("+%s%s", form.PhonePrefix, form.Phone)
checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode) checkResult := object.CheckVerificationCode(checkPhone, form.PhoneCode, c.GetAcceptLanguage())
if len(checkResult) != 0 { if len(checkResult) != 0 {
c.ResponseError(fmt.Sprintf("Phone: %s", checkResult)) c.ResponseError(c.T("PhoneErr.PhoneCheckResult"), checkResult)
return return
} }
} }
@ -159,7 +163,7 @@ func (c *ApiController) Signup() {
initScore, err := getInitScore() initScore, err := getInitScore()
if err != nil { if err != nil {
c.ResponseError(fmt.Errorf("get init score failed, error: %w", err).Error()) c.ResponseError(fmt.Errorf(c.T("InitErr.InitScoreFailed"), err).Error())
return return
} }
@ -205,7 +209,7 @@ func (c *ApiController) Signup() {
affected := object.AddUser(user) affected := object.AddUser(user)
if !affected { if !affected {
c.ResponseError(fmt.Sprintf("Failed to create user, user information is invalid: %s", util.StructToJson(user))) c.ResponseError(c.T("UserErr.InvalidInformation"), util.StructToJson(user))
return return
} }
@ -241,8 +245,7 @@ func (c *ApiController) Logout() {
util.LogInfo(c.Ctx, "API: [%s] logged out", user) util.LogInfo(c.Ctx, "API: [%s] logged out", user)
application := c.GetSessionApplication() application := c.GetSessionApplication()
c.SetSessionUsername("") c.ClearUserSession()
c.SetSessionData(nil)
if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" { if application == nil || application.Name == "app-built-in" || application.HomepageUrl == "" {
c.ResponseOk(user) c.ResponseOk(user)
@ -309,7 +312,7 @@ func (c *ApiController) GetCaptcha() {
applicationId := c.Input().Get("applicationId") applicationId := c.Input().Get("applicationId")
isCurrentProvider := c.Input().Get("isCurrentProvider") isCurrentProvider := c.Input().Get("isCurrentProvider")
captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider) captchaProvider, err := object.GetCaptchaProviderByApplication(applicationId, isCurrentProvider, c.GetAcceptLanguage())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -86,7 +86,7 @@ func (c *ApiController) GetUserApplication() {
id := c.Input().Get("id") id := c.Input().Get("id")
user := object.GetUser(id) user := object.GetUser(id)
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", id)) c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), id))
return return
} }
@ -107,7 +107,7 @@ func (c *ApiController) GetOrganizationApplications() {
organization := c.Input().Get("organization") organization := c.Input().Get("organization")
if organization == "" { if organization == "" {
c.ResponseError("Parameter organization is missing") c.ResponseError(c.T("ParameterErr.OrgMissingErr"))
return return
} }

View File

@ -23,6 +23,8 @@ import (
"strings" "strings"
"time" "time"
"github.com/casdoor/casdoor/captcha"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -56,7 +58,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
return return
} }
if !allowed { if !allowed {
c.ResponseError("Unauthorized operation") c.ResponseError(c.T("AuthErr.Unauthorized"))
return return
} }
@ -75,10 +77,10 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
codeChallenge := c.Input().Get("code_challenge") codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" { if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError("Challenge method should be S256") c.ResponseError(c.T("AuthErr.ChallengeMethodErr"))
return return
} }
code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host) code := object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, c.Ctx.Request.Host, c.GetAcceptLanguage())
resp = codeToResponse(code) resp = codeToResponse(code)
if application.EnableSigninSession || application.HasPromptPage() { if application.EnableSigninSession || application.HasPromptPage() {
@ -151,7 +153,7 @@ func (c *ApiController) GetApplicationLogin() {
scope := c.Input().Get("scope") scope := c.Input().Get("scope")
state := c.Input().Get("state") state := c.Input().Get("state")
msg, application := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state) msg, application := object.CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, c.GetAcceptLanguage())
application = object.GetMaskedApplication(application, "") application = object.GetMaskedApplication(application, "")
if msg != "" { if msg != "" {
c.ResponseError(msg, application) c.ResponseError(msg, application)
@ -196,7 +198,7 @@ func (c *ApiController) Login() {
if form.Username != "" { if form.Username != "" {
if form.Type == ResponseTypeLogin { if form.Type == ResponseTypeLogin {
if c.GetSessionUsername() != "" { if c.GetSessionUsername() != "" {
c.ResponseError("Please sign out first before signing in", c.GetSessionUsername()) c.ResponseError(c.T("LoginErr.SignOutFirst"), c.GetSessionUsername())
return return
} }
} }
@ -218,11 +220,11 @@ func (c *ApiController) Login() {
if user != nil && util.GetMaskedEmail(user.Email) == form.Username { if user != nil && util.GetMaskedEmail(user.Email) == form.Username {
form.Username = user.Email form.Username = user.Email
} }
checkResult = object.CheckVerificationCode(form.Username, form.Code) checkResult = object.CheckVerificationCode(form.Username, form.Code, c.GetAcceptLanguage())
} else { } else {
verificationCodeType = "phone" verificationCodeType = "phone"
if len(form.PhonePrefix) == 0 { if len(form.PhonePrefix) == 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, "No phone prefix") responseText := fmt.Sprintf(c.T("PhoneErr.NoPrefix"), verificationCodeType)
c.ResponseError(responseText) c.ResponseError(responseText)
return return
} }
@ -230,7 +232,7 @@ func (c *ApiController) Login() {
form.Username = user.Phone form.Username = user.Phone
} }
checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username) checkPhone := fmt.Sprintf("+%s%s", form.PhonePrefix, form.Username)
checkResult = object.CheckVerificationCode(checkPhone, form.Code) checkResult = object.CheckVerificationCode(checkPhone, form.Code, c.GetAcceptLanguage())
} }
if len(checkResult) != 0 { if len(checkResult) != 0 {
responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult) responseText := fmt.Sprintf("%s%s", verificationCodeType, checkResult)
@ -247,12 +249,31 @@ func (c *ApiController) Login() {
user = object.GetUserByFields(form.Organization, form.Username) user = object.GetUserByFields(form.Organization, form.Username)
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s/%s doesn't exist", form.Organization, form.Username)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.UserDoNotExist"), form.Organization, form.Username))
return return
} }
} else { } else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil {
c.ResponseError(fmt.Sprintf("The application: %s does not exist", form.Application))
return
}
if object.CheckToEnableCaptcha(application) {
isHuman, err := captcha.VerifyCaptchaByCaptchaType(form.CaptchaType, form.CaptchaToken, form.ClientSecret)
if err != nil {
c.ResponseError(err.Error())
return
}
if !isHuman {
c.ResponseError("Turing test failed.")
return
}
}
password := form.Password password := form.Password
user, msg = object.CheckUserPassword(form.Organization, form.Username, password) user, msg = object.CheckUserPassword(form.Organization, form.Username, password, c.GetAcceptLanguage())
} }
if msg != "" { if msg != "" {
@ -260,7 +281,7 @@ func (c *ApiController) Login() {
} else { } else {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf("The application: %s does not exist", form.Application)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppDoNotExist"), form.Application))
return return
} }
@ -274,7 +295,7 @@ func (c *ApiController) Login() {
} else if form.Provider != "" { } else if form.Provider != "" {
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf("The application: %s does not exist", form.Application)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppDoNotExist"), form.Application))
return return
} }
@ -282,7 +303,7 @@ func (c *ApiController) Login() {
provider := object.GetProvider(fmt.Sprintf("admin/%s", form.Provider)) provider := object.GetProvider(fmt.Sprintf("admin/%s", form.Provider))
providerItem := application.GetProviderItem(provider.Name) providerItem := application.GetProviderItem(provider.Name)
if !providerItem.IsProviderVisible() { if !providerItem.IsProviderVisible() {
c.ResponseError(fmt.Sprintf("The provider: %s is not enabled for the application", provider.Name)) c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotEnabled"), provider.Name))
return return
} }
@ -306,14 +327,14 @@ func (c *ApiController) Login() {
idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl) idProvider := idp.GetIdProvider(provider.Type, provider.SubType, clientId, clientSecret, provider.AppId, form.RedirectUri, provider.Domain, provider.CustomAuthUrl, provider.CustomTokenUrl, provider.CustomUserInfoUrl)
if idProvider == nil { if idProvider == nil {
c.ResponseError(fmt.Sprintf("The provider type: %s is not supported", provider.Type)) c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotSupported"), provider.Type))
return return
} }
setHttpClient(idProvider, provider.Type) setHttpClient(idProvider, provider.Type)
if form.State != conf.GetConfigString("authState") && form.State != application.Name { if form.State != conf.GetConfigString("authState") && form.State != application.Name {
c.ResponseError(fmt.Sprintf("state expected: \"%s\", but got: \"%s\"", conf.GetConfigString("authState"), form.State)) c.ResponseError(fmt.Sprintf(c.T("AuthErr.AuthStateWrong"), conf.GetConfigString("authState"), form.State))
return return
} }
@ -325,13 +346,13 @@ func (c *ApiController) Login() {
} }
if !token.Valid() { if !token.Valid() {
c.ResponseError("Invalid token") c.ResponseError(c.T("TokenErr.InvalidToken"))
return return
} }
userInfo, err = idProvider.GetUserInfo(token) userInfo, err = idProvider.GetUserInfo(token)
if err != nil { if err != nil {
c.ResponseError(fmt.Sprintf("Failed to login in: %s", err.Error())) c.ResponseError(fmt.Sprintf(c.T("LoginErr.LoginFail"), err.Error()))
return return
} }
} }
@ -348,7 +369,7 @@ func (c *ApiController) Login() {
// Sign in via OAuth (want to sign up but already have account) // Sign in via OAuth (want to sign up but already have account)
if user.IsForbidden { if user.IsForbidden {
c.ResponseError("the user is forbidden to sign in, please contact the administrator") c.ResponseError(c.T("LoginErr.UserIsForbidden"))
} }
resp = c.HandleLoggedIn(application, user, &form) resp = c.HandleLoggedIn(application, user, &form)
@ -360,12 +381,12 @@ func (c *ApiController) Login() {
} else if provider.Category == "OAuth" { } else if provider.Category == "OAuth" {
// Sign up via OAuth // Sign up via OAuth
if !application.EnableSignUp { if !application.EnableSignUp {
c.ResponseError(fmt.Sprintf("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", provider.Type, userInfo.Username, userInfo.DisplayName)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppNotEnableSignUp"), provider.Type, userInfo.Username, userInfo.DisplayName))
return return
} }
if !providerItem.CanSignUp { if !providerItem.CanSignUp {
c.ResponseError(fmt.Sprintf("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", provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.ProviderCanNotSignUp"), provider.Type, userInfo.Username, userInfo.DisplayName, provider.Type))
return return
} }
@ -386,7 +407,7 @@ func (c *ApiController) Login() {
properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2) properties["no"] = strconv.Itoa(len(object.GetUsers(application.Organization)) + 2)
initScore, err := getInitScore() initScore, err := getInitScore()
if err != nil { if err != nil {
c.ResponseError(fmt.Errorf("get init score failed, error: %w", err).Error()) c.ResponseError(fmt.Errorf(c.T("InitErr.InitScoreFailed"), err).Error())
return return
} }
@ -413,7 +434,7 @@ func (c *ApiController) Login() {
affected := object.AddUser(user) affected := object.AddUser(user)
if !affected { if !affected {
c.ResponseError(fmt.Sprintf("Failed to create user, user information is invalid: %s", util.StructToJson(user))) c.ResponseError(fmt.Sprintf(c.T("LoginErr.InvalidUserInformation"), util.StructToJson(user)))
return return
} }
@ -438,13 +459,13 @@ func (c *ApiController) Login() {
} else { // form.Method != "signup" } else { // form.Method != "signup"
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("The account does not exist", userInfo) c.ResponseError(c.T("LoginErr.AccountDoNotExist"), userInfo)
return return
} }
oldUser := object.GetUserByField(application.Organization, provider.Type, userInfo.Id) oldUser := object.GetUserByField(application.Organization, provider.Type, userInfo.Id)
if oldUser != nil { if oldUser != nil {
c.ResponseError(fmt.Sprintf("The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)", provider.Type, userInfo.Username, userInfo.DisplayName, oldUser.Name, oldUser.DisplayName)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.OldUser"), provider.Type, userInfo.Username, userInfo.DisplayName, oldUser.Name, oldUser.DisplayName))
return return
} }
@ -465,7 +486,7 @@ func (c *ApiController) Login() {
// user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in // user already signed in to Casdoor, so let the user click the avatar button to do the quick sign-in
application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application)) application := object.GetApplication(fmt.Sprintf("admin/%s", form.Application))
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf("The application: %s does not exist", form.Application)) c.ResponseError(fmt.Sprintf(c.T("LoginErr.AppDoNotExist"), form.Application))
return return
} }
@ -477,7 +498,7 @@ func (c *ApiController) Login() {
record.User = user.Name record.User = user.Name
util.SafeGoroutine(func() { object.AddRecord(record) }) util.SafeGoroutine(func() { object.AddRecord(record) })
} else { } else {
c.ResponseError(fmt.Sprintf("unknown authentication type (not password or provider), form = %s", util.StructToJson(form))) c.ResponseError(fmt.Sprintf(c.T("LoginErr.UnknownAuthentication"), util.StructToJson(form)))
return return
} }
} }
@ -489,7 +510,7 @@ func (c *ApiController) Login() {
func (c *ApiController) GetSamlLogin() { func (c *ApiController) GetSamlLogin() {
providerId := c.Input().Get("id") providerId := c.Input().Get("id")
relayState := c.Input().Get("relayState") relayState := c.Input().Get("relayState")
authURL, method, err := object.GenerateSamlLoginUrl(providerId, relayState) authURL, method, err := object.GenerateSamlLoginUrl(providerId, relayState, c.GetAcceptLanguage())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
} }

View File

@ -63,8 +63,7 @@ func (c *ApiController) GetSessionUsername() string {
if sessionData != nil && if sessionData != nil &&
sessionData.ExpireTime != 0 && sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() { sessionData.ExpireTime < time.Now().Unix() {
c.SetSessionUsername("") c.ClearUserSession()
c.SetSessionData(nil)
return "" return ""
} }
@ -85,13 +84,17 @@ func (c *ApiController) GetSessionApplication() *object.Application {
return application return application
} }
func (c *ApiController) ClearUserSession() {
c.SetSessionUsername("")
c.SetSessionData(nil)
}
func (c *ApiController) GetSessionOidc() (string, string) { func (c *ApiController) GetSessionOidc() (string, string) {
sessionData := c.GetSessionData() sessionData := c.GetSessionData()
if sessionData != nil && if sessionData != nil &&
sessionData.ExpireTime != 0 && sessionData.ExpireTime != 0 &&
sessionData.ExpireTime < time.Now().Unix() { sessionData.ExpireTime < time.Now().Unix() {
c.SetSessionUsername("") c.ClearUserSession()
c.SetSessionData(nil)
return "", "" return "", ""
} }
scopeValue := c.GetSession("scope") scopeValue := c.GetSession("scope")

View File

@ -210,7 +210,7 @@ func (c *RootController) SamlValidate() {
} }
if !strings.HasPrefix(target, service) { if !strings.HasPrefix(target, service) {
c.ResponseError(fmt.Sprintf("service %s and %s do not match", target, service)) c.ResponseError(fmt.Sprintf(c.T("CasErr.ServiceDoNotMatch"), target, service))
return return
} }

View File

@ -23,7 +23,7 @@ import (
func (c *ApiController) Enforce() { func (c *ApiController) Enforce() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please sign in first") c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return return
} }
@ -41,7 +41,7 @@ func (c *ApiController) Enforce() {
func (c *ApiController) BatchEnforce() { func (c *ApiController) BatchEnforce() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please sign in first") c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return return
} }
@ -59,7 +59,7 @@ func (c *ApiController) BatchEnforce() {
func (c *ApiController) GetAllObjects() { func (c *ApiController) GetAllObjects() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please sign in first") c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return return
} }
@ -70,7 +70,7 @@ func (c *ApiController) GetAllObjects() {
func (c *ApiController) GetAllActions() { func (c *ApiController) GetAllActions() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please sign in first") c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return return
} }
@ -81,7 +81,7 @@ func (c *ApiController) GetAllActions() {
func (c *ApiController) GetAllRoles() { func (c *ApiController) GetAllRoles() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please sign in first") c.ResponseError(c.T("EnforcerErr.SignInFirst"))
return return
} }

View File

@ -52,7 +52,7 @@ func (c *ApiController) GetLdapUser() {
ldapServer := LdapServer{} ldapServer := LdapServer{}
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldapServer)
if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) { if err != nil || util.IsStrsEmpty(ldapServer.Host, ldapServer.Admin, ldapServer.Passwd, ldapServer.BaseDn) {
c.ResponseError("Missing parameter") c.ResponseError(c.T("ParameterErr.Missing"))
return return
} }
@ -120,7 +120,7 @@ func (c *ApiController) GetLdap() {
id := c.Input().Get("id") id := c.Input().Get("id")
if util.IsStrsEmpty(id) { if util.IsStrsEmpty(id) {
c.ResponseError("Missing parameter") c.ResponseError(c.T("ParameterErr.Missing"))
return return
} }
@ -136,17 +136,17 @@ func (c *ApiController) AddLdap() {
var ldap object.Ldap var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil { if err != nil {
c.ResponseError("Missing parameter") c.ResponseError(c.T("ParameterErr.Missing"))
return return
} }
if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { if util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError("Missing parameter") c.ResponseError(c.T("ParameterErr.Missing"))
return return
} }
if object.CheckLdapExist(&ldap) { if object.CheckLdapExist(&ldap) {
c.ResponseError("Ldap server exist") c.ResponseError(c.T("LdapErr.ServerExisted"))
return return
} }
@ -171,7 +171,7 @@ func (c *ApiController) UpdateLdap() {
var ldap object.Ldap var ldap object.Ldap
err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap) err := json.Unmarshal(c.Ctx.Input.RequestBody, &ldap)
if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) { if err != nil || util.IsStrsEmpty(ldap.Owner, ldap.ServerName, ldap.Host, ldap.Admin, ldap.Passwd, ldap.BaseDn) {
c.ResponseError("Missing parameter") c.ResponseError(c.T("ParameterErr.Missing"))
return return
} }

View File

@ -49,7 +49,7 @@ func handleBind(w ldapserver.ResponseWriter, m *ldapserver.Message) {
return return
} }
bindpassword := string(r.AuthenticationSimple()) bindpassword := string(r.AuthenticationSimple())
binduser, err := object.CheckUserPassword(bindorg, bindusername, bindpassword) binduser, err := object.CheckUserPassword(bindorg, bindusername, bindpassword, "en")
if err != "" { if err != "" {
log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err) log.Printf("Bind failed User=%s, Pass=%#v, ErrMsg=%s", string(r.Name()), r.Authentication(), err)
res.SetResultCode(ldapserver.LDAPResultInvalidCredentials) res.SetResultCode(ldapserver.LDAPResultInvalidCredentials)

View File

@ -47,7 +47,7 @@ func (c *ApiController) Unlink() {
if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin { if user.Id != unlinkedUser.Id && !user.IsGlobalAdmin {
// if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin. // if the user is not the same as the one we are unlinking, we need to make sure the user is the global admin.
c.ResponseError("You are not the global admin, you can't unlink other users") c.ResponseError(c.T("AuthErr.CanNotUnlinkUsers"))
return return
} }
@ -55,23 +55,23 @@ func (c *ApiController) Unlink() {
// if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error. // if the user is unlinking themselves, should check the provider can be unlinked, if not, we should return an error.
application := object.GetApplicationByUser(user) application := object.GetApplicationByUser(user)
if application == nil { if application == nil {
c.ResponseError("You can't unlink yourself, you are not a member of any application") c.ResponseError(c.T("AuthErr.CanNotLinkMySelf"))
return return
} }
if len(application.Providers) == 0 { if len(application.Providers) == 0 {
c.ResponseError("This application has no providers") c.ResponseError(c.T("ApplicationErr.HasNoProviders"))
return return
} }
provider := application.GetProviderItemByType(providerType) provider := application.GetProviderItemByType(providerType)
if provider == nil { if provider == nil {
c.ResponseError("This application has no providers of type " + providerType) c.ResponseError(c.T("ApplicationErr.HasNoProvidersOfType") + providerType)
return return
} }
if !provider.CanUnlink { if !provider.CanUnlink {
c.ResponseError("This provider can't be unlinked") c.ResponseError(c.T("ProviderErr.CanNotBeUnlinked"))
return return
} }
@ -84,7 +84,7 @@ func (c *ApiController) Unlink() {
value := object.GetUserField(&unlinkedUser, providerType) value := object.GetUserField(&unlinkedUser, providerType)
if value == "" { if value == "" {
c.ResponseError("Please link first", value) c.ResponseError(c.T("ProviderErr.LinkFirstErr"), value)
return return
} }

View File

@ -141,13 +141,13 @@ func (c *ApiController) BuyProduct() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please login first") c.ResponseError(c.T("LoginErr.LoginFirst"))
return return
} }
user := object.GetUser(userId) user := object.GetUser(userId)
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", userId)) c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), userId))
return return
} }

View File

@ -113,7 +113,7 @@ func (c *ApiController) DeleteResource() {
return return
} }
err = object.DeleteFile(provider, resource.Name) err = object.DeleteFile(provider, resource.Name, c.GetAcceptLanguage())
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -145,7 +145,7 @@ func (c *ApiController) UploadResource() {
defer file.Close() defer file.Close()
if username == "" || fullFilePath == "" { if username == "" || fullFilePath == "" {
c.ResponseError(fmt.Sprintf("username or fullFilePath is empty: username = %s, fullFilePath = %s", username, fullFilePath)) c.ResponseError(fmt.Sprintf(c.T("ResourceErr.UsernameOrFilePathEmpty"), username, fullFilePath))
return return
} }
@ -205,7 +205,7 @@ func (c *ApiController) UploadResource() {
if user == nil { if user == nil {
user = object.GetUserNoCheck(username) user = object.GetUserNoCheck(username)
if user == nil { if user == nil {
c.ResponseError("user is nil for tag: \"avatar\"") c.ResponseError(c.T("ResourceErr.UserIsNil"))
return return
} }
} }

View File

@ -25,7 +25,7 @@ func (c *ApiController) GetSamlMeta() {
paramApp := c.Input().Get("application") paramApp := c.Input().Get("application")
application := object.GetApplication(paramApp) application := object.GetApplication(paramApp)
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf("err: application %s not found", paramApp)) c.ResponseError(fmt.Sprintf(c.T("ApplicationErr.AppNotFound"), paramApp))
return return
} }
metadata, _ := object.GetSamlMeta(application, host) metadata, _ := object.GetSamlMeta(application, host)

View File

@ -81,7 +81,7 @@ func (c *ApiController) SendEmail() {
} }
if util.IsStrsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) { if util.IsStrsEmpty(emailForm.Title, emailForm.Content, emailForm.Sender) {
c.ResponseError(fmt.Sprintf("Empty parameters for emailForm: %v", emailForm)) c.ResponseError(fmt.Sprintf(c.T("EmailErr.EmptyParam"), emailForm))
return return
} }
@ -93,7 +93,7 @@ func (c *ApiController) SendEmail() {
} }
if len(invalidReceivers) != 0 { if len(invalidReceivers) != 0 {
c.ResponseError(fmt.Sprintf("Invalid Email receivers: %s", invalidReceivers)) c.ResponseError(fmt.Sprintf(c.T("EmailErr.InvalidReceivers"), invalidReceivers))
return return
} }
@ -141,7 +141,7 @@ func (c *ApiController) SendSms() {
} }
if len(invalidReceivers) != 0 { if len(invalidReceivers) != 0 {
c.ResponseError(fmt.Sprintf("Invalid phone receivers: %s", invalidReceivers)) c.ResponseError(fmt.Sprintf(c.T("PhoneErr.InvalidReceivers"), invalidReceivers))
return return
} }

View File

@ -40,7 +40,7 @@ func (c *ApiController) GetSystemInfo() {
user := object.GetUser(id) user := object.GetUser(id)
if user == nil || !user.IsGlobalAdmin { if user == nil || !user.IsGlobalAdmin {
c.ResponseError("You are not authorized to access this resource") c.ResponseError(c.T("ResourceErr.NotAuthorized"))
return return
} }

View File

@ -150,12 +150,12 @@ func (c *ApiController) GetOAuthCode() {
codeChallenge := c.Input().Get("code_challenge") codeChallenge := c.Input().Get("code_challenge")
if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" { if challengeMethod != "S256" && challengeMethod != "null" && challengeMethod != "" {
c.ResponseError("Challenge method should be S256") c.ResponseError(c.T("AuthErr.ChallengeMethodErr"))
return return
} }
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, host) c.Data["json"] = object.GetOAuthCode(userId, clientId, responseType, redirectUri, scope, state, nonce, codeChallenge, host, c.GetAcceptLanguage())
c.ServeJSON() c.ServeJSON()
} }
@ -204,7 +204,7 @@ func (c *ApiController) GetOAuthToken() {
} }
host := c.Ctx.Request.Host host := c.Ctx.Request.Host
c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, tag, avatar) c.Data["json"] = object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, tag, avatar, c.GetAcceptLanguage())
c.SetTokenErrorHttpStatus() c.SetTokenErrorHttpStatus()
c.ServeJSON() c.ServeJSON()
} }
@ -290,7 +290,7 @@ func (c *ApiController) IntrospectToken() {
clientId = c.Input().Get("client_id") clientId = c.Input().Get("client_id")
clientSecret = c.Input().Get("client_secret") clientSecret = c.Input().Get("client_secret")
if clientId == "" || clientSecret == "" { if clientId == "" || clientSecret == "" {
c.ResponseError("empty clientId or clientSecret") c.ResponseError(c.T("TokenErr.EmptyClientID"))
c.Data["json"] = &object.TokenError{ c.Data["json"] = &object.TokenError{
Error: object.InvalidRequest, Error: object.InvalidRequest,
} }
@ -301,7 +301,7 @@ func (c *ApiController) IntrospectToken() {
} }
application := object.GetApplicationByClientId(clientId) application := object.GetApplicationByClientId(clientId)
if application == nil || application.ClientSecret != clientSecret { if application == nil || application.ClientSecret != clientSecret {
c.ResponseError("invalid application or wrong clientSecret") c.ResponseError(c.T("TokenErr.InvalidAppOrWrongClientSecret"))
c.Data["json"] = &object.TokenError{ c.Data["json"] = &object.TokenError{
Error: object.InvalidClient, Error: object.InvalidClient,
} }

View File

@ -100,7 +100,7 @@ func (c *ApiController) GetUser() {
organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", owner)) organization := object.GetOrganization(fmt.Sprintf("%s/%s", "admin", owner))
if !organization.IsProfilePublic { if !organization.IsProfilePublic {
requestUserId := c.GetSessionUsername() requestUserId := c.GetSessionUsername()
hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false) hasPermission, err := object.CheckUserPermission(requestUserId, id, owner, false, c.GetAcceptLanguage())
if !hasPermission { if !hasPermission {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -149,7 +149,7 @@ func (c *ApiController) UpdateUser() {
} }
if user.DisplayName == "" { if user.DisplayName == "" {
c.ResponseError("Display name cannot be empty") c.ResponseError(c.T("UserErr.DisplayNameCanNotBeEmpty"))
return return
} }
@ -183,7 +183,7 @@ func (c *ApiController) AddUser() {
return return
} }
msg := object.CheckUsername(user.Name) msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
@ -230,7 +230,7 @@ func (c *ApiController) GetEmailAndPhone() {
user := object.GetUserByFields(form.Organization, form.Username) user := object.GetUserByFields(form.Organization, form.Username)
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s/%s doesn't exist", form.Organization, form.Username)) c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExistInOrg"), form.Organization, form.Username))
return return
} }
@ -271,7 +271,7 @@ func (c *ApiController) SetPassword() {
requestUserId := c.GetSessionUsername() requestUserId := c.GetSessionUsername()
userId := fmt.Sprintf("%s/%s", userOwner, userName) userId := fmt.Sprintf("%s/%s", userOwner, userName)
hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true) hasPermission, err := object.CheckUserPermission(requestUserId, userId, userOwner, true, c.GetAcceptLanguage())
if !hasPermission { if !hasPermission {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -280,7 +280,7 @@ func (c *ApiController) SetPassword() {
targetUser := object.GetUser(userId) targetUser := object.GetUser(userId)
if oldPassword != "" { if oldPassword != "" {
msg := object.CheckPassword(targetUser, oldPassword) msg := object.CheckPassword(targetUser, oldPassword, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
@ -288,12 +288,12 @@ func (c *ApiController) SetPassword() {
} }
if strings.Contains(newPassword, " ") { if strings.Contains(newPassword, " ") {
c.ResponseError("New password cannot contain blank space.") c.ResponseError(c.T("SetPasswordErr.CanNotContainBlank"))
return return
} }
if len(newPassword) <= 5 { if len(newPassword) <= 5 {
c.ResponseError("New password must have at least 6 characters") c.ResponseError(c.T("SetPasswordErr.LessThanSixCharacters"))
return return
} }
@ -315,7 +315,7 @@ func (c *ApiController) CheckUserPassword() {
return return
} }
_, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password) _, msg := object.CheckUserPassword(user.Owner, user.Name, user.Password, c.GetAcceptLanguage())
if msg == "" { if msg == "" {
c.ResponseOk() c.ResponseOk()
} else { } else {

View File

@ -61,6 +61,6 @@ func (c *ApiController) UploadUsers() {
if affected { if affected {
c.ResponseOk() c.ResponseOk()
} else { } else {
c.ResponseError("Failed to import users") c.ResponseError(c.T("UserErr.FailToImportUsers"))
} }
} }

View File

@ -19,6 +19,7 @@ import (
"strconv" "strconv"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
@ -48,6 +49,19 @@ func (c *ApiController) ResponseError(error string, data ...interface{}) {
c.ResponseJsonData(resp, data...) c.ResponseJsonData(resp, data...)
} }
func (c *ApiController) T(error string) string {
return i18n.Translate(c.GetAcceptLanguage(), error)
}
// GetAcceptLanguage ...
func (c *ApiController) GetAcceptLanguage() string {
lang := c.Ctx.Request.Header.Get("Accept-Language")
if lang == "" {
lang = "en"
}
return lang[0:2]
}
// SetTokenErrorHttpStatus ... // SetTokenErrorHttpStatus ...
func (c *ApiController) SetTokenErrorHttpStatus() { func (c *ApiController) SetTokenErrorHttpStatus() {
_, ok := c.Data["json"].(*object.TokenError) _, ok := c.Data["json"].(*object.TokenError)
@ -69,7 +83,7 @@ func (c *ApiController) SetTokenErrorHttpStatus() {
func (c *ApiController) RequireSignedIn() (string, bool) { func (c *ApiController) RequireSignedIn() (string, bool) {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
if userId == "" { if userId == "" {
c.ResponseError("Please sign in first") c.ResponseError(c.T("LoginErr.LoginFirst"), "Please login first")
return "", false return "", false
} }
return userId, true return userId, true
@ -84,7 +98,8 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
user := object.GetUser(userId) user := object.GetUser(userId)
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s doesn't exist", userId)) c.ClearUserSession()
c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), userId))
return nil, false return nil, false
} }
return user, true return user, true
@ -112,7 +127,7 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
if providerName != "" { if providerName != "" {
provider := object.GetProvider(util.GetId(providerName)) provider := object.GetProvider(util.GetId(providerName))
if provider == nil { if provider == nil {
c.ResponseError(fmt.Sprintf("The provider: %s is not found", providerName)) c.ResponseError(c.T("ProviderErr.ProviderNotFound"), providerName)
return nil, nil, false return nil, nil, false
} }
return provider, nil, true return provider, nil, true
@ -125,13 +140,13 @@ func (c *ApiController) GetProviderFromContext(category string) (*object.Provide
application, user := object.GetApplicationByUserId(userId) application, user := object.GetApplicationByUserId(userId)
if application == nil { if application == nil {
c.ResponseError(fmt.Sprintf("No application is found for userId: \"%s\"", userId)) c.ResponseError(fmt.Sprintf(c.T("ApplicationErr.AppNotFoundForUserID"), userId))
return nil, nil, false return nil, nil, false
} }
provider := application.GetProviderByCategory(category) provider := application.GetProviderByCategory(category)
if provider == nil { if provider == nil {
c.ResponseError(fmt.Sprintf("No provider for category: \"%s\" is found for application: %s", category, application.Name)) c.ResponseError(fmt.Sprintf(c.T("ProviderErr.ProviderNotFoundForCategory"), category, application.Name))
return nil, nil, false return nil, nil, false
} }

View File

@ -50,23 +50,23 @@ func (c *ApiController) SendVerificationCode() {
remoteAddr := util.GetIPFromRequest(c.Ctx.Request) remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" { if destType == "" {
c.ResponseError("Missing parameter: type.") c.ResponseError(c.T("ParameterErr.Missing") + ": type.")
return return
} }
if dest == "" { if dest == "" {
c.ResponseError("Missing parameter: dest.") c.ResponseError(c.T("ParameterErr.Missing") + ": dest.")
return return
} }
if applicationId == "" { if applicationId == "" {
c.ResponseError("Missing parameter: applicationId.") c.ResponseError(c.T("ParameterErr.Missing") + ": applicationId.")
return return
} }
if !strings.Contains(applicationId, "/") { if !strings.Contains(applicationId, "/") {
c.ResponseError("Wrong parameter: applicationId.") c.ResponseError(c.T("ParameterErr.Wrong") + ": applicationId.")
return return
} }
if checkType == "" { if checkType == "" {
c.ResponseError("Missing parameter: checkType.") c.ResponseError(c.T("ParameterErr.Missing") + ": checkType.")
return return
} }
@ -74,7 +74,7 @@ func (c *ApiController) SendVerificationCode() {
if captchaProvider != nil { if captchaProvider != nil {
if checkKey == "" { if checkKey == "" {
c.ResponseError("Missing parameter: checkKey.") c.ResponseError(c.T("ParameterErr.Missing") + ": checkKey.")
return return
} }
isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId) isHuman, err := captchaProvider.VerifyCaptcha(checkKey, checkId)
@ -84,7 +84,7 @@ func (c *ApiController) SendVerificationCode() {
} }
if !isHuman { if !isHuman {
c.ResponseError("Turing test failed.") c.ResponseError(c.T("AuthErr.NotHuman"))
return return
} }
} }
@ -94,7 +94,7 @@ func (c *ApiController) SendVerificationCode() {
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization)) organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization))
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil { if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
c.ResponseError("Please login first") c.ResponseError(c.T("LoginErr.LoginFirst"))
return return
} }
@ -110,7 +110,7 @@ func (c *ApiController) SendVerificationCode() {
dest = user.Email dest = user.Email
} }
if !util.IsEmailValid(dest) { if !util.IsEmailValid(dest) {
c.ResponseError("Invalid Email address") c.ResponseError(c.T("EmailErr.EmailInvalid"))
return return
} }
@ -121,11 +121,11 @@ func (c *ApiController) SendVerificationCode() {
dest = user.Phone dest = user.Phone
} }
if !util.IsPhoneCnValid(dest) { if !util.IsPhoneCnValid(dest) {
c.ResponseError("Invalid phone number") c.ResponseError(c.T("PhoneErr.NumberInvalid"))
return return
} }
if organization == nil { if organization == nil {
c.ResponseError("The organization doesn't exist.") c.ResponseError(c.T("OrgErr.DoNotExist"))
return return
} }
@ -157,7 +157,7 @@ func (c *ApiController) ResetEmailOrPhone() {
dest := c.Ctx.Request.Form.Get("dest") dest := c.Ctx.Request.Form.Get("dest")
code := c.Ctx.Request.Form.Get("code") code := c.Ctx.Request.Form.Get("code")
if len(dest) == 0 || len(code) == 0 || len(destType) == 0 { if len(dest) == 0 || len(code) == 0 || len(destType) == 0 {
c.ResponseError("Missing parameter.") c.ResponseError(c.T("ParameterErr.Missing"))
return return
} }
@ -166,11 +166,11 @@ func (c *ApiController) ResetEmailOrPhone() {
if destType == "phone" { if destType == "phone" {
phoneItem := object.GetAccountItemByName("Phone", org) phoneItem := object.GetAccountItemByName("Phone", org)
if phoneItem == nil { if phoneItem == nil {
c.ResponseError("Unable to get the phone modify rule.") c.ResponseError(c.T("PhoneErr.UnableGetModifyRule"))
return return
} }
if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user); !pass { if pass, errMsg := object.CheckAccountItemModifyRule(phoneItem, user, c.GetAcceptLanguage()); !pass {
c.ResponseError(errMsg) c.ResponseError(errMsg)
return return
} }
@ -183,16 +183,16 @@ func (c *ApiController) ResetEmailOrPhone() {
} else if destType == "email" { } else if destType == "email" {
emailItem := object.GetAccountItemByName("Email", org) emailItem := object.GetAccountItemByName("Email", org)
if emailItem == nil { if emailItem == nil {
c.ResponseError("Unable to get the email modify rule.") c.ResponseError(c.T("EmailErr.UnableGetModifyRule"))
return return
} }
if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user); !pass { if pass, errMsg := object.CheckAccountItemModifyRule(emailItem, user, c.GetAcceptLanguage()); !pass {
c.ResponseError(errMsg) c.ResponseError(errMsg)
return return
} }
} }
if ret := object.CheckVerificationCode(checkDest, code); len(ret) != 0 { if ret := object.CheckVerificationCode(checkDest, code, c.GetAcceptLanguage()); len(ret) != 0 {
c.ResponseError(ret) c.ResponseError(ret)
return return
} }
@ -205,7 +205,7 @@ func (c *ApiController) ResetEmailOrPhone() {
user.Phone = dest user.Phone = dest
object.SetUserField(user, "phone", user.Phone) object.SetUserField(user, "phone", user.Phone)
default: default:
c.ResponseError("Unknown type.") c.ResponseError(c.T("ParameterErr.UnknownType"))
return return
} }
@ -224,17 +224,17 @@ func (c *ApiController) VerifyCaptcha() {
captchaToken := c.Ctx.Request.Form.Get("captchaToken") captchaToken := c.Ctx.Request.Form.Get("captchaToken")
clientSecret := c.Ctx.Request.Form.Get("clientSecret") clientSecret := c.Ctx.Request.Form.Get("clientSecret")
if captchaToken == "" { if captchaToken == "" {
c.ResponseError("Missing parameter: captchaToken.") c.ResponseError(c.T("ParameterErr.Missing") + ": captchaToken.")
return return
} }
if clientSecret == "" { if clientSecret == "" {
c.ResponseError("Missing parameter: clientSecret.") c.ResponseError(c.T("ParameterErr.Missing") + ": clientSecret.")
return return
} }
provider := captcha.GetCaptchaProvider(captchaType) provider := captcha.GetCaptchaProvider(captchaType)
if provider == nil { if provider == nil {
c.ResponseError("Invalid captcha provider.") c.ResponseError(c.T("ProviderErr.InvalidProvider"))
return return
} }

View File

@ -35,7 +35,7 @@ func (c *ApiController) WebAuthnSignupBegin() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host) webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser() user := c.getCurrentUser()
if user == nil { if user == nil {
c.ResponseError("Please login first.") c.ResponseError(c.T("LoginErr.LoginFirst"))
return return
} }
@ -66,13 +66,13 @@ func (c *ApiController) WebAuthnSignupFinish() {
webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host) webauthnObj := object.GetWebAuthnObject(c.Ctx.Request.Host)
user := c.getCurrentUser() user := c.getCurrentUser()
if user == nil { if user == nil {
c.ResponseError("Please login first.") c.ResponseError(c.T("LoginErr.LoginFirst"))
return return
} }
sessionObj := c.GetSession("registration") sessionObj := c.GetSession("registration")
sessionData, ok := sessionObj.(webauthn.SessionData) sessionData, ok := sessionObj.(webauthn.SessionData)
if !ok { if !ok {
c.ResponseError("Please call WebAuthnSignupBegin first") c.ResponseError(c.T("AuthErr.CallWebAuthnSigninBegin"))
return return
} }
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody)) c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))
@ -101,7 +101,7 @@ func (c *ApiController) WebAuthnSigninBegin() {
userName := c.Input().Get("name") userName := c.Input().Get("name")
user := object.GetUserByFields(userOwner, userName) user := object.GetUserByFields(userOwner, userName)
if user == nil { if user == nil {
c.ResponseError(fmt.Sprintf("The user: %s/%s doesn't exist", userOwner, userName)) c.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExistInOrg"), userOwner, userName))
return return
} }
options, sessionData, err := webauthnObj.BeginLogin(user) options, sessionData, err := webauthnObj.BeginLogin(user)
@ -127,7 +127,7 @@ func (c *ApiController) WebAuthnSigninFinish() {
sessionObj := c.GetSession("authentication") sessionObj := c.GetSession("authentication")
sessionData, ok := sessionObj.(webauthn.SessionData) sessionData, ok := sessionObj.(webauthn.SessionData)
if !ok { if !ok {
c.ResponseError("Please call WebAuthnSigninBegin first") c.ResponseError(c.T("AuthErr.CallWebAuthnSigninBegin"))
return return
} }
c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody)) c.Ctx.Request.Body = io.NopCloser(bytes.NewBuffer(c.Ctx.Input.RequestBody))

3
go.mod
View File

@ -4,6 +4,7 @@ go 1.16
require ( require (
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b
github.com/Unknwon/goconfig v1.0.0
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/aws/aws-sdk-go v1.44.4 github.com/aws/aws-sdk-go v1.44.4
github.com/beego/beego v1.12.11 github.com/beego/beego v1.12.11
@ -47,7 +48,7 @@ require (
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/ini.v1 v1.62.0 // indirect gopkg.in/ini.v1 v1.67.0
gopkg.in/square/go-jose.v2 v2.6.0 gopkg.in/square/go-jose.v2 v2.6.0
gopkg.in/yaml.v2 v2.3.0 // indirect gopkg.in/yaml.v2 v2.3.0 // indirect
xorm.io/core v0.7.2 xorm.io/core v0.7.2

6
go.sum
View File

@ -61,6 +61,8 @@ github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go
github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ= github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b h1:EgJ6N2S0h1WfFIjU5/VVHWbMSVYXAluop97Qxpr/lfQ=
github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU= github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b/go.mod h1:3SAoF0F5EbcOuBD5WT9nYkbIJieBS84cUQXADbXeBsU=
github.com/Unknwon/goconfig v1.0.0 h1:9IAu/BYbSLQi8puFjUQApZTxIHqSwrj5d8vpP8vTq4A=
github.com/Unknwon/goconfig v1.0.0/go.mod h1:wngxua9XCNjvHjDiTiV26DaKDT+0c63QR6H5hjVUUxw=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@ -757,8 +759,8 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw= gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.62.0 h1:duBzk771uxoUuOlyRLkHsygud9+5lrlGjdFBb4mSKDU= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=

122
i18n/generate_backend.go Normal file
View File

@ -0,0 +1,122 @@
// Copyright 2022 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 i18n
import (
"log"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/Unknwon/goconfig"
"github.com/casdoor/casdoor/util"
)
var (
reI18nBackendObject *regexp.Regexp
re18nBackendController *regexp.Regexp
)
func init() {
reI18nBackendObject, _ = regexp.Compile("i18n.Translate\\((.*?)\"\\)")
re18nBackendController, _ = regexp.Compile("c.T\\((.*?)\"\\)")
}
func GetAllI18nStrings(fileContent string, path string) []string {
res := []string{}
if strings.Contains(path, "object") {
matches := reI18nBackendObject.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
match := strings.Split(match[1], ",")
res = append(res, match[1][2:])
}
} else {
matches := re18nBackendController.FindAllStringSubmatch(fileContent, -1)
if matches == nil {
return res
}
for _, match := range matches {
res = append(res, match[1][1:])
}
}
return res
}
func getAllGoFilePaths() []string {
path := "../"
res := []string{}
err := filepath.Walk(path,
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !strings.HasSuffix(info.Name(), ".go") {
return nil
}
res = append(res, path)
// fmt.Println(path, info.Name())
return nil
})
if err != nil {
panic(err)
}
return res
}
func getErrName(paths []string) map[string]bool {
ErrName := make(map[string]bool)
for i := 0; i < len(paths); i++ {
content := util.ReadStringFromPath(paths[i])
words := GetAllI18nStrings(content, paths[i])
for i := 0; i < len(words); i++ {
ErrName[words[i]] = true
}
}
return ErrName
}
func writeToAllLanguageFiles(errName map[string]bool) {
languages := "en,zh,es,fr,de,ja,ko,ru"
languageArr := strings.Split(languages, ",")
var c [10]*goconfig.ConfigFile
for i := 0; i < len(languageArr); i++ {
var err error
c[i], err = goconfig.LoadConfigFile("../i18n/languages/" + "locale_" + languageArr[i] + ".ini")
if err != nil {
log.Println(err.Error())
}
for j := range errName {
parts := strings.Split(j, ".")
_, err := c[i].GetValue(parts[0], parts[1])
if err != nil {
c[i].SetValue(parts[0], parts[1], parts[1])
}
}
c[i].SetPrettyFormat(true)
err = goconfig.SaveConfigFile(c[i], "../i18n/languages/"+"locale_"+languageArr[i]+".ini")
if err != nil {
log.Println(err)
}
}
}

View File

@ -14,7 +14,10 @@
package i18n package i18n
import "testing" import (
"fmt"
"testing"
)
func applyToOtherLanguage(dataEn *I18nData, lang string) { func applyToOtherLanguage(dataEn *I18nData, lang string) {
dataOther := readI18nFile(lang) dataOther := readI18nFile(lang)
@ -24,7 +27,7 @@ func applyToOtherLanguage(dataEn *I18nData, lang string) {
writeI18nFile(lang, dataEn) writeI18nFile(lang, dataEn)
} }
func TestGenerateI18nStrings(t *testing.T) { func TestGenerateI18nStringsForFrontend(t *testing.T) {
dataEn := parseToData() dataEn := parseToData()
writeI18nFile("en", dataEn) writeI18nFile("en", dataEn)
@ -35,3 +38,17 @@ func TestGenerateI18nStrings(t *testing.T) {
applyToOtherLanguage(dataEn, "ru") applyToOtherLanguage(dataEn, "ru")
applyToOtherLanguage(dataEn, "zh") applyToOtherLanguage(dataEn, "zh")
} }
func TestGenerateI18nStringsForBackend(t *testing.T) {
paths := getAllGoFilePaths()
errName := getErrName(paths)
writeToAllLanguageFiles(errName)
fmt.Println("Total Err Words:", len(errName))
for i := range errName {
fmt.Println(i)
}
}

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
WrongPasswordManyTimes = You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again
Unauthorized = Unauthorized operation
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = Application %s not found
AppNotFoundForUserID = No application is found for userId: %s
GrantTypeNotSupport = Grant_type: %s is not supported in this application
HasNoProviders = This application has no providers
HasNoProvidersOfType = This application has no providers of type
InvalidID = Invalid application id
[AuthErr]
AuthStateWrong = State expected: %s, but got: %s
ChallengeMethodErr = Challenge method should be S256
CanNotUnlinkUsers = You are not the global admin, you can't unlink other users
CanNotLinkMySelf = You can't unlink yourself, you are not a member of any application
CallWebAuthnSigninBegin = Please call WebAuthnSigninBegin first
NotHuman = Turing test failed.
Unauthorized = Unauthorized operation
WrongPasswordManyTimes = WrongPasswordManyTimes
[CasErr]
ServiceDoNotMatch = Service %s and %s do not match
[EmailErr]
ExistedErr = Email already exists
EmptyErr = Email cannot be empty
EmailInvalid = Email is invalid
EmailCheckResult = Email: %s
EmptyParam = Empty parameters for emailForm: %v
InvalidReceivers = Invalid Email receivers: %s
UnableGetModifyRule = Unable to get the email modify rule.
[EnforcerErr]
SignInFirst = Please sign in first
[InitErr]
InitScoreFailed = Get init score failed, error: %%w
[LdapErr]
MultipleAccounts = Multiple accounts with same uid, please check your ldap server
PasswordWrong = Ldap user name or password incorrect
ServerExisted = Ldap server exist
[LoginErr]
AppDoNotExist = The application: %s does not exist
AppNotEnableSignUp = 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
AccountDoNotExist = The account does not exist
InvalidUserInformation = Failed to create user, user information is invalid: %s
LoginFirst = Please login first
LoginFail = Failed to login in: %s
NoPermission = You don't have the permission to do this
OldUser = The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)
ProviderCanNotSignUp = 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
SessionOutdated = Session outdated, please login again
SignOutFirst = Please sign out first before signing in
UserDoNotExist = The user: %s/%s doesn't exist
UserIsForbidden = The user is forbidden to sign in, please contact the administrator
UnknownAuthentication = Unknown authentication type (not password or provider), form = %s
UnsupportedPasswordType = unsupported password type: %s
[OrgErr]
DoNotExist = Organization does not exist
Immutable = The %s is immutable.
OnlyAdmin = Only admin can modify the %s.
UnknownModifyRule = Unknown modify rule %s.
[ParameterErr]
OrgMissingErr = Parameter organization is missing
Missing = Missing parameter
UnknownType = Unknown type
Wrong = Wrong parameter
[PhoneErr]
CodeNotSent = Code has not been sent yet!
CodeTimeOut = You should verify your code in %d min!
ExistedErr = Phone already exists
EmptyErr = Phone cannot be empty
InvalidReceivers = Invalid phone receivers: %s
NumberInvalid = Phone number is invalid
NoPrefix = %s No phone prefix
PhoneCheckResult = Phone: %s
UnableGetModifyRule = Unable to get the phone modify rule.
[ProviderErr]
CanNotBeUnlinked = This provider can't be unlinked
CategoryNotSAML = provider %s's category is not SAML
DoNotExist = the provider: %s does not exist
InvalidProvider = Invalid captcha provider.
LinkFirstErr = Please link first
ProviderNotEnabled = The provider: %s is not enabled for the application
ProviderNotSupported = The provider type: %s is not supported
ProviderNotFound = The provider: %s is not found
ProviderNotFoundForCategory = No provider for category: %s is found for application: %s
[ResourceErr]
NotAuthorized = You are not authorized to access this resource
UserIsNil = User is nil for tag: /"avatar/"
UsernameOrFilePathEmpty = Username or fullFilePath is empty: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = New password cannot contain blank space.
LessThanSixCharacters = New password must have at least 6 characters
[SignUpErr]
DoNotAllowSignUp = The application does not allow to sign up new account
SignOutFirst = Please sign out first before signing up
[StorageErr]
ObjectKeyNotAllowed = The objectKey: %s is not allowed
[TokenErr]
EmptyClientID = Empty clientId or clientSecret
InvalidToken = Invalid token
InvalidAppOrWrongClientSecret = Invalid application or wrong clientSecret
InvalidClientId = Invalid client_id
RedirectURIDoNotExist = Redirect URI: %s doesn't exist in the allowed Redirect URI list
[UserErr]
AffiliationBlankErr = Affiliation cannot be blank
DisplayNameBlankErr = DisplayName cannot be blank
DisplayNameInvalid = DisplayName is not valid real name
DisplayNameCanNotBeEmpty = Display name cannot be empty
DoNotExist = The user: %s doesn't exist
DoNotExistInOrg = The user: %s/%s doesn't exist
DoNotExistSignUp = the user does not exist, please sign up first
FirstNameBlankErr = FirstName cannot be blank
FailToImportUsers = Failed to import users
LastNameBlankErr = LastName cannot be blank
NameLessThanTwoCharacters = Username must have at least 2 characters
NameStartWithADigitErr = Username cannot start with a digit
NameIsEmailErr = Username cannot be an email address
NameCantainWhitSpaceErr = Username cannot contain white spaces
NameExistedErr = Username already exists
NameEmptyErr = Empty username.
NameTooLang = Username is too long (maximum is 39 characters).
NameFormatErr = 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.
PasswordLessThanSixCharacters = Password must have at least 6 characters
InvalidInformation = Invalid information

View File

@ -0,0 +1,137 @@
[ApplicationErr]
AppNotFound = 应用 %%s 未找到
AppNotFoundForUserID = 找不到该用户的应用程序 %s
GrantTypeNotSupport = 此应用中不支持此授权类型
HasNoProviders = 该应用无提供商
HasNoProvidersOfType = 应用没有该类型的提供商
InvalidID = 无效的Application ID
[AuthErr]
AuthStateWrong = 期望状态位: %s, 实际状态为: %s
ChallengeMethodErr = Challenge 方法应该为 S256
CanNotUnlinkUsers = 您不是全局管理员,无法取消链接其他用户
CanNotLinkMySelf = 您无法取消链接,您不是任何应用程序的成员
CallWebAuthnSigninBegin = 请先调用WebAuthnSigninBegin
NotHuman = 真人验证失败
Unauthorized = 未授权的操作
WrongPasswordManyTimes = 输入密码错误次数已达上限,请在 %d 分 %d 秒后重试
[CasErr]
ServiceDoNotMatch = 服务 %s 与 %s 不匹配
[EmailErr]
ExistedErr = 该邮箱已存在
EmptyErr = 邮箱不可为空
EmailInvalid = 无效邮箱
EmailCheckResult = Email: %s
EmptyParam = 邮件参数为空: %v
InvalidReceivers = 无效的邮箱接收者: %%s
UnableGetModifyRule = 无法得到Email修改规则
[EnforcerErr]
SignInFirst = 请先登录
[InitErr]
InitScoreFailed = 初始化分数失败: %w
[LdapErr]
MultipleAccounts = 多个帐户具有相同的uid请检查您的 ldap 服务器
PasswordWrong = Ldap密码错误
ServerExisted = Ldap服务器已存在
[LoginErr]
AppDoNotExist = 应用不存在: %s
AppNotEnableSignUp = 提供商账户: %s 与用户名: %s (%s) 不存在且 不允许注册新账户, 请联系IT支持
AccountDoNotExist = 账户不存在
InvalidUserInformation = 创建用户失败,用户信息无效: %%s
LoginFirst = 请先登录
LoginFail = 无法登录: %s
NoPermission = 您没有权限执行此操作
OldUser = 提供商账户: %s 与用户名: %s (%s) 已经与其他账户绑定: %s (%s)
ProviderCanNotSignUp = 提供商账户: %s 与用户名: %s (%s) 不存在且 不允许通过 %s 注册新账户, 请使用其他方式注册
SignOutFirst = 请在登录前登出
SessionOutdated = Session已过期请重新登陆
UserDoNotExist = 用户不存在: %s/%s
UserIsForbidden = 该用户被禁止登陆,请联系管理员
UnknownAuthentication = 未知的认证类型 (非密码或提供商认证), form = %s
UnsupportedPasswordType = 不支持此密码类型
[OrgErr]
DoNotExist = 组织不存在
Immutable = %s是不可变的
OnlyAdmin = 只有管理员用户有此权限
UnknownModifyRule = 未知的修改规则
[ParameterErr]
Missing = 参数丢失
OrgMissingErr = Organization参数丢失
UnknownType = 未知类型
Wrong = 参数错误
[PhoneErr]
CodeNotSent = 验证码还未发送
CodeTimeOut = 验证码过期
ExistedErr = 该电话已存在
EmptyErr = 电话不可为空
InvalidReceivers = 无效的电话接收者: %s
NumberInvalid = 无效电话
PhoneCheckResult = 电话: %s
UnableGetModifyRule = 无法得到电话修改规则
NoPrefix = %s 无此电话前缀
[ProviderErr]
CanNotBeUnlinked = 该提供商不可被链接
InvalidProvider = 无效的验证码提供商
LinkFirstErr = 请先绑定
ProviderNotEnabled = 提供商: %s 未被启用
ProviderNotSupported = 不支持该类型的提供商: %s
ProviderNotFound = 该提供商未找到: %s
ProviderNotFoundForCategory = 该类型的提供商: %s 在应用中未找到: %s
DoNotExist = 提供商: %s 不存在
CategoryNotSAML = 提供商 %s类型不是SAML
[ResourceErr]
NotAuthorized = 您无权获取此资源
UserIsNil = 用户头像标签为空
UsernameOrFilePathEmpty = username或FilePath为空: username = %s, fullFilePath = %s
[SetPasswordErr]
CanNotContainBlank = 新密码不可以包含空客
LessThanSixCharacters = 新密码至少为6位
[SignUpErr]
DoNotAllowSignUp = 该应用不允许注册新账户
SignOutFirst = 请在登陆前登出
[TokenErr]
EmptyClientID = clientId或clientSecret为空
InvalidAppOrWrongClientSecret = 无效应用或错误的clientSecret
InvalidToken = 无效token
InvalidClientId = 无效的ClientId
RedirectURIDoNotExist = 重定向 URI%s 在可列表中未找到
[UserErr]
AffiliationBlankErr = 联系方式不可为空
DisplayNameBlankErr = 展示名称不可为空
DisplayNameInvalid = 展示名称无效
DisplayNameCanNotBeEmpty = 展示名称不可为空
DoNotExist = 用户不存在: %s
DoNotExistInOrg = 用户不存在: %s/%s
FirstNameBlankErr = 名不可以为空
FailToImportUsers = 导入用户失败
LastNameBlankErr = 姓不可以为空
NameLessThanTwoCharacters = 用户名至少要有2个字符
NameStartWithADigitErr = 用户名禁止使用数字作为第一个字符
NameIsEmailErr = 用户名不可以是邮箱地址
NameCantainWhitSpaceErr = 用户名不可以包含空格
NameExistedErr = 用户名已存在
NameEmptyErr = 用户名不可为空
NameTooLang = 用户名过长最大长度为39个字符
NameFormatErr = 用户名只能包含字母数字字符、下划线或连字符,不能有连续的连字符或下划线,也不能以连字符或下划线开头或结尾
PasswordLessThanSixCharacters = 密码至少为6字符
DoNotExistSignUp = 用户不存在,请先注册
InvalidInformation = 无效信息
[StorageErr]
ObjectKeyNotAllowed = object key :%s 不被允许

View File

@ -15,10 +15,21 @@
package i18n package i18n
import ( import (
"embed"
"fmt" "fmt"
"log"
"strings" "strings"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"gopkg.in/ini.v1"
)
//go:embed languages/*.ini
var f embed.FS
var (
langMapConfig = make(map[string]*ini.File)
isNotFirstLoad = make(map[string]bool)
) )
func getI18nFilePath(language string) string { func getI18nFilePath(language string) string {
@ -62,3 +73,20 @@ func applyData(data1 *I18nData, data2 *I18nData) {
} }
} }
} }
func Translate(lang string, error string) string {
parts := strings.Split(error, ".")
if !strings.Contains(error, ".") || len(parts) != 2 {
log.Println("Invalid Error Name")
return ""
}
if isNotFirstLoad[lang] {
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
} else {
file, _ := f.ReadFile("languages/locale_" + lang + ".ini")
langMapConfig[lang], _ = ini.Load(file)
isNotFirstLoad[lang] = true
return langMapConfig[lang].Section(parts[0]).Key(parts[1]).String()
}
}

View File

@ -15,9 +15,12 @@
package idp package idp
import ( import (
"bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log"
"net/http" "net/http"
"strings" "strings"
"time" "time"
@ -167,7 +170,10 @@ func (idp *DingTalkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, erro
Email: dtUserInfo.Email, Email: dtUserInfo.Email,
AvatarUrl: dtUserInfo.AvatarUrl, AvatarUrl: dtUserInfo.AvatarUrl,
} }
isUserInOrg, err := idp.isUserInOrg(userInfo.UnionId)
if !isUserInOrg {
return nil, err
}
return &userInfo, nil return &userInfo, nil
} }
@ -194,3 +200,62 @@ func (idp *DingTalkIdProvider) postWithBody(body interface{}, url string) ([]byt
return data, nil return data, nil
} }
func (idp *DingTalkIdProvider) getInnerAppAccessToken() string {
appKey := idp.Config.ClientID
appSecret := idp.Config.ClientSecret
body := make(map[string]string)
body["appKey"] = appKey
body["appSecret"] = appSecret
bodyData, err := json.Marshal(body)
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
request, err := http.NewRequest("POST", "https://api.dingtalk.com/v1.0/oauth2/accessToken", reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
}
var data struct {
ExpireIn int `json:"expireIn"`
AccessToken string `json:"accessToken"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
log.Println(err.Error())
}
return data.AccessToken
}
func (idp *DingTalkIdProvider) isUserInOrg(unionId string) (bool, error) {
body := make(map[string]string)
body["unionid"] = unionId
bodyData, err := json.Marshal(body)
if err != nil {
log.Println(err.Error())
}
reader := bytes.NewReader(bodyData)
accessToken := idp.getInnerAppAccessToken()
request, _ := http.NewRequest("POST", "https://oapi.dingtalk.com/topapi/user/getbyunionid?access_token="+accessToken, reader)
request.Header.Set("Content-Type", "application/json;charset=UTF-8")
resp, err := idp.Client.Do(request)
respBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println(err.Error())
}
var data struct {
ErrCode int `json:"errcode"`
ErrMessage string `json:"errmsg"`
}
err = json.Unmarshal(respBytes, &data)
if err != nil {
log.Println(err.Error())
}
if data.ErrCode == 60121 {
return false, fmt.Errorf("the user is not found in the organization where clientId and clientSecret belong")
}
return true, nil
}

View File

@ -70,6 +70,7 @@ type Application struct {
SigninHtml string `xorm:"mediumtext" json:"signinHtml"` SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
FormCss string `xorm:"text" json:"formCss"` FormCss string `xorm:"text" json:"formCss"`
FormOffset int `json:"formOffset"` FormOffset int `json:"formOffset"`
FormSideHtml string `xorm:"mediumtext" json:"formSideHtml"`
FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"` FormBackgroundUrl string `xorm:"varchar(200)" json:"formBackgroundUrl"`
} }
@ -269,6 +270,13 @@ func UpdateApplication(id string, application *Application) bool {
application.Name = name application.Name = name
} }
if name != application.Name {
err := applicationChangeTrigger(name, application.Name)
if err != nil {
return false
}
}
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
providerItem.Provider = nil providerItem.Provider = nil
} }
@ -399,3 +407,55 @@ func ExtendManagedAccountsWithUser(user *User) *User {
return user return user
} }
func applicationChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
organization := new(Organization)
organization.DefaultApplication = newName
_, err = session.Where("default_application=?", oldName).Update(organization)
if err != nil {
return err
}
user := new(User)
user.SignupApplication = newName
_, err = session.Where("signup_application=?", oldName).Update(user)
if err != nil {
return err
}
resource := new(Resource)
resource.Application = newName
_, err = session.Where("application=?", oldName).Update(resource)
if err != nil {
return err
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for i := 0; i < len(permissions); i++ {
permissionResoureces := permissions[i].Resources
for j := 0; j < len(permissionResoureces); j++ {
if permissionResoureces[j] == oldName {
permissionResoureces[j] = newName
}
}
permissions[i].Resources = permissionResoureces
_, err = session.Where("name=?", permissions[i].Name).Update(permissions[i])
if err != nil {
return err
}
}
return session.Commit()
}

View File

@ -22,7 +22,6 @@ import (
"github.com/casbin/casbin/v2/model" "github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v3" xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
) )

View File

@ -114,6 +114,12 @@ func UpdateCert(id string, cert *Cert) bool {
return false return false
} }
if name != cert.Name {
err := certChangeTrigger(name, cert.Name)
if err != nil {
return false
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(cert) affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
if err != nil { if err != nil {
panic(err) panic(err)
@ -161,3 +167,22 @@ func getCertByApplication(application *Application) *Cert {
func GetDefaultCert() *Cert { func GetDefaultCert() *Cert {
return getCert("admin", "cert-built-in") return getCert("admin", "cert-built-in")
} }
func certChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
application := new(Application)
application.Cert = newName
_, err = session.Where("cert=?", oldName).Update(application)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -22,6 +22,7 @@ import (
"unicode" "unicode"
"github.com/casdoor/casdoor/cred" "github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
goldap "github.com/go-ldap/ldap/v3" goldap "github.com/go-ldap/ldap/v3"
) )
@ -41,89 +42,89 @@ func init() {
reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`) reFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
} }
func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string) string { func CheckUserSignup(application *Application, organization *Organization, username string, password string, displayName string, firstName string, lastName string, email string, phone string, affiliation string, lang string) string {
if organization == nil { if organization == nil {
return "organization does not exist" return i18n.Translate(lang, "OrgErr.DoNotExist")
} }
if application.IsSignupItemVisible("Username") { if application.IsSignupItemVisible("Username") {
if len(username) <= 1 { if len(username) <= 1 {
return "username must have at least 2 characters" return i18n.Translate(lang, "UserErr.NameLessThanTwoCharacters")
} }
if unicode.IsDigit(rune(username[0])) { if unicode.IsDigit(rune(username[0])) {
return "username cannot start with a digit" return i18n.Translate(lang, "UserErr.NameStartWithADigitErr")
} }
if util.IsEmailValid(username) { if util.IsEmailValid(username) {
return "username cannot be an email address" return i18n.Translate(lang, "UserErr.NameIsEmailErr")
} }
if reWhiteSpace.MatchString(username) { if reWhiteSpace.MatchString(username) {
return "username cannot contain white spaces" return i18n.Translate(lang, "UserErr.NameCantainWhitSpaceErr")
} }
msg := CheckUsername(username) msg := CheckUsername(username, lang)
if msg != "" { if msg != "" {
return msg return msg
} }
if HasUserByField(organization.Name, "name", username) { if HasUserByField(organization.Name, "name", username) {
return "username already exists" return i18n.Translate(lang, "UserErr.NameExistedErr")
} }
if HasUserByField(organization.Name, "email", email) { if HasUserByField(organization.Name, "email", email) {
return "email already exists" return i18n.Translate(lang, "EmailErr.ExistedErr")
} }
if HasUserByField(organization.Name, "phone", phone) { if HasUserByField(organization.Name, "phone", phone) {
return "phone already exists" return i18n.Translate(lang, "PhoneErr.ExistedErr")
} }
} }
if len(password) <= 5 { if len(password) <= 5 {
return "password must have at least 6 characters" return i18n.Translate(lang, "UserErr.PasswordLessThanSixCharacters")
} }
if application.IsSignupItemVisible("Email") { if application.IsSignupItemVisible("Email") {
if email == "" { if email == "" {
if application.IsSignupItemRequired("Email") { if application.IsSignupItemRequired("Email") {
return "email cannot be empty" return i18n.Translate(lang, "EmailErr.EmptyErr")
} else { } else {
return "" return ""
} }
} }
if HasUserByField(organization.Name, "email", email) { if HasUserByField(organization.Name, "email", email) {
return "email already exists" return i18n.Translate(lang, "EmailErr.ExistedErr")
} else if !util.IsEmailValid(email) { } else if !util.IsEmailValid(email) {
return "email is invalid" return i18n.Translate(lang, "EmailErr.EmailInvalid")
} }
} }
if application.IsSignupItemVisible("Phone") { if application.IsSignupItemVisible("Phone") {
if phone == "" { if phone == "" {
if application.IsSignupItemRequired("Phone") { if application.IsSignupItemRequired("Phone") {
return "phone cannot be empty" return i18n.Translate(lang, "PhoneErr.EmptyErr")
} else { } else {
return "" return ""
} }
} }
if HasUserByField(organization.Name, "phone", phone) { if HasUserByField(organization.Name, "phone", phone) {
return "phone already exists" return i18n.Translate(lang, "PhoneErr.ExistedErr")
} else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) { } else if organization.PhonePrefix == "86" && !util.IsPhoneCnValid(phone) {
return "phone number is invalid" return i18n.Translate(lang, "PhoneErr.NumberInvalid")
} }
} }
if application.IsSignupItemVisible("Display name") { if application.IsSignupItemVisible("Display name") {
if application.GetSignupItemRule("Display name") == "First, last" && (firstName != "" || lastName != "") { if application.GetSignupItemRule("Display name") == "First, last" && (firstName != "" || lastName != "") {
if firstName == "" { if firstName == "" {
return "firstName cannot be blank" return i18n.Translate(lang, "UserErr.FirstNameBlankErr")
} else if lastName == "" { } else if lastName == "" {
return "lastName cannot be blank" return i18n.Translate(lang, "UserErr.LastNameBlankErr")
} }
} else { } else {
if displayName == "" { if displayName == "" {
return "displayName cannot be blank" return i18n.Translate(lang, "UserErr.DisplayNameBlankErr")
} else if application.GetSignupItemRule("Display name") == "Real name" { } else if application.GetSignupItemRule("Display name") == "Real name" {
if !isValidRealName(displayName) { if !isValidRealName(displayName) {
return "displayName is not valid real name" return i18n.Translate(lang, "UserErr.DisplayNameInvalid")
} }
} }
} }
@ -131,14 +132,14 @@ func CheckUserSignup(application *Application, organization *Organization, usern
if application.IsSignupItemVisible("Affiliation") { if application.IsSignupItemVisible("Affiliation") {
if affiliation == "" { if affiliation == "" {
return "affiliation cannot be blank" return i18n.Translate(lang, "UserErr.AffiliationBlankErr")
} }
} }
return "" return ""
} }
func checkSigninErrorTimes(user *User) string { func checkSigninErrorTimes(user *User, lang string) string {
if user.SigninWrongTimes >= SigninWrongTimesLimit { if user.SigninWrongTimes >= SigninWrongTimesLimit {
lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime) lastSignWrongTime, _ := time.Parse(time.RFC3339, user.LastSigninWrongTime)
passedTime := time.Now().UTC().Sub(lastSignWrongTime) passedTime := time.Now().UTC().Sub(lastSignWrongTime)
@ -146,7 +147,7 @@ func checkSigninErrorTimes(user *User) string {
// deny the login if the error times is greater than the limit and the last login time is less than the duration // deny the login if the error times is greater than the limit and the last login time is less than the duration
if seconds > 0 { if seconds > 0 {
return fmt.Sprintf("You have entered the wrong password too many times, please wait for %d minutes %d seconds and try again", seconds/60, seconds%60) return fmt.Sprintf(i18n.Translate(lang, "AuthErr.WrongPasswordManyTimes"), seconds/60, seconds%60)
} }
// reset the error times // reset the error times
@ -158,15 +159,15 @@ func checkSigninErrorTimes(user *User) string {
return "" return ""
} }
func CheckPassword(user *User, password string) string { func CheckPassword(user *User, password string, lang string) string {
// check the login error times // check the login error times
if msg := checkSigninErrorTimes(user); msg != "" { if msg := checkSigninErrorTimes(user, lang); msg != "" {
return msg return msg
} }
organization := GetOrganizationByUser(user) organization := GetOrganizationByUser(user)
if organization == nil { if organization == nil {
return "organization does not exist" return i18n.Translate(lang, "OrgErr.DoNotExist")
} }
credManager := cred.GetCredManager(organization.PasswordType) credManager := cred.GetCredManager(organization.PasswordType)
@ -185,11 +186,11 @@ func CheckPassword(user *User, password string) string {
return recordSigninErrorInfo(user) return recordSigninErrorInfo(user)
} else { } else {
return fmt.Sprintf("unsupported password type: %s", organization.PasswordType) return fmt.Sprintf(i18n.Translate(lang, "LoginErr.UnsupportedPasswordType"), organization.PasswordType)
} }
} }
func checkLdapUserPassword(user *User, password string) (*User, string) { func checkLdapUserPassword(user *User, password string, lang string) (*User, string) {
ldaps := GetLdaps(user.Owner) ldaps := GetLdaps(user.Owner)
ldapLoginSuccess := false ldapLoginSuccess := false
for _, ldapServer := range ldaps { for _, ldapServer := range ldaps {
@ -209,7 +210,7 @@ func checkLdapUserPassword(user *User, password string) (*User, string) {
if len(searchResult.Entries) == 0 { if len(searchResult.Entries) == 0 {
continue continue
} else if len(searchResult.Entries) > 1 { } else if len(searchResult.Entries) > 1 {
return nil, "Error: multiple accounts with same uid, please check your ldap server" return nil, i18n.Translate(lang, "LdapErr.MultipleAccounts")
} }
dn := searchResult.Entries[0].DN dn := searchResult.Entries[0].DN
@ -220,26 +221,26 @@ func checkLdapUserPassword(user *User, password string) (*User, string) {
} }
if !ldapLoginSuccess { if !ldapLoginSuccess {
return nil, "ldap user name or password incorrect" return nil, i18n.Translate(lang, "LdapErr.PasswordWrong")
} }
return user, "" return user, ""
} }
func CheckUserPassword(organization string, username string, password string) (*User, string) { func CheckUserPassword(organization string, username string, password string, lang string) (*User, string) {
user := GetUserByFields(organization, username) user := GetUserByFields(organization, username)
if user == nil || user.IsDeleted == true { if user == nil || user.IsDeleted == true {
return nil, "the user does not exist, please sign up first" return nil, i18n.Translate(lang, "UserErr.DoNotExistSignUp")
} }
if user.IsForbidden { if user.IsForbidden {
return nil, "the user is forbidden to sign in, please contact the administrator" return nil, i18n.Translate(lang, "LoginErr.UserIsForbidden")
} }
if user.Ldap != "" { if user.Ldap != "" {
// ONLY for ldap users // ONLY for ldap users
return checkLdapUserPassword(user, password) return checkLdapUserPassword(user, password, lang)
} else { } else {
msg := CheckPassword(user, password) msg := CheckPassword(user, password, lang)
if msg != "" { if msg != "" {
return nil, msg return nil, msg
} }
@ -251,15 +252,15 @@ func filterField(field string) bool {
return reFieldWhiteList.MatchString(field) return reFieldWhiteList.MatchString(field)
} }
func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (bool, error) { func CheckUserPermission(requestUserId, userId, userOwner string, strict bool, lang string) (bool, error) {
if requestUserId == "" { if requestUserId == "" {
return false, fmt.Errorf("please login first") return false, fmt.Errorf(i18n.Translate(lang, "LoginErr.LoginFirst"))
} }
if userId != "" { if userId != "" {
targetUser := GetUser(userId) targetUser := GetUser(userId)
if targetUser == nil { if targetUser == nil {
return false, fmt.Errorf("the user: %s doesn't exist", userId) return false, fmt.Errorf(i18n.Translate(lang, "UserErr.DoNotExist"), userId)
} }
userOwner = targetUser.Owner userOwner = targetUser.Owner
@ -271,7 +272,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (
} else { } else {
requestUser := GetUser(requestUserId) requestUser := GetUser(requestUserId)
if requestUser == nil { if requestUser == nil {
return false, fmt.Errorf("session outdated, please login again") return false, fmt.Errorf(i18n.Translate(lang, "LoginErr.SessionOutdated"))
} }
if requestUser.IsGlobalAdmin { if requestUser.IsGlobalAdmin {
hasPermission = true hasPermission = true
@ -286,7 +287,7 @@ func CheckUserPermission(requestUserId, userId, userOwner string, strict bool) (
} }
} }
return hasPermission, fmt.Errorf("you don't have the permission to do this") return hasPermission, fmt.Errorf(i18n.Translate(lang, "LoginErr.NoPermission"))
} }
func CheckAccessPermission(userId string, application *Application) (bool, error) { func CheckAccessPermission(userId string, application *Application) (bool, error) {
@ -319,11 +320,11 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
return allowed, err return allowed, err
} }
func CheckUsername(username string) string { func CheckUsername(username string, lang string) string {
if username == "" { if username == "" {
return "Empty username." return i18n.Translate(lang, "UserErr.NameEmptyErr")
} else if len(username) > 39 { } else if len(username) > 39 {
return "Username is too long (maximum is 39 characters)." return i18n.Translate(lang, "UserErr.NameTooLang")
} }
exclude, _ := regexp.Compile("^[\u0021-\u007E]+$") exclude, _ := regexp.Compile("^[\u0021-\u007E]+$")
@ -334,8 +335,25 @@ func CheckUsername(username string) string {
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex // https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
re, _ := regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$") re, _ := regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$")
if !re.MatchString(username) { if !re.MatchString(username) {
return "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." return i18n.Translate(lang, "UserErr.NameFormatErr")
} }
return "" return ""
} }
func CheckToEnableCaptcha(application *Application) bool {
if len(application.Providers) == 0 {
return false
}
for _, providerItem := range application.Providers {
if providerItem.Provider == nil {
continue
}
if providerItem.Provider.Category == "Captcha" && providerItem.Provider.Type == "Default" {
return providerItem.Rule == "Always"
}
}
return false
}

View File

@ -143,7 +143,7 @@ func initBuiltInApplication() {
EnablePassword: true, EnablePassword: true,
EnableSignUp: true, EnableSignUp: true,
Providers: []*ProviderItem{ Providers: []*ProviderItem{
{Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, AlertType: "None", Provider: nil}, {Name: "provider_captcha_default", CanSignUp: false, CanSignIn: false, CanUnlink: false, Prompted: false, AlertType: "None", Rule: "None", Provider: nil},
}, },
SignupItems: []*SignupItem{ SignupItems: []*SignupItem{
{Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"}, {Name: "ID", Visible: false, Required: true, Prompted: false, Rule: "Random"},

View File

@ -56,12 +56,40 @@ func readInitDataFromFile(filePath string) *InitData {
s := util.ReadStringFromPath(filePath) s := util.ReadStringFromPath(filePath)
data := &InitData{} data := &InitData{
Organizations: []*Organization{},
Applications: []*Application{},
Users: []*User{},
Certs: []*Cert{},
Providers: []*Provider{},
Ldaps: []*Ldap{},
}
err := util.JsonToStruct(s, data) err := util.JsonToStruct(s, data)
if err != nil { if err != nil {
panic(err) panic(err)
} }
// transform nil slice to empty slice
for _, organization := range data.Organizations {
if organization.Tags == nil {
organization.Tags = []string{}
}
}
for _, application := range data.Applications {
if application.Providers == nil {
application.Providers = []*ProviderItem{}
}
if application.SignupItems == nil {
application.SignupItems = []*SignupItem{}
}
if application.GrantTypes == nil {
application.GrantTypes = []string{}
}
if application.RedirectUris == nil {
application.RedirectUris = []string{}
}
}
return data return data
} }

View File

@ -62,7 +62,7 @@ func GetFilteredUsers(m *ldapserver.Message, name, org string) ([]*User, int) {
return nil, ldapserver.LDAPResultInsufficientAccessRights return nil, ldapserver.LDAPResultInsufficientAccessRights
} }
} else { } else {
hasPermission, err := CheckUserPermission(fmt.Sprintf("%s/%s", m.Client.OrgName, m.Client.UserName), fmt.Sprintf("%s/%s", org, name), org, true) hasPermission, err := CheckUserPermission(fmt.Sprintf("%s/%s", m.Client.OrgName, m.Client.UserName), fmt.Sprintf("%s/%s", org, name), org, true, "en")
if !hasPermission { if !hasPermission {
log.Printf("ErrMsg = %v", err.Error()) log.Printf("ErrMsg = %v", err.Error())
return nil, ldapserver.LDAPResultInsufficientAccessRights return nil, ldapserver.LDAPResultInsufficientAccessRights

View File

@ -92,6 +92,12 @@ func UpdateModel(id string, modelObj *Model) bool {
return false return false
} }
if name != modelObj.Name {
err := modelChangeTrigger(name, modelObj.Name)
if err != nil {
return false
}
}
// check model grammar // check model grammar
_, err := model.NewModelFromString(modelObj.ModelText) _, err := model.NewModelFromString(modelObj.ModelText)
if err != nil { if err != nil {
@ -127,3 +133,22 @@ func DeleteModel(model *Model) bool {
func (model *Model) GetId() string { func (model *Model) GetId() string {
return fmt.Sprintf("%s/%s", model.Owner, model.Name) return fmt.Sprintf("%s/%s", model.Owner, model.Name)
} }
func modelChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
permission := new(Permission)
permission.Model = newName
_, err = session.Where("model=?", oldName).Update(permission)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -16,8 +16,10 @@ package object
import ( import (
"fmt" "fmt"
"strings"
"github.com/casdoor/casdoor/cred" "github.com/casdoor/casdoor/cred"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
) )
@ -133,15 +135,10 @@ func UpdateOrganization(id string, organization *Organization) bool {
} }
if name != organization.Name { if name != organization.Name {
go func() { err := organizationChangeTrigger(name, organization.Name)
application := new(Application) if err != nil {
application.Organization = organization.Name return false
_, _ = adapter.Engine.Where("organization=?", name).Update(application) }
user := new(User)
user.Owner = organization.Name
_, _ = adapter.Engine.Where("owner=?", name).Update(user)
}()
} }
if organization.MasterPassword != "" && organization.MasterPassword != "***" { if organization.MasterPassword != "" && organization.MasterPassword != "***" {
@ -202,18 +199,18 @@ func GetAccountItemByName(name string, organization *Organization) *AccountItem
return nil return nil
} }
func CheckAccountItemModifyRule(accountItem *AccountItem, user *User) (bool, string) { func CheckAccountItemModifyRule(accountItem *AccountItem, user *User, lang string) (bool, string) {
switch accountItem.ModifyRule { switch accountItem.ModifyRule {
case "Admin": case "Admin":
if !(user.IsAdmin || user.IsGlobalAdmin) { if !(user.IsAdmin || user.IsGlobalAdmin) {
return false, fmt.Sprintf("Only admin can modify the %s.", accountItem.Name) return false, fmt.Sprintf(i18n.Translate(lang, "OrgErr.OnlyAdmin"), accountItem.Name)
} }
case "Immutable": case "Immutable":
return false, fmt.Sprintf("The %s is immutable.", accountItem.Name) return false, fmt.Sprintf(i18n.Translate(lang, "OrgErr.Immutable"), accountItem.Name)
case "Self": case "Self":
break break
default: default:
return false, fmt.Sprintf("Unknown modify rule %s.", accountItem.ModifyRule) return false, fmt.Sprintf(i18n.Translate(lang, "OrgErr.UnknownModifyRule"), accountItem.ModifyRule)
} }
return true, "" return true, ""
} }
@ -251,3 +248,148 @@ func GetDefaultApplication(id string) (*Application, error) {
return defaultApplication, nil return defaultApplication, nil
} }
func organizationChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
application := new(Application)
application.Organization = newName
_, err = session.Where("organization=?", oldName).Update(application)
if err != nil {
return err
}
user := new(User)
user.Owner = newName
_, err = session.Where("owner=?", oldName).Update(user)
if err != nil {
return err
}
role := new(Role)
_, err = adapter.Engine.Where("owner=?", oldName).Get(role)
if err != nil {
return err
}
for i, u := range role.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
role.Users[i] = split[0] + "/" + split[1]
}
}
for i, u := range role.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
role.Roles[i] = split[0] + "/" + split[1]
}
}
role.Owner = newName
_, err = session.Where("owner=?", oldName).Update(role)
if err != nil {
return err
}
permission := new(Permission)
_, err = adapter.Engine.Where("owner=?", oldName).Get(permission)
if err != nil {
return err
}
for i, u := range permission.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
permission.Users[i] = split[0] + "/" + split[1]
}
}
for i, u := range permission.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[0] == oldName {
split[0] = newName
permission.Roles[i] = split[0] + "/" + split[1]
}
}
permission.Owner = newName
_, err = session.Where("owner=?", oldName).Update(permission)
if err != nil {
return err
}
casbinAdapter := new(CasbinAdapter)
casbinAdapter.Owner = newName
casbinAdapter.Organization = newName
_, err = session.Where("owner=?", oldName).Update(casbinAdapter)
if err != nil {
return err
}
ldap := new(Ldap)
ldap.Owner = newName
_, err = session.Where("owner=?", oldName).Update(ldap)
if err != nil {
return err
}
model := new(Model)
model.Owner = newName
_, err = session.Where("owner=?", oldName).Update(model)
if err != nil {
return err
}
payment := new(Payment)
payment.Organization = newName
_, err = session.Where("organization=?", oldName).Update(payment)
if err != nil {
return err
}
record := new(Record)
record.Owner = newName
record.Organization = newName
_, err = session.Where("organization=?", oldName).Update(record)
if err != nil {
return err
}
resource := new(Resource)
resource.Owner = newName
_, err = session.Where("owner=?", oldName).Update(resource)
if err != nil {
return err
}
syncer := new(Syncer)
syncer.Organization = newName
_, err = session.Where("organization=?", oldName).Update(syncer)
if err != nil {
return err
}
token := new(Token)
token.Organization = newName
_, err = session.Where("organization=?", oldName).Update(token)
if err != nil {
return err
}
webhook := new(Webhook)
webhook.Organization = newName
_, err = session.Where("organization=?", oldName).Update(webhook)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -17,6 +17,7 @@ package object
import ( import (
"fmt" "fmt"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/pp" "github.com/casdoor/casdoor/pp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
@ -174,6 +175,13 @@ func UpdateProvider(id string, provider *Provider) bool {
return false return false
} }
if name != provider.Name {
err := providerChangeTrigger(name, provider.Name)
if err != nil {
return false
}
}
session := adapter.Engine.ID(core.PK{owner, name}).AllCols() session := adapter.Engine.ID(core.PK{owner, name}).AllCols()
if provider.ClientSecret == "***" { if provider.ClientSecret == "***" {
session = session.Omit("client_secret") session = session.Omit("client_secret")
@ -228,7 +236,7 @@ func (p *Provider) GetId() string {
return fmt.Sprintf("%s/%s", p.Owner, p.Name) return fmt.Sprintf("%s/%s", p.Owner, p.Name)
} }
func GetCaptchaProviderByOwnerName(applicationId string) (*Provider, error) { func GetCaptchaProviderByOwnerName(applicationId, lang string) (*Provider, error) {
owner, name := util.GetOwnerAndNameFromId(applicationId) owner, name := util.GetOwnerAndNameFromId(applicationId)
provider := Provider{Owner: owner, Name: name, Category: "Captcha"} provider := Provider{Owner: owner, Name: name, Category: "Captcha"}
existed, err := adapter.Engine.Get(&provider) existed, err := adapter.Engine.Get(&provider)
@ -237,27 +245,65 @@ func GetCaptchaProviderByOwnerName(applicationId string) (*Provider, error) {
} }
if !existed { if !existed {
return nil, fmt.Errorf("the provider: %s does not exist", applicationId) return nil, fmt.Errorf(i18n.Translate(lang, "ProviderErr.DoNotExist"), applicationId)
} }
return &provider, nil return &provider, nil
} }
func GetCaptchaProviderByApplication(applicationId, isCurrentProvider string) (*Provider, error) { func GetCaptchaProviderByApplication(applicationId, isCurrentProvider, lang string) (*Provider, error) {
if isCurrentProvider == "true" { if isCurrentProvider == "true" {
return GetCaptchaProviderByOwnerName(applicationId) return GetCaptchaProviderByOwnerName(applicationId, lang)
} }
application := GetApplication(applicationId) application := GetApplication(applicationId)
if application == nil || len(application.Providers) == 0 { if application == nil || len(application.Providers) == 0 {
return nil, fmt.Errorf("invalid application id") return nil, fmt.Errorf(i18n.Translate(lang, "ApplicationErr.InvalidID"))
} }
for _, provider := range application.Providers { for _, provider := range application.Providers {
if provider.Provider == nil { if provider.Provider == nil {
continue continue
} }
if provider.Provider.Category == "Captcha" { if provider.Provider.Category == "Captcha" {
return GetCaptchaProviderByOwnerName(fmt.Sprintf("%s/%s", provider.Provider.Owner, provider.Provider.Name)) return GetCaptchaProviderByOwnerName(fmt.Sprintf("%s/%s", provider.Provider.Owner, provider.Provider.Name), lang)
} }
} }
return nil, nil return nil, nil
} }
func providerChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var applications []*Application
err = adapter.Engine.Find(&applications)
if err != nil {
return err
}
for i := 0; i < len(applications); i++ {
providers := applications[i].Providers
for j := 0; j < len(providers); j++ {
if providers[j].Name == oldName {
providers[j].Name = newName
}
}
applications[i].Providers = providers
_, err = session.Where("name=?", applications[i].Name).Update(applications[i])
if err != nil {
return err
}
}
resource := new(Resource)
resource.Provider = newName
_, err = session.Where("provider=?", oldName).Update(resource)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -21,6 +21,7 @@ type ProviderItem struct {
CanUnlink bool `json:"canUnlink"` CanUnlink bool `json:"canUnlink"`
Prompted bool `json:"prompted"` Prompted bool `json:"prompted"`
AlertType string `json:"alertType"` AlertType string `json:"alertType"`
Rule string `json:"rule"`
Provider *Provider `json:"provider"` Provider *Provider `json:"provider"`
} }

View File

@ -16,6 +16,7 @@ package object
import ( import (
"fmt" "fmt"
"strings"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
@ -94,6 +95,13 @@ func UpdateRole(id string, role *Role) bool {
return false return false
} }
if name != role.Name {
err := roleChangeTrigger(name, role.Name)
if err != nil {
return false
}
}
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(role) affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(role)
if err != nil { if err != nil {
panic(err) panic(err)
@ -133,3 +141,54 @@ func GetRolesByUser(userId string) []*Role {
return roles return roles
} }
func roleChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var roles []*Role
err = adapter.Engine.Find(&roles)
if err != nil {
return err
}
for _, role := range roles {
for j, u := range role.Roles {
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
role.Roles[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", role.Name).Update(role)
if err != nil {
return err
}
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for _, permission := range permissions {
for j, u := range permission.Roles {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
permission.Roles[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", permission.Name).Update(permission)
if err != nil {
return err
}
}
return session.Commit()
}

View File

@ -24,6 +24,7 @@ import (
"strings" "strings"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
saml2 "github.com/russellhaering/gosaml2" saml2 "github.com/russellhaering/gosaml2"
dsig "github.com/russellhaering/goxmldsig" dsig "github.com/russellhaering/goxmldsig"
) )
@ -41,10 +42,10 @@ func ParseSamlResponse(samlResponse string, providerType string) (string, error)
return assertionInfo.NameID, nil return assertionInfo.NameID, nil
} }
func GenerateSamlLoginUrl(id, relayState string) (string, string, error) { func GenerateSamlLoginUrl(id, relayState, lang string) (string, string, error) {
provider := GetProvider(id) provider := GetProvider(id)
if provider.Category != "SAML" { if provider.Category != "SAML" {
return "", "", fmt.Errorf("provider %s's category is not SAML", provider.Name) return "", "", fmt.Errorf(i18n.Translate(lang, "ProviderErr.CategoryNotSAML"), provider.Name)
} }
sp, err := buildSp(provider, "") sp, err := buildSp(provider, "")
if err != nil { if err != nil {

View File

@ -21,6 +21,7 @@ import (
"strings" "strings"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/storage" "github.com/casdoor/casdoor/storage"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
) )
@ -126,16 +127,16 @@ func UploadFileSafe(provider *Provider, fullFilePath string, fileBuffer *bytes.B
return fileUrl, objectKey, nil return fileUrl, objectKey, nil
} }
func DeleteFile(provider *Provider, objectKey string) error { func DeleteFile(provider *Provider, objectKey string, lang string) error {
// check fullFilePath is there security issue // check fullFilePath is there security issue
if strings.Contains(objectKey, "..") { if strings.Contains(objectKey, "..") {
return fmt.Errorf("the objectKey: %s is not allowed", objectKey) return fmt.Errorf(i18n.Translate(lang, "StorageErr.ObjectKeyNotAllowed"), objectKey)
} }
endpoint := getProviderEndpoint(provider) endpoint := getProviderEndpoint(provider)
storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint) storageProvider := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if storageProvider == nil { if storageProvider == nil {
return fmt.Errorf("the provider type: %s is not supported", provider.Type) return fmt.Errorf(i18n.Translate(lang, "ProviderErr.ProviderNotSupported"), provider.Type)
} }
if provider.Domain == "" { if provider.Domain == "" {

View File

@ -21,6 +21,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/idp" "github.com/casdoor/casdoor/idp"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
@ -238,14 +239,14 @@ func GetTokenByTokenAndApplication(token string, application string) *Token {
return &tokenResult return &tokenResult
} }
func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string) (string, *Application) { func CheckOAuthLogin(clientId string, responseType string, redirectUri string, scope string, state string, lang string) (string, *Application) {
if responseType != "code" && responseType != "token" && responseType != "id_token" { if responseType != "code" && responseType != "token" && responseType != "id_token" {
return fmt.Sprintf("error: grant_type: %s is not supported in this application", responseType), nil return fmt.Sprintf(i18n.Translate(lang, "ApplicationErr.GrantTypeNotSupport"), responseType), nil
} }
application := GetApplicationByClientId(clientId) application := GetApplicationByClientId(clientId)
if application == nil { if application == nil {
return "Invalid client_id", nil return i18n.Translate(lang, "TokenErr.InvalidClientId"), nil
} }
validUri := false validUri := false
@ -256,7 +257,7 @@ func CheckOAuthLogin(clientId string, responseType string, redirectUri string, s
} }
} }
if !validUri { if !validUri {
return fmt.Sprintf("Redirect URI: \"%s\" doesn't exist in the allowed Redirect URI list", redirectUri), application return fmt.Sprintf(i18n.Translate(lang, "TokenErr.RedirectURIDoNotExist"), redirectUri), application
} }
// Mask application for /api/get-app-login // Mask application for /api/get-app-login
@ -264,7 +265,7 @@ func CheckOAuthLogin(clientId string, responseType string, redirectUri string, s
return "", application return "", application
} }
func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string) *Code { func GetOAuthCode(userId string, clientId string, responseType string, redirectUri string, scope string, state string, nonce string, challenge string, host string, lang string) *Code {
user := GetUser(userId) user := GetUser(userId)
if user == nil { if user == nil {
return &Code{ return &Code{
@ -279,7 +280,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
} }
} }
msg, application := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state) msg, application := CheckOAuthLogin(clientId, responseType, redirectUri, scope, state, lang)
if msg != "" { if msg != "" {
return &Code{ return &Code{
Message: msg, Message: msg,
@ -322,7 +323,7 @@ func GetOAuthCode(userId string, clientId string, responseType string, redirectU
} }
} }
func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, tag string, avatar string) interface{} { func GetOAuthToken(grantType string, clientId string, clientSecret string, code string, verifier string, scope string, username string, password string, host string, tag string, avatar string, lang string) interface{} {
application := GetApplicationByClientId(clientId) application := GetApplicationByClientId(clientId)
if application == nil { if application == nil {
return &TokenError{ return &TokenError{
@ -353,7 +354,7 @@ func GetOAuthToken(grantType string, clientId string, clientSecret string, code
if tag == "wechat_miniprogram" { if tag == "wechat_miniprogram" {
// Wechat Mini Program // Wechat Mini Program
token, tokenError = GetWechatMiniProgramToken(application, code, host, username, avatar) token, tokenError = GetWechatMiniProgramToken(application, code, host, username, avatar, lang)
} }
if tokenError != nil { if tokenError != nil {
@ -559,7 +560,7 @@ func GetPasswordToken(application *Application, username string, password string
ErrorDescription: "the user does not exist", ErrorDescription: "the user does not exist",
} }
} }
msg := CheckPassword(user, password) msg := CheckPassword(user, password, "en")
if msg != "" { if msg != "" {
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidGrant, Error: InvalidGrant,
@ -669,7 +670,7 @@ func GetTokenByUser(application *Application, user *User, scope string, host str
// GetWechatMiniProgramToken // GetWechatMiniProgramToken
// Wechat Mini Program flow // Wechat Mini Program flow
func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string) (*Token, *TokenError) { func GetWechatMiniProgramToken(application *Application, code string, host string, username string, avatar string, lang string) (*Token, *TokenError) {
mpProvider := GetWechatMiniProgramProvider(application) mpProvider := GetWechatMiniProgramProvider(application)
if mpProvider == nil { if mpProvider == nil {
return nil, &TokenError{ return nil, &TokenError{
@ -703,7 +704,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
} }
// Add new user // Add new user
var name string var name string
if CheckUsername(username) == "" { if CheckUsername(username, lang) == "" {
name = username name = username
} else { } else {
name = fmt.Sprintf("wechat-%s", openId) name = fmt.Sprintf("wechat-%s", openId)

View File

@ -37,7 +37,7 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
// Encode private key to PKCS#1 ASN.1 PEM. // Encode private key to PKCS#1 ASN.1 PEM.
privateKeyPem := pem.EncodeToMemory( privateKeyPem := pem.EncodeToMemory(
&pem.Block{ &pem.Block{
Type: "PRIVATE KEY", Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(key), Bytes: x509.MarshalPKCS1PrivateKey(key),
}, },
) )

View File

@ -380,6 +380,13 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
return false return false
} }
if name != user.Name {
err := userChangeTrigger(name, user.Name)
if err != nil {
return false
}
}
if user.Password == "***" { if user.Password == "***" {
user.Password = oldUser.Password user.Password = oldUser.Password
} }
@ -416,6 +423,13 @@ func UpdateUserForAllFields(id string, user *User) bool {
return false return false
} }
if name != user.Name {
err := userChangeTrigger(name, user.Name)
if err != nil {
return false
}
}
user.UpdateUserHash() user.UpdateUserHash()
if user.Avatar != oldUser.Avatar && user.Avatar != "" { if user.Avatar != oldUser.Avatar && user.Avatar != "" {
@ -567,3 +581,62 @@ func ExtendUserWithRolesAndPermissions(user *User) {
user.Roles = GetRolesByUser(user.GetId()) user.Roles = GetRolesByUser(user.GetId())
user.Permissions = GetPermissionsByUser(user.GetId()) user.Permissions = GetPermissionsByUser(user.GetId())
} }
func userChangeTrigger(oldName string, newName string) error {
session := adapter.Engine.NewSession()
defer session.Close()
err := session.Begin()
if err != nil {
return err
}
var roles []*Role
err = adapter.Engine.Find(&roles)
if err != nil {
return err
}
for _, role := range roles {
for j, u := range role.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
role.Users[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", role.Name).Update(role)
if err != nil {
return err
}
}
var permissions []*Permission
err = adapter.Engine.Find(&permissions)
if err != nil {
return err
}
for _, permission := range permissions {
for j, u := range permission.Users {
// u = organization/username
split := strings.Split(u, "/")
if split[1] == oldName {
split[1] = newName
permission.Users[j] = split[0] + "/" + split[1]
}
}
_, err = session.Where("name=?", permission.Name).Update(permission)
if err != nil {
return err
}
}
resource := new(Resource)
resource.User = newName
_, err = session.Where("user=?", oldName).Update(resource)
if err != nil {
return err
}
return session.Commit()
}

View File

@ -21,6 +21,7 @@ import (
"time" "time"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"xorm.io/core" "xorm.io/core"
) )
@ -122,11 +123,11 @@ func getVerificationRecord(dest string) *VerificationRecord {
return &record return &record
} }
func CheckVerificationCode(dest, code string) string { func CheckVerificationCode(dest, code, lang string) string {
record := getVerificationRecord(dest) record := getVerificationRecord(dest)
if record == nil { if record == nil {
return "Code has not been sent yet!" return i18n.Translate(lang, "PhoneErr.CodeNotSent")
} }
timeout, err := conf.GetConfigInt64("verificationCodeTimeout") timeout, err := conf.GetConfigInt64("verificationCodeTimeout")
@ -136,7 +137,7 @@ func CheckVerificationCode(dest, code string) string {
now := time.Now().Unix() now := time.Now().Unix()
if now-record.Time > timeout*60 { if now-record.Time > timeout*60 {
return fmt.Sprintf("You should verify your code in %d min!", timeout) return fmt.Sprintf(i18n.Translate(lang, "PhoneErr.CodeTimeOut"), timeout)
} }
if record.Code != code { if record.Code != code {

View File

@ -62,7 +62,7 @@ func AutoSigninFilter(ctx *context.Context) {
password := ctx.Input.Query("password") password := ctx.Input.Query("password")
if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" { if userId != "" && password != "" && ctx.Input.Query("grant_type") == "" {
owner, name := util.GetOwnerAndNameFromId(userId) owner, name := util.GetOwnerAndNameFromId(userId)
_, msg := object.CheckUserPassword(owner, name, password) _, msg := object.CheckUserPassword(owner, name, password, "en")
if msg != "" { if msg != "" {
responseError(ctx, msg) responseError(ctx, msg)
return return

View File

@ -70,6 +70,7 @@
"eslint-plugin-react": "^7.31.1", "eslint-plugin-react": "^7.31.1",
"husky": "^4.3.8", "husky": "^4.3.8",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
"path-browserify": "^1.0.1",
"stylelint": "^14.11.0", "stylelint": "^14.11.0",
"stylelint-config-recommended-less": "^1.0.4", "stylelint-config-recommended-less": "^1.0.4",
"stylelint-config-standard": "^28.0.0" "stylelint-config-standard": "^28.0.0"

View File

@ -221,10 +221,9 @@ class App extends Component {
if (res.status === "ok") { if (res.status === "ok") {
account = res.data; account = res.data;
account.organization = res.data2; account.organization = res.data2;
this.setLanguage(account); this.setLanguage(account);
} else { } else {
if (res.msg !== "Please sign in first") { if (res.data !== "Please login first") {
Setting.showMessage("error", `Failed to sign in: ${res.msg}`); Setting.showMessage("error", `Failed to sign in: ${res.msg}`);
} }
} }

View File

@ -94,6 +94,38 @@
align-items: stretch; align-items: stretch;
} }
.side-image {
display: none;
@media screen and (min-width: 1100px) {
display: block;
position: relative;
width: 500px;
border-right: 0.5px solid rgb(196 203 215);
}
}
.forget-content {
padding: 10px 100px 20px;
border: 2px solid #fff;
border-radius: 7px;
background-color: rgb(255 255 255);
box-shadow: 0 0 20px rgb(0 0 0 / 20%);
}
.login-panel {
margin-top: 50px;
margin-bottom: 50px;
display: flex;
background-color: rgb(255 255 255);
overflow: hidden;
}
.login-form {
text-align: center;
padding: 10px;
}
.login-content { .login-content {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -105,7 +137,9 @@
} }
.loginBackground { .loginBackground {
height: 100%; display: flex;
align-items: center;
flex: 1 1 0;
background: #fff no-repeat; background: #fff no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
background-attachment: fixed; background-attachment: fixed;

View File

@ -32,6 +32,7 @@ import copy from "copy-to-clipboard";
import {Controlled as CodeMirror} from "react-codemirror2"; import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css"; import "codemirror/lib/codemirror.css";
require("codemirror/theme/material-darker.css"); require("codemirror/theme/material-darker.css");
require("codemirror/mode/htmlmixed/htmlmixed"); require("codemirror/mode/htmlmixed/htmlmixed");
require("codemirror/mode/xml/xml"); require("codemirror/mode/xml/xml");
@ -39,13 +40,51 @@ require("codemirror/mode/css/css");
const {Option} = Select; const {Option} = Select;
const template = { const template = `<style>
padding: "30px", .login-panel{
border: "2px solid #ffffff", padding: 40px 70px 0 70px;
borderRadius: "7px", border-radius: 10px;
backgroundColor: "#ffffff", background-color: #ffffff;
boxShadow: " 0px 0px 20px rgba(0, 0, 0, 0.20)", box-shadow: 0 0 30px 20px rgba(0, 0, 0, 0.20);
}; }
</style>`;
const sideTemplate = `<style>
.left-model{
text-align: center;
padding: 30px;
background-color: #8ca0ed;
position: absolute;
transform: none;
width: 100%;
height: 100%;
}
.side-logo{
display: flex;
align-items: center;
}
.side-logo span {
font-family: Montserrat, sans-serif;
font-weight: 900;
font-size: 2.4rem;
line-height: 1.3;
margin-left: 16px;
color: #404040;
}
.img{
max-width: none;
margin: 41px 0 13px;
}
</style>
<div class="left-model">
<span class="side-logo"> <img src="https://cdn.casbin.org/img/casdoor-logo_1185x256.png" alt="Casdoor" style="width: 120px">
<span>SSO</span>
</span>
<div class="img">
<img src="https://cdn.casbin.org/img/casbin.svg" alt="Casdoor"/>
</div>
</div>
`;
class ApplicationEditPage extends React.Component { class ApplicationEditPage extends React.Component {
constructor(props) { constructor(props) {
@ -157,7 +196,6 @@ class ApplicationEditPage extends React.Component {
} }
renderApplication() { renderApplication() {
const preview = JSON.stringify(template, null, 2);
return ( return (
<Card size="small" title={ <Card size="small" title={
<div> <div>
@ -562,10 +600,10 @@ class ApplicationEditPage extends React.Component {
</Col> </Col>
<Col span={22} style={(Setting.isMobile()) ? {maxWidth: "100%"} : {}}> <Col span={22} style={(Setting.isMobile()) ? {maxWidth: "100%"} : {}}>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} : {Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
</Col> </Col>
<Col span={23} > <Col span={22} >
<Input prefix={<LinkOutlined />} value={this.state.application.formBackgroundUrl} onChange={e => { <Input prefix={<LinkOutlined />} value={this.state.application.formBackgroundUrl} onChange={e => {
this.updateApplicationField("formBackgroundUrl", e.target.value); this.updateApplicationField("formBackgroundUrl", e.target.value);
}} /> }} />
@ -590,7 +628,7 @@ class ApplicationEditPage extends React.Component {
<Col span={22}> <Col span={22}>
<Popover placement="right" content={ <Popover placement="right" content={
<div style={{width: "900px", height: "300px"}} > <div style={{width: "900px", height: "300px"}} >
<CodeMirror value={this.state.application.formCss === "" ? preview : this.state.application.formCss} <CodeMirror value={this.state.application.formCss === "" ? template : this.state.application.formCss}
options={{mode: "css", theme: "material-darker"}} options={{mode: "css", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => { onBeforeChange={(editor, data, value) => {
this.updateApplicationField("formCss", value); this.updateApplicationField("formCss", value);
@ -606,14 +644,42 @@ class ApplicationEditPage extends React.Component {
</Row> </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:From position"), i18next.t("application:From position - Tooltip"))} : {Setting.getLabel(i18next.t("application:Form position"), i18next.t("application:Form position - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Radio.Group onChange={e => {this.updateApplicationField("formOffset", e.target.value);}} value={this.state.application.formOffset !== 0 ? this.state.application.formOffset : 8}> <Row style={{marginTop: "20px"}} >
<Radio.Button value={2}>left</Radio.Button> <Radio.Group onChange={e => {this.updateApplicationField("formOffset", e.target.value);}} value={this.state.application.formOffset}>
<Radio.Button value={8}>center</Radio.Button> <Radio.Button value={1}>{i18next.t("application:Left")}</Radio.Button>
<Radio.Button value={14}>right</Radio.Button> <Radio.Button value={2}>{i18next.t("application:Center")}</Radio.Button>
</Radio.Group> <Radio.Button value={3}>{i18next.t("application:Right")}</Radio.Button>
<Radio.Button value={4}>
{i18next.t("application:Enable side panel")}
</Radio.Button>
</Radio.Group>
</Row>
{this.state.application.formOffset === 4 ?
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 3}>
{Setting.getLabel(i18next.t("application:Side panel HTML"), i18next.t("application:Side panel HTML - Tooltip"))} :
</Col>
<Col span={21} >
<Popover placement="right" content={
<div style={{width: "900px", height: "300px"}} >
<CodeMirror value={this.state.application.formSideHtml === "" ? sideTemplate : this.state.application.formSideHtml}
options={{mode: "htmlmixed", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => {
this.updateApplicationField("formSideHtml", value);
}}
/>
</div>
} title={i18next.t("application:Side panel HTML - Edit")} trigger="click">
<Input value={this.state.application.formSideHtml} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("formSideHtml", e.target.value);
}} />
</Popover>
</Col>
</Row>
: null}
</Col> </Col>
</Row> </Row>
{ {
@ -707,7 +773,7 @@ class ApplicationEditPage extends React.Component {
<br /> <br />
<div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}> <div style={{position: "relative", width: "90%", border: "1px solid rgb(217,217,217)", boxShadow: "10px 10px 5px #888888", flexDirection: "column", flex: "auto"}}>
<PromptPage application={this.state.application} account={this.props.account} /> <PromptPage application={this.state.application} account={this.props.account} />
<div style={maskStyle}></div> <div style={maskStyle} />
</div> </div>
</Col> </Col>
); );

View File

@ -39,7 +39,7 @@ class ProviderTable extends React.Component {
} }
addRow(table) { addRow(table) {
const row = {name: Setting.getNewRowNameForTable(table, "Please select a provider"), canSignUp: true, canSignIn: true, canUnlink: true, alertType: "None"}; const row = {name: Setting.getNewRowNameForTable(table, "Please select a provider"), canSignUp: true, canSignIn: true, canUnlink: true, alertType: "None", rule: "None"};
if (table === undefined) { if (table === undefined) {
table = []; table = [];
} }
@ -193,6 +193,28 @@ class ProviderTable extends React.Component {
// ) // )
// } // }
// }, // },
{
title: i18next.t("application:Rule"),
dataIndex: "rule",
key: "rule",
width: "100px",
render: (text, record, index) => {
if (record.provider?.category !== "Captcha") {
return null;
}
return (
<Select virtual={false} style={{width: "100%"}}
value={text}
defaultValue="None"
onChange={value => {
this.updateField(table, index, "rule", value);
}} >
<Option key="None" value="None">{i18next.t("application:None")}</Option>
<Option key="Always" value="Always">{i18next.t("application:Always")}</Option>
</Select>
);
},
},
{ {
title: i18next.t("general:Action"), title: i18next.t("general:Action"),
key: "action", key: "action",

View File

@ -12,9 +12,10 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import React from "react";
import {Link, useHistory} from "react-router-dom";
import {Tag, Tooltip, message} from "antd"; import {Tag, Tooltip, message} from "antd";
import {QuestionCircleTwoTone} from "@ant-design/icons"; import {QuestionCircleTwoTone} from "@ant-design/icons";
import React from "react";
import {isMobile as isMobileDevice} from "react-device-detect"; import {isMobile as isMobileDevice} from "react-device-detect";
import "./i18n"; import "./i18n";
import i18next from "i18next"; import i18next from "i18next";
@ -22,6 +23,7 @@ import copy from "copy-to-clipboard";
import {authConfig} from "./auth/Auth"; import {authConfig} from "./auth/Auth";
import {Helmet} from "react-helmet"; import {Helmet} from "react-helmet";
import * as Conf from "./Conf"; import * as Conf from "./Conf";
import * as path from "path-browserify";
export const ServerUrl = ""; export const ServerUrl = "";
@ -557,6 +559,10 @@ export function changeLanguage(language) {
// window.location.reload(true); // window.location.reload(true);
} }
export function getAcceptLanguage() {
return i18next.language + ";q=0.9,en;q=0.8";
}
export function changeMomentLanguage(language) { export function changeMomentLanguage(language) {
// if (language === "zh") { // if (language === "zh") {
// moment.locale("zh", { // moment.locale("zh", {
@ -737,64 +743,97 @@ export function renderLogo(application) {
} }
} }
export function goToLogin(ths, application) { export function getLoginLink(application) {
let url;
if (application === null) { if (application === null) {
return; url = null;
} } else if (!application.enablePassword && window.location.pathname.includes("/auto-signup/oauth/authorize")) {
url = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize");
if (!application.enablePassword && window.location.pathname.includes("/auto-signup/oauth/authorize")) { } else if (authConfig.appName === application.name) {
const link = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize"); url = "/login";
goToLink(link); } else if (application.signinUrl === "") {
return; url = path.join(application.homepageUrl, "login");
}
if (authConfig.appName === application.name) {
goToLinkSoft(ths, "/login");
} else { } else {
if (application.signinUrl === "") { url = application.signinUrl;
goToLink(`${application.homepageUrl}/login`); }
} else { return url;
goToLink(application.signinUrl); }
}
export function renderLoginLink(application, text) {
const url = getLoginLink(application);
return renderLink(url, text, null);
}
export function redirectToLoginPage(application) {
const loginLink = getLoginLink(application);
const history = useHistory();
history.push(loginLink);
}
function renderLink(url, text, onClick) {
if (url === null) {
return null;
}
if (url.startsWith("/")) {
return (
<Link style={{float: "right"}} to={url} onClick={() => {
if (onClick !== null) {
onClick();
}
}}>{text}</Link>
);
} else if (url.startsWith("http")) {
return (
<a target="_blank" rel="noopener noreferrer" style={{float: "right"}} href={url} onClick={() => {
if (onClick !== null) {
onClick();
}
}}>{text}</a>
);
} else {
return null;
} }
} }
export function goToSignup(ths, application) { export function renderSignupLink(application, text) {
let url;
if (application === null) { if (application === null) {
return; url = null;
} } else if (!application.enablePassword && window.location.pathname.includes("/login/oauth/authorize")) {
url = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize");
if (!application.enablePassword && window.location.pathname.includes("/login/oauth/authorize")) { } else if (authConfig.appName === application.name) {
const link = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize"); url = "/signup";
goToLink(link);
return;
}
if (authConfig.appName === application.name) {
goToLinkSoft(ths, "/signup");
} else { } else {
if (application.signupUrl === "") { if (application.signupUrl === "") {
goToLinkSoft(ths, `/signup/${application.name}`); url = `/signup/${application.name}`;
} else { } else {
goToLink(application.signupUrl); url = application.signupUrl;
} }
} }
const storeSigninUrl = () => {
sessionStorage.setItem("signinUrl", window.location.href);
};
return renderLink(url, text, storeSigninUrl);
} }
export function goToForget(ths, application) { export function renderForgetLink(application, text) {
let url;
if (application === null) { if (application === null) {
return; url = null;
} } else if (authConfig.appName === application.name) {
url = "/forget";
if (authConfig.appName === application.name) {
goToLinkSoft(ths, "/forget");
} else { } else {
if (application.forgetUrl === "") { if (application.forgetUrl === "") {
goToLinkSoft(ths, `/forget/${application.name}`); url = `/forget/${application.name}`;
} else { } else {
goToLink(application.forgetUrl); url = application.forgetUrl;
} }
} }
return renderLink(url, text, null);
} }
export function renderHelmet(application) { export function renderHelmet(application) {

View File

@ -164,7 +164,7 @@ class SignupTable extends React.Component {
}, },
}, },
{ {
title: i18next.t("application:rule"), title: i18next.t("application:Rule"),
dataIndex: "rule", dataIndex: "rule",
key: "rule", key: "rule",
width: "155px", width: "155px",

View File

@ -352,7 +352,7 @@ class UserListPage extends BaseListPage {
return ( return (
<div> <div>
<Table scroll={{x: "max-content"}} columns={columns} dataSource={users} rowKey="name" size="middle" bordered pagination={paginationProps} <Table scroll={{x: "max-content"}} columns={columns} dataSource={users} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
title={() => ( title={() => (
<div> <div>
{i18next.t("general:Users")}&nbsp;&nbsp;&nbsp;&nbsp; {i18next.t("general:Users")}&nbsp;&nbsp;&nbsp;&nbsp;

View File

@ -13,11 +13,15 @@
// limitations under the License. // limitations under the License.
import {authConfig} from "./Auth"; import {authConfig} from "./Auth";
import * as Setting from "../Setting";
export function getAccount(query) { export function getAccount(query) {
return fetch(`${authConfig.serverUrl}/api/get-account${query}`, { return fetch(`${authConfig.serverUrl}/api/get-account${query}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -26,6 +30,9 @@ export function signup(values) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(values), body: JSON.stringify(values),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +41,9 @@ export function getEmailAndPhone(values) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(values), body: JSON.stringify(values),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then((res) => res.json()); }).then((res) => res.json());
} }
@ -51,6 +61,9 @@ export function getApplicationLogin(oAuthParams) {
return fetch(`${authConfig.serverUrl}/api/get-app-login${oAuthParamsToQuery(oAuthParams)}`, { return fetch(`${authConfig.serverUrl}/api/get-app-login${oAuthParamsToQuery(oAuthParams)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,6 +72,9 @@ export function login(values, oAuthParams) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(values), body: JSON.stringify(values),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -67,6 +83,9 @@ export function loginCas(values, params) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(values), body: JSON.stringify(values),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -74,6 +93,9 @@ export function logout() {
return fetch(`${authConfig.serverUrl}/api/logout`, { return fetch(`${authConfig.serverUrl}/api/logout`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -82,6 +104,9 @@ export function unlink(values) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(values), body: JSON.stringify(values),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -89,6 +114,9 @@ export function getSamlLogin(providerId, relayState) {
return fetch(`${authConfig.serverUrl}/api/get-saml-login?id=${providerId}&relayState=${relayState}`, { return fetch(`${authConfig.serverUrl}/api/get-saml-login?id=${providerId}&relayState=${relayState}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -97,5 +125,8 @@ export function loginWithSaml(values, param) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(values), body: JSON.stringify(values),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -166,7 +166,7 @@ class ForgetPage extends React.Component {
values.userOwner = this.state.application?.organizationObj.name; values.userOwner = this.state.application?.organizationObj.name;
UserBackend.setPassword(values.userOwner, values.username, "", values?.newPassword).then(res => { UserBackend.setPassword(values.userOwner, values.username, "", values?.newPassword).then(res => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.goToLogin(this, this.state.application); Setting.redirectToLoginPage(this.state.application);
} else { } else {
Setting.showMessage("error", i18next.t(`signup:${res.msg}`)); Setting.showMessage("error", i18next.t(`signup:${res.msg}`));
} }
@ -487,61 +487,65 @@ class ForgetPage extends React.Component {
} }
return ( return (
<Row> <div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}>
<Col span={24} style={{justifyContent: "center"}}> <CustomGithubCorner />
<div className="login-content forget-content">
<Row> <Row>
<Col span={24}> <Col span={24} style={{justifyContent: "center"}}>
<div style={{marginTop: "80px", marginBottom: "10px", textAlign: "center"}}> <Row>
{ <Col span={24}>
Setting.renderHelmet(application) <div style={{marginTop: "80px", marginBottom: "10px", textAlign: "center"}}>
} {
<CustomGithubCorner /> Setting.renderHelmet(application)
{ }
Setting.renderLogo(application) {
} Setting.renderLogo(application)
}
</div>
</Col>
</Row>
<Row>
<Col span={24}>
<div style={{textAlign: "center", fontSize: "28px"}}>
{i18next.t("forget:Retrieve password")}
</div>
</Col>
</Row>
<Row>
<Col span={24}>
<Steps
current={this.state.current}
style={{
width: "90%",
maxWidth: "500px",
margin: "auto",
marginTop: "80px",
}}
>
<Step
title={i18next.t("forget:Account")}
icon={<UserOutlined />}
/>
<Step
title={i18next.t("forget:Verify")}
icon={<SolutionOutlined />}
/>
<Step
title={i18next.t("forget:Reset")}
icon={<KeyOutlined />}
/>
</Steps>
</Col>
</Row>
</Col>
<Col span={24} style={{display: "flex", justifyContent: "center"}}>
<div style={{marginTop: "10px", textAlign: "center"}}>
{this.renderForm(application)}
</div> </div>
</Col> </Col>
</Row> </Row>
<Row> </div>
<Col span={24}> </div>
<div style={{textAlign: "center", fontSize: "28px"}}>
{i18next.t("forget:Retrieve password")}
</div>
</Col>
</Row>
<Row>
<Col span={24}>
<Steps
current={this.state.current}
style={{
width: "90%",
maxWidth: "500px",
margin: "auto",
marginTop: "80px",
}}
>
<Step
title={i18next.t("forget:Account")}
icon={<UserOutlined />}
/>
<Step
title={i18next.t("forget:Verify")}
icon={<SolutionOutlined />}
/>
<Step
title={i18next.t("forget:Reset")}
icon={<KeyOutlined />}
/>
</Steps>
</Col>
</Row>
</Col>
<Col span={24} style={{display: "flex", justifyContent: "center"}}>
<div style={{marginTop: "10px", textAlign: "center"}}>
{this.renderForm(application)}
</div>
</Col>
</Row>
); );
} }
} }

View File

@ -13,7 +13,6 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Link} from "react-router-dom";
import {Button, Checkbox, Col, Form, Input, Result, Row, Spin, Tabs} from "antd"; import {Button, Checkbox, Col, Form, Input, Result, Row, Spin, Tabs} from "antd";
import {LockOutlined, UserOutlined} from "@ant-design/icons"; import {LockOutlined, UserOutlined} from "@ant-design/icons";
import * as UserWebauthnBackend from "../backend/UserWebauthnBackend"; import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
@ -30,6 +29,7 @@ import CustomGithubCorner from "../CustomGithubCorner";
import {CountDownInput} from "../common/CountDownInput"; import {CountDownInput} from "../common/CountDownInput";
import SelectLanguageBox from "../SelectLanguageBox"; import SelectLanguageBox from "../SelectLanguageBox";
import {withTranslation} from "react-i18next"; import {withTranslation} from "react-i18next";
import {CaptchaModal} from "../common/CaptchaModal";
const {TabPane} = Tabs; const {TabPane} = Tabs;
@ -49,6 +49,9 @@ class LoginPage extends React.Component {
validEmail: false, validEmail: false,
validPhone: false, validPhone: false,
loginMethod: "password", loginMethod: "password",
enableCaptchaModal: false,
openCaptchaModal: false,
verifyCaptcha: undefined,
}; };
if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) { if (this.state.type === "cas" && props.match?.params.casApplicationName !== undefined) {
@ -69,6 +72,18 @@ class LoginPage extends React.Component {
} }
} }
componentDidUpdate(prevProps, prevState, snapshot) {
if (this.state.application && !prevState.application) {
const defaultCaptchaProviderItems = this.getDefaultCaptchaProviderItems(this.state.application);
if (!defaultCaptchaProviderItems) {
return;
}
this.setState({enableCaptchaModal: defaultCaptchaProviderItems.some(providerItem => providerItem.rule === "Always")});
}
}
getApplicationLogin() { getApplicationLogin() {
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
AuthBackend.getApplicationLogin(oAuthParams) AuthBackend.getApplicationLogin(oAuthParams)
@ -139,6 +154,18 @@ class LoginPage extends React.Component {
this.props.onUpdateAccount(account); this.props.onUpdateAccount(account);
} }
parseOffset(offset) {
if (offset === 2 || offset === 4 || Setting.inIframe() || Setting.isMobile()) {
return "0 auto";
}
if (offset === 1) {
return "0 10%";
}
if (offset === 3) {
return "0 60%";
}
}
populateOauthValues(values) { populateOauthValues(values) {
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
if (oAuthParams !== null && oAuthParams.responseType !== null && oAuthParams.responseType !== "") { if (oAuthParams !== null && oAuthParams.responseType !== null && oAuthParams.responseType !== "") {
@ -214,6 +241,23 @@ class LoginPage extends React.Component {
return; return;
} }
if (this.state.loginMethod === "password" && this.state.enableCaptchaModal) {
this.setState({
openCaptchaModal: true,
verifyCaptcha: (captchaType, captchaToken, secret) => {
values["captchaType"] = captchaType;
values["captchaToken"] = captchaToken;
values["clientSecret"] = secret;
this.login(values);
},
});
} else {
this.login(values);
}
}
login(values) {
// here we are supposed to determine whether Casdoor is working as an OAuth server or CAS server // here we are supposed to determine whether Casdoor is working as an OAuth server or CAS server
if (this.state.type === "cas") { if (this.state.type === "cas") {
// CAS // CAS
@ -228,6 +272,8 @@ class LoginPage extends React.Component {
} }
Util.showMessage("success", msg); Util.showMessage("success", msg);
this.setState({openCaptchaModal: false});
if (casParams.service !== "") { if (casParams.service !== "") {
const st = res.data; const st = res.data;
const newUrl = new URL(casParams.service); const newUrl = new URL(casParams.service);
@ -235,6 +281,7 @@ class LoginPage extends React.Component {
window.location.href = newUrl.toString(); window.location.href = newUrl.toString();
} }
} else { } else {
this.setState({openCaptchaModal: false});
Util.showMessage("error", `Failed to log in: ${res.msg}`); Util.showMessage("error", `Failed to log in: ${res.msg}`);
} }
}); });
@ -247,6 +294,7 @@ class LoginPage extends React.Component {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
const responseType = values["type"]; const responseType = values["type"];
if (responseType === "login") { if (responseType === "login") {
Util.showMessage("success", "Logged in successfully"); Util.showMessage("success", "Logged in successfully");
@ -264,6 +312,7 @@ class LoginPage extends React.Component {
Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`); Setting.goToLink(`${redirectUri}?SAMLResponse=${encodeURIComponent(SAMLResponse)}&RelayState=${oAuthParams.relayState}`);
} }
} else { } else {
this.setState({openCaptchaModal: false});
Util.showMessage("error", `Failed to log in: ${res.msg}`); Util.showMessage("error", `Failed to log in: ${res.msg}`);
} }
}); });
@ -290,13 +339,11 @@ class LoginPage extends React.Component {
title="Sign Up Error" title="Sign Up Error"
subTitle={"The application does not allow to sign up new account"} subTitle={"The application does not allow to sign up new account"}
extra={[ extra={[
<Link key="login" onClick={() => { <Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application)}>
Setting.goToLogin(this, application); {
}}> i18next.t("login:Sign In")
<Button type="primary" key="signin"> }
Sign In </Button>,
</Button>
</Link>,
]} ]}
> >
</Result> </Result>
@ -393,11 +440,9 @@ class LoginPage extends React.Component {
{i18next.t("login:Auto sign in")} {i18next.t("login:Auto sign in")}
</Checkbox> </Checkbox>
</Form.Item> </Form.Item>
<a style={{float: "right"}} onClick={() => { {
Setting.goToForget(this, application); Setting.renderForgetLink(application, i18next.t("login:Forgot password?"))
}}> }
{i18next.t("login:Forgot password?")}
</a>
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
<Button <Button
@ -411,6 +456,9 @@ class LoginPage extends React.Component {
i18next.t("login:Sign In") i18next.t("login:Sign In")
} }
</Button> </Button>
{
this.renderCaptchaModal(application)
}
{ {
this.renderFooter(application) this.renderFooter(application)
} }
@ -453,16 +501,54 @@ class LoginPage extends React.Component {
} }
} }
getDefaultCaptchaProviderItems(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" && providerItem.provider.type === "Default";
});
}
renderCaptchaModal(application) {
if (!this.state.enableCaptchaModal) {
return null;
}
const provider = this.getDefaultCaptchaProviderItems(application)
.filter(providerItem => providerItem.rule === "Always")
.map(providerItem => providerItem.provider)[0];
return <CaptchaModal
owner={provider.owner}
name={provider.name}
captchaType={provider.type}
subType={provider.subType}
clientId={provider.clientId}
clientId2={provider.clientId2}
clientSecret={provider.clientSecret}
clientSecret2={provider.clientSecret2}
open={this.state.openCaptchaModal}
onOk={(captchaType, captchaToken, secret) => this.state.verifyCaptcha?.(captchaType, captchaToken, secret)}
canCancel={false}
/>;
}
renderFooter(application) { renderFooter(application) {
if (this.state.mode === "signup") { if (this.state.mode === "signup") {
return ( return (
<div style={{float: "right"}}> <div style={{float: "right"}}>
{i18next.t("signup:Have account?")}&nbsp; {i18next.t("signup:Have account?")}&nbsp;
<Link onClick={() => { {
Setting.goToLogin(this, application); Setting.renderLoginLink(application, i18next.t("signup:sign in now"))
}}> }
{i18next.t("signup:sign in now")}
</Link>
</div> </div>
); );
} else { } else {
@ -473,12 +559,9 @@ class LoginPage extends React.Component {
!application.enableSignUp ? null : ( !application.enableSignUp ? null : (
<> <>
{i18next.t("login:No account?")}&nbsp; {i18next.t("login:No account?")}&nbsp;
<a onClick={() => { {
sessionStorage.setItem("signinUrl", window.location.href); Setting.renderSignupLink(application, i18next.t("login:sign up now"))
Setting.goToSignup(this, application); }
}}>
{i18next.t("login:sign up now")}
</a>
</> </>
) )
} }
@ -695,16 +778,18 @@ class LoginPage extends React.Component {
); );
} }
const formStyle = Setting.inIframe() ? null : Setting.parseObject(application.formCss);
return ( return (
<div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}> <div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}>
<CustomGithubCorner /> <CustomGithubCorner />
<Row> <div className="login-content" style={{margin: this.parseOffset(application.formOffset)}}>
<Col span={8} offset={application.formOffset === 0 || Setting.inIframe() || Setting.isMobile() ? 8 : application.formOffset} style={{display: "flex", justifyContent: "center"}}> {Setting.inIframe() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
<div className="login-content"> <div className="login-panel">
<div style={{marginTop: "80px", marginBottom: "50px", textAlign: "center", ...formStyle}}> <SelectLanguageBox id="language-box-corner" style={{top: "50px"}} />
<SelectLanguageBox id="language-box-corner" style={{top: formStyle !== null ? "80px" : "45px", right: formStyle !== null ? "5px" : "-45px"}} /> <div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
<div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
</div>
<div className="login-form">
<div >
<div> <div>
{ {
Setting.renderHelmet(application) Setting.renderHelmet(application)
@ -724,8 +809,8 @@ class LoginPage extends React.Component {
</div> </div>
</div> </div>
</div> </div>
</Col> </div>
</Row> </div>
</div> </div>
); );
} }

View File

@ -190,7 +190,7 @@ class PromptPage extends React.Component {
if (redirectUrl !== "" && redirectUrl !== null) { if (redirectUrl !== "" && redirectUrl !== null) {
Setting.goToLink(redirectUrl); Setting.goToLink(redirectUrl);
} else { } else {
Setting.goToLogin(this, this.getApplicationObj()); Setting.redirectToLoginPage(this.getApplicationObj());
} }
} else { } else {
Setting.showMessage("error", `Failed to log out: ${res.msg}`); Setting.showMessage("error", `Failed to log out: ${res.msg}`);
@ -234,10 +234,10 @@ class PromptPage extends React.Component {
title="Sign Up Error" title="Sign Up Error"
subTitle={"You are unexpected to see this prompt page"} subTitle={"You are unexpected to see this prompt page"}
extra={[ extra={[
<Button type="primary" key="signin" onClick={() => { <Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application)}>
Setting.goToLogin(this, application); {
}}> i18next.t("login:Sign In")
Sign In }
</Button>, </Button>,
]} ]}
> >

View File

@ -69,7 +69,7 @@ class ResultPage extends React.Component {
if (linkInStorage !== null && linkInStorage !== "") { if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage); Setting.goToLink(linkInStorage);
} else { } else {
Setting.goToLogin(this, application); Setting.redirectToLoginPage(application);
} }
}}> }}>
{i18next.t("login:Sign In")} {i18next.t("login:Sign In")}

View File

@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Button, Checkbox, Col, Form, Input, Modal, Result, Row} from "antd"; import {Button, Checkbox, Form, Input, Modal, Result} from "antd";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import * as ProviderButton from "./ProviderButton"; import * as ProviderButton from "./ProviderButton";
@ -148,6 +148,18 @@ class SignupPage extends React.Component {
this.props.onUpdateAccount(account); this.props.onUpdateAccount(account);
} }
parseOffset(offset) {
if (offset === 2 || offset === 4 || Setting.inIframe() || Setting.isMobile()) {
return "0 auto";
}
if (offset === 1) {
return "0 10%";
}
if (offset === 3) {
return "0 60%";
}
}
onFinish(values) { onFinish(values) {
const application = this.getApplicationObj(); const application = this.getApplicationObj();
values.phonePrefix = application.organizationObj.phonePrefix; values.phonePrefix = application.organizationObj.phonePrefix;
@ -529,10 +541,10 @@ class SignupPage extends React.Component {
title="Sign Up Error" title="Sign Up Error"
subTitle={"The application does not allow to sign up new account"} subTitle={"The application does not allow to sign up new account"}
extra={[ extra={[
<Button type="primary" key="signin" onClick={() => { <Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application)}>
Setting.goToLogin(this, application); {
}}> i18next.t("login:Sign In")
Sign In }
</Button>, </Button>,
]} ]}
> >
@ -588,7 +600,7 @@ class SignupPage extends React.Component {
if (linkInStorage !== null && linkInStorage !== "") { if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLink(linkInStorage); Setting.goToLink(linkInStorage);
} else { } else {
Setting.goToLogin(this, application); Setting.redirectToLoginPage(application);
} }
}}> }}>
{i18next.t("signup:sign in now")} {i18next.t("signup:sign in now")}
@ -615,17 +627,18 @@ class SignupPage extends React.Component {
); );
} }
const formStyle = Setting.inIframe() ? null : Setting.parseObject(application.formCss);
return ( return (
<div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}> <div className="loginBackground" style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${application.formBackgroundUrl})`}}>
<CustomGithubCorner /> <CustomGithubCorner />
&nbsp; <div className="login-content" style={{margin: this.parseOffset(application.formOffset)}}>
<Row> {Setting.inIframe() ? null : <div dangerouslySetInnerHTML={{__html: application.formCss}} />}
<Col span={8} offset={application.formOffset === 0 || Setting.inIframe() || Setting.isMobile() ? 8 : application.formOffset} style={{display: "flex", justifyContent: "center"}} > <div className="login-panel" >
<div className="login-content"> <SelectLanguageBox id="language-box-corner" style={{top: "50px"}} />
<div style={{marginBottom: "10px", textAlign: "center", ...formStyle}}> <div className="side-image" style={{display: application.formOffset !== 4 ? "none" : null}}>
<SelectLanguageBox id="language-box-corner" style={{top: formStyle !== null ? "3px" : "-20px", right: formStyle !== null ? "5px" : "-45px"}} /> <div dangerouslySetInnerHTML={{__html: application.formSideHtml}} />
</div>
<div className="login-form">
<div >
{ {
Setting.renderHelmet(application) Setting.renderHelmet(application)
} }
@ -637,8 +650,8 @@ class SignupPage extends React.Component {
} }
</div> </div>
</div> </div>
</Col> </div>
</Row> </div>
{ {
this.renderModal() this.renderModal()
} }

View File

@ -18,6 +18,9 @@ export function getAdapters(owner, page = "", pageSize = "", field = "", value =
return fetch(`${Setting.ServerUrl}/api/get-adapters?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-adapters?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getAdapter(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-adapter?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-adapter?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateAdapter(owner, name, Adapter) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newAdapter), body: JSON.stringify(newAdapter),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addAdapter(Adapter) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newAdapter), body: JSON.stringify(newAdapter),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,6 +64,9 @@ export function deleteAdapter(Adapter) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newAdapter), body: JSON.stringify(newAdapter),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,5 +74,8 @@ export function syncPolicies(owner, name) {
return fetch(`${Setting.ServerUrl}/api/sync-policies?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/sync-policies?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getApplications(owner, page = "", pageSize = "", field = "", val
return fetch(`${Setting.ServerUrl}/api/get-applications?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-applications?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getApplicationsByOrganization(owner, organization) {
return fetch(`${Setting.ServerUrl}/api/get-organization-applications?owner=${owner}&organization=${organization}`, { return fetch(`${Setting.ServerUrl}/api/get-organization-applications?owner=${owner}&organization=${organization}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -32,6 +38,9 @@ export function getApplication(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-application?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-application?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -39,6 +48,9 @@ export function getUserApplication(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-user-application?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-user-application?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -48,6 +60,9 @@ export function updateApplication(owner, name, application) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newApplication), body: JSON.stringify(newApplication),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -58,6 +73,9 @@ export function addApplication(application) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newApplication), body: JSON.stringify(newApplication),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -67,6 +85,9 @@ export function deleteApplication(application) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newApplication), body: JSON.stringify(newApplication),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -74,5 +95,8 @@ export function getSamlMetadata(owner, name) {
return fetch(`${Setting.ServerUrl}/api/saml/metadata?application=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/saml/metadata?application=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.text()); }).then(res => res.text());
} }

View File

@ -18,6 +18,9 @@ export function getCerts(owner, page = "", pageSize = "", field = "", value = ""
return fetch(`${Setting.ServerUrl}/api/get-certs?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-certs?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getCert(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-cert?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-cert?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateCert(owner, name, cert) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newCert), body: JSON.stringify(newCert),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addCert(cert) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newCert), body: JSON.stringify(newCert),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,5 +64,8 @@ export function deleteCert(cert) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newCert), body: JSON.stringify(newCert),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getLdaps(owner) {
return fetch(`${Setting.ServerUrl}/api/get-ldaps?owner=${owner}`, { return fetch(`${Setting.ServerUrl}/api/get-ldaps?owner=${owner}`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getLdap(id) {
return fetch(`${Setting.ServerUrl}/api/get-ldap?id=${id}`, { return fetch(`${Setting.ServerUrl}/api/get-ldap?id=${id}`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -33,6 +39,9 @@ export function addLdap(body) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(body), body: JSON.stringify(body),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -41,6 +50,9 @@ export function deleteLdap(body) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(body), body: JSON.stringify(body),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -49,6 +61,9 @@ export function updateLdap(body) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(body), body: JSON.stringify(body),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -57,6 +72,9 @@ export function getLdapUser(body) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(body), body: JSON.stringify(body),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -65,6 +83,9 @@ export function syncUsers(owner, ldapId, body) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(body), body: JSON.stringify(body),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -73,5 +94,8 @@ export function checkLdapUsersExist(owner, body) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(body), body: JSON.stringify(body),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getModels(owner, page = "", pageSize = "", field = "", value = "
return fetch(`${Setting.ServerUrl}/api/get-models?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-models?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getModel(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-model?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-model?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateModel(owner, name, model) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newModel), body: JSON.stringify(newModel),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addModel(model) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newModel), body: JSON.stringify(newModel),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,5 +64,8 @@ export function deleteModel(model) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newModel), body: JSON.stringify(newModel),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getOrganizations(owner, page = "", pageSize = "", field = "", va
return fetch(`${Setting.ServerUrl}/api/get-organizations?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-organizations?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getOrganization(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-organization?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-organization?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateOrganization(owner, name, organization) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newOrganization), body: JSON.stringify(newOrganization),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addOrganization(organization) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newOrganization), body: JSON.stringify(newOrganization),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,6 +64,9 @@ export function deleteOrganization(organization) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newOrganization), body: JSON.stringify(newOrganization),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,5 +74,8 @@ export function getDefaultApplication(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-default-application?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-default-application?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getPayments(owner, page = "", pageSize = "", field = "", value =
return fetch(`${Setting.ServerUrl}/api/get-payments?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-payments?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getPayment(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-payment?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-payment?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updatePayment(owner, name, payment) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newPayment), body: JSON.stringify(newPayment),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addPayment(payment) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newPayment), body: JSON.stringify(newPayment),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,6 +64,9 @@ export function deletePayment(payment) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newPayment), body: JSON.stringify(newPayment),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,5 +74,8 @@ export function invoicePayment(owner, name) {
return fetch(`${Setting.ServerUrl}/api/invoice-payment?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/invoice-payment?id=${owner}/${encodeURIComponent(name)}`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getPermissions(owner, page = "", pageSize = "", field = "", valu
return fetch(`${Setting.ServerUrl}/api/get-permissions?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-permissions?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getPermissionsBySubmitter() {
return fetch(`${Setting.ServerUrl}/api/get-permissions-by-submitter`, { return fetch(`${Setting.ServerUrl}/api/get-permissions-by-submitter`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -32,6 +38,9 @@ export function getPermission(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-permission?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-permission?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -41,6 +50,9 @@ export function updatePermission(owner, name, permission) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newPermission), body: JSON.stringify(newPermission),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -50,6 +62,9 @@ export function addPermission(permission) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newPermission), body: JSON.stringify(newPermission),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,5 +74,8 @@ export function deletePermission(permission) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newPermission), body: JSON.stringify(newPermission),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getProducts(owner, page = "", pageSize = "", field = "", value =
return fetch(`${Setting.ServerUrl}/api/get-products?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-products?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getProduct(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-product?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-product?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateProduct(owner, name, product) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newProduct), body: JSON.stringify(newProduct),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addProduct(product) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newProduct), body: JSON.stringify(newProduct),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,6 +64,9 @@ export function deleteProduct(product) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newProduct), body: JSON.stringify(newProduct),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,5 +74,8 @@ export function buyProduct(owner, name, providerId) {
return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerId}`, { return fetch(`${Setting.ServerUrl}/api/buy-product?id=${owner}/${encodeURIComponent(name)}&providerName=${providerId}`, {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getProviders(owner, page = "", pageSize = "", field = "", value
return fetch(`${Setting.ServerUrl}/api/get-providers?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-providers?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getProvider(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-provider?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-provider?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateProvider(owner, name, provider) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newProvider), body: JSON.stringify(newProvider),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addProvider(provider) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newProvider), body: JSON.stringify(newProvider),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,5 +64,8 @@ export function deleteProvider(provider) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newProvider), body: JSON.stringify(newProvider),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,5 +18,8 @@ export function getRecords(page, pageSize, field = "", value = "", sortField = "
return fetch(`${Setting.ServerUrl}/api/get-records?pageSize=${pageSize}&p=${page}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-records?pageSize=${pageSize}&p=${page}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getResources(owner, user, page = "", pageSize = "", field = "",
return fetch(`${Setting.ServerUrl}/api/get-resources?owner=${owner}&user=${user}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-resources?owner=${owner}&user=${user}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getResource(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-resource?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-resource?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateResource(owner, name, resource) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newResource), body: JSON.stringify(newResource),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addResource(resource) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newResource), body: JSON.stringify(newResource),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,6 +64,9 @@ export function deleteResource(resource, provider = "") {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newResource), body: JSON.stringify(newResource),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -63,5 +78,8 @@ export function uploadResource(owner, user, tag, parent, fullFilePath, file, pro
body: formData, body: formData,
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getRoles(owner, page = "", pageSize = "", field = "", value = ""
return fetch(`${Setting.ServerUrl}/api/get-roles?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-roles?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getRole(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-role?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-role?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateRole(owner, name, role) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newRole), body: JSON.stringify(newRole),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addRole(role) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newRole), body: JSON.stringify(newRole),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,5 +64,8 @@ export function deleteRole(role) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newRole), body: JSON.stringify(newRole),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getSyncers(owner, page = "", pageSize = "", field = "", value =
return fetch(`${Setting.ServerUrl}/api/get-syncers?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-syncers?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getSyncer(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-syncer?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-syncer?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateSyncer(owner, name, syncer) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newSyncer), body: JSON.stringify(newSyncer),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addSyncer(syncer) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newSyncer), body: JSON.stringify(newSyncer),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,6 +64,9 @@ export function deleteSyncer(syncer) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newSyncer), body: JSON.stringify(newSyncer),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -59,5 +74,8 @@ export function runSyncer(owner, name) {
return fetch(`${Setting.ServerUrl}/api/run-syncer?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/run-syncer?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getSystemInfo(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-system-info?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-system-info?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,5 +28,8 @@ export function getGitHubLatestReleaseVersion() {
return fetch(`${Setting.ServerUrl}/api/get-release`, { return fetch(`${Setting.ServerUrl}/api/get-release`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getTokens(owner, page = "", pageSize = "", field = "", value = "
return fetch(`${Setting.ServerUrl}/api/get-tokens?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-tokens?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getToken(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-token?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-token?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateToken(owner, name, token) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newToken), body: JSON.stringify(newToken),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addToken(token) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newToken), body: JSON.stringify(newToken),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,5 +64,8 @@ export function deleteToken(token) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newToken), body: JSON.stringify(newToken),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -19,6 +19,9 @@ export function getGlobalUsers(page, pageSize, field = "", value = "", sortField
return fetch(`${Setting.ServerUrl}/api/get-global-users?p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-global-users?p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -26,6 +29,9 @@ export function getUsers(owner, page = "", pageSize = "", field = "", value = ""
return fetch(`${Setting.ServerUrl}/api/get-users?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-users?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -33,6 +39,9 @@ export function getUser(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-user?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-user?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -42,6 +51,9 @@ export function updateUser(owner, name, user) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newUser), body: JSON.stringify(newUser),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -51,6 +63,9 @@ export function addUser(user) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newUser), body: JSON.stringify(newUser),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -60,6 +75,9 @@ export function deleteUser(user) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newUser), body: JSON.stringify(newUser),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -85,6 +103,9 @@ export function setPassword(userOwner, userName, oldPassword, newPassword) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: formData, body: formData,
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -101,6 +122,9 @@ export function sendCode(checkType, checkId, checkKey, dest, type, applicationId
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: formData, body: formData,
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()).then(res => { }).then(res => res.json()).then(res => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("user:Code Sent")); Setting.showMessage("success", i18next.t("user:Code Sent"));
@ -121,6 +145,9 @@ export function verifyCaptcha(captchaType, captchaToken, clientSecret) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: formData, body: formData,
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()).then(res => { }).then(res => res.json()).then(res => {
if (res.status === "ok") { if (res.status === "ok") {
if (res.data) { if (res.data) {
@ -145,11 +172,17 @@ export function resetEmailOrPhone(dest, type, code) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: formData, body: formData,
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
export function getCaptcha(owner, name, isCurrentProvider) { export function getCaptcha(owner, name, isCurrentProvider) {
return fetch(`${Setting.ServerUrl}/api/get-captcha?applicationId=${owner}/${encodeURIComponent(name)}&isCurrentProvider=${isCurrentProvider}`, { return fetch(`${Setting.ServerUrl}/api/get-captcha?applicationId=${owner}/${encodeURIComponent(name)}&isCurrentProvider=${isCurrentProvider}`, {
method: "GET", method: "GET",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()).then(res => res.data); }).then(res => res.json()).then(res => res.data);
} }

View File

@ -18,6 +18,9 @@ export function registerWebauthnCredential() {
return fetch(`${Setting.ServerUrl}/api/webauthn/signup/begin`, { return fetch(`${Setting.ServerUrl}/api/webauthn/signup/begin`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}) })
.then(res => res.json()) .then(res => res.json())
.then((credentialCreationOptions) => { .then((credentialCreationOptions) => {
@ -62,6 +65,9 @@ export function deleteUserWebAuthnCredential(credentialID) {
credentials: "include", credentials: "include",
body: form, body: form,
dataType: "text", dataType: "text",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -18,6 +18,9 @@ export function getWebhooks(owner, page = "", pageSize = "", field = "", value =
return fetch(`${Setting.ServerUrl}/api/get-webhooks?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, { return fetch(`${Setting.ServerUrl}/api/get-webhooks?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -25,6 +28,9 @@ export function getWebhook(owner, name) {
return fetch(`${Setting.ServerUrl}/api/get-webhook?id=${owner}/${encodeURIComponent(name)}`, { return fetch(`${Setting.ServerUrl}/api/get-webhook?id=${owner}/${encodeURIComponent(name)}`, {
method: "GET", method: "GET",
credentials: "include", credentials: "include",
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -34,6 +40,9 @@ export function updateWebhook(owner, name, webhook) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newWebhook), body: JSON.stringify(newWebhook),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -43,6 +52,9 @@ export function addWebhook(webhook) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newWebhook), body: JSON.stringify(newWebhook),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }
@ -52,5 +64,8 @@ export function deleteWebhook(webhook) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
body: JSON.stringify(newWebhook), body: JSON.stringify(newWebhook),
headers: {
"Accept-Language": Setting.getAcceptLanguage(),
},
}).then(res => res.json()); }).then(res => res.json());
} }

View File

@ -62,10 +62,10 @@ class SingleCard extends React.Component {
<Card <Card
hoverable hoverable
cover={ cover={
<img alt="logo" src={logo} style={{width: "100%", objectFit: "scale-down"}} /> <img alt="logo" src={logo} style={{width: "100%", height: "200px", padding: "20px", objectFit: "scale-down"}} />
} }
onClick={() => Setting.goToLinkSoft(this, silentSigninLink)} onClick={() => Setting.goToLinkSoft(this, silentSigninLink)}
style={isSingle ? {width: "320px"} : {width: "100%"}} style={isSingle ? {width: "320px", height: "100%"} : {width: "100%", height: "100%"}}
> >
<Meta title={title} description={desc} /> <Meta title={title} description={desc} />
<br /> <br />

View File

@ -0,0 +1,159 @@
// Copyright 2022 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 {Button, Col, Input, Modal, Row} from "antd";
import i18next from "i18next";
import React, {useEffect} from "react";
import * as UserBackend from "../backend/UserBackend";
import {CaptchaWidget} from "./CaptchaWidget";
import {SafetyOutlined} from "@ant-design/icons";
export const CaptchaModal = ({
owner,
name,
captchaType,
subType,
clientId,
clientId2,
clientSecret,
clientSecret2,
open,
onOk,
onCancel,
canCancel,
}) => {
const [visible, setVisible] = React.useState(false);
const [captchaImg, setCaptchaImg] = React.useState("");
const [captchaToken, setCaptchaToken] = React.useState("");
const [secret, setSecret] = React.useState(clientSecret);
const [secret2, setSecret2] = React.useState(clientSecret2);
useEffect(() => {
setVisible(() => {
if (open) {
getCaptchaFromBackend();
} else {
cleanUp();
}
return open;
});
}, [open]);
const handleOk = () => {
onOk?.(captchaType, captchaToken, secret);
};
const handleCancel = () => {
onCancel?.();
};
const cleanUp = () => {
setCaptchaToken("");
};
const getCaptchaFromBackend = () => {
UserBackend.getCaptcha(owner, name, true).then((res) => {
if (captchaType === "Default") {
setSecret(res.captchaId);
setCaptchaImg(res.captchaImage);
} else {
setSecret(res.clientSecret);
setSecret2(res.clientSecret2);
}
});
};
const renderDefaultCaptcha = () => {
return (
<Col>
<Row
style={{
backgroundImage: `url('data:image/png;base64,${captchaImg}')`,
backgroundRepeat: "no-repeat",
height: "80px",
width: "200px",
borderRadius: "5px",
border: "1px solid #ccc",
marginBottom: 10,
}}
/>
<Row>
<Input
autoFocus
value={captchaToken}
prefix={<SafetyOutlined />}
placeholder={i18next.t("general:Captcha")}
onPressEnter={handleOk}
onChange={(e) => setCaptchaToken(e.target.value)}
/>
</Row>
</Col>
);
};
const onSubmit = (token) => {
setCaptchaToken(token);
};
const renderCheck = () => {
if (captchaType === "Default") {
return renderDefaultCaptcha();
} else {
return (
<Col>
<Row>
<CaptchaWidget
captchaType={captchaType}
subType={subType}
siteKey={clientId}
clientSecret={secret}
onChange={onSubmit}
clientId2={clientId2}
clientSecret2={secret2}
/>
</Row>
</Col>
);
}
};
const renderFooter = () => {
if (canCancel) {
return [
<Button key="cancel" onClick={handleCancel}>{i18next.t("user:Cancel")}</Button>,
<Button key="ok" type="primary" onClick={handleOk}>{i18next.t("user:OK")}</Button>,
];
} else {
return [
<Button key="ok" type="primary" onClick={handleOk}>{i18next.t("user:OK")}</Button>,
];
}
};
return (
<React.Fragment>
<Modal
closable={false}
maskClosable={false}
destroyOnClose={true}
title={i18next.t("general:Captcha")}
visible={visible}
width={348}
footer={renderFooter()}
>
{renderCheck()}
</Modal>
</React.Fragment>
);
};

View File

@ -12,13 +12,12 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import {Button, Col, Input, Modal, Row} from "antd"; import {Button} from "antd";
import React from "react"; import React from "react";
import i18next from "i18next"; import i18next from "i18next";
import * as UserBackend from "../backend/UserBackend"; import {CaptchaModal} from "./CaptchaModal";
import * as ProviderBackend from "../backend/ProviderBackend"; import * as ProviderBackend from "../backend/ProviderBackend";
import {SafetyOutlined} from "@ant-design/icons"; import * as UserBackend from "../backend/UserBackend";
import {CaptchaWidget} from "./CaptchaWidget";
export const CaptchaPreview = ({ export const CaptchaPreview = ({
provider, provider,
@ -33,37 +32,9 @@ export const CaptchaPreview = ({
clientId2, clientId2,
clientSecret2, clientSecret2,
}) => { }) => {
const [visible, setVisible] = React.useState(false); const [open, setOpen] = React.useState(false);
const [captchaImg, setCaptchaImg] = React.useState("");
const [captchaToken, setCaptchaToken] = React.useState("");
const [secret, setSecret] = React.useState(clientSecret);
const [secret2, setSecret2] = React.useState(clientSecret2);
const handleOk = () => {
UserBackend.verifyCaptcha(captchaType, captchaToken, secret).then(() => {
setCaptchaToken("");
setVisible(false);
});
};
const handleCancel = () => {
setVisible(false);
};
const getCaptchaFromBackend = () => {
UserBackend.getCaptcha(owner, name, true).then((res) => {
if (captchaType === "Default") {
setSecret(res.captchaId);
setCaptchaImg(res.captchaImage);
} else {
setSecret(res.clientSecret);
setSecret2(res.clientSecret2);
}
});
};
const clickPreview = () => { const clickPreview = () => {
setVisible(true);
provider.name = name; provider.name = name;
provider.clientId = clientId; provider.clientId = clientId;
provider.type = captchaType; provider.type = captchaType;
@ -71,64 +42,10 @@ export const CaptchaPreview = ({
if (clientSecret !== "***") { if (clientSecret !== "***") {
provider.clientSecret = clientSecret; provider.clientSecret = clientSecret;
ProviderBackend.updateProvider(owner, providerName, provider).then(() => { ProviderBackend.updateProvider(owner, providerName, provider).then(() => {
getCaptchaFromBackend(); setOpen(true);
}); });
} else { } else {
getCaptchaFromBackend(); setOpen(true);
}
};
const renderDefaultCaptcha = () => {
return (
<Col>
<Row
style={{
backgroundImage: `url('data:image/png;base64,${captchaImg}')`,
backgroundRepeat: "no-repeat",
height: "80px",
width: "200px",
borderRadius: "5px",
border: "1px solid #ccc",
marginBottom: 10,
}}
/>
<Row>
<Input
autoFocus
value={captchaToken}
prefix={<SafetyOutlined />}
placeholder={i18next.t("general:Captcha")}
onPressEnter={handleOk}
onChange={(e) => setCaptchaToken(e.target.value)}
/>
</Row>
</Col>
);
};
const onSubmit = (token) => {
setCaptchaToken(token);
};
const renderCheck = () => {
if (captchaType === "Default") {
return renderDefaultCaptcha();
} else {
return (
<Col>
<Row>
<CaptchaWidget
captchaType={captchaType}
subType={subType}
siteKey={clientId}
clientSecret={secret}
onChange={onSubmit}
clientId2={clientId2}
clientSecret2={secret2}
/>
</Row>
</Col>
);
} }
}; };
@ -146,6 +63,16 @@ export const CaptchaPreview = ({
return false; return false;
}; };
const onOk = (captchaType, captchaToken, secret) => {
UserBackend.verifyCaptcha(captchaType, captchaToken, secret).then(() => {
setOpen(false);
});
};
const onCancel = () => {
setOpen(false);
};
return ( return (
<React.Fragment> <React.Fragment>
<Button <Button
@ -156,20 +83,20 @@ export const CaptchaPreview = ({
> >
{i18next.t("general:Preview")} {i18next.t("general:Preview")}
</Button> </Button>
<Modal <CaptchaModal
closable={false} owner={owner}
maskClosable={false} name={name}
destroyOnClose={true} captchaType={captchaType}
title={i18next.t("general:Captcha")} subType={subType}
visible={visible} clientId={clientId}
okText={i18next.t("user:OK")} clientId2={clientId2}
cancelText={i18next.t("user:Cancel")} clientSecret={clientSecret}
onOk={handleOk} clientSecret2={clientSecret2}
onCancel={handleCancel} open={open}
width={348} onOk={onOk}
> onCancel={onCancel}
{renderCheck()} canCancel={true}
</Modal> />
</React.Fragment> </React.Fragment>
); );
}; };

View File

@ -17,6 +17,7 @@
"Auto signin - Tooltip": "Auto signin - Tooltip", "Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip", "Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL", "Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL", "Copy signin page URL": "Copy signin page URL",
@ -28,6 +29,7 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Code-Anmeldung aktivieren", "Enable code signin": "Code-Anmeldung aktivieren",
"Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip", "Enable code signin - Tooltip": "Aktiviere Codeanmeldung - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip", "Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip",
"Enable signup": "Anmeldung aktivieren", "Enable signup": "Anmeldung aktivieren",
"Enable signup - Tooltip": "Whether to allow users to sign up", "Enable signup - Tooltip": "Whether to allow users to sign up",
@ -35,10 +37,11 @@
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip", "Form CSS - Tooltip": "Form CSS - Tooltip",
"From position": "From position", "Form position": "Form position",
"From position - Tooltip": "From position - Tooltip", "Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types", "Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip", "Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application", "New Application": "New Application",
"Password ON": "Passwort AN", "Password ON": "Passwort AN",
"Password ON - Tooltip": "Whether to allow password login", "Password ON - Tooltip": "Whether to allow password login",
@ -49,9 +52,13 @@
"Redirect URLs - Tooltip": "List of redirect addresses after successful login", "Redirect URLs - Tooltip": "List of redirect addresses after successful login",
"Refresh token expire": "Aktualisierungs-Token läuft ab", "Refresh token expire": "Aktualisierungs-Token läuft ab",
"Refresh token expire - Tooltip": "Aktualisierungs-Token läuft ab - Tooltip", "Refresh token expire - Tooltip": "Aktualisierungs-Token läuft ab - Tooltip",
"Right": "Right",
"SAML metadata": "SAML metadata", "SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip", "SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully", "SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser", "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Anmeldesitzung", "Signin session": "Anmeldesitzung",
"Signup items": "Artikel registrieren", "Signup items": "Artikel registrieren",
@ -61,7 +68,9 @@
"Token expire - Tooltip": "Token läuft ab - Tooltip", "Token expire - Tooltip": "Token läuft ab - Tooltip",
"Token format": "Token-Format", "Token format": "Token-Format",
"Token format - Tooltip": "Token-Format - Tooltip", "Token format - Tooltip": "Token-Format - Tooltip",
"rule": "rule" "Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Bitgröße", "Bit size": "Bitgröße",
@ -688,6 +697,7 @@
"Old Password": "Altes Passwort", "Old Password": "Altes Passwort",
"Password": "Passwort", "Password": "Passwort",
"Password Set": "Passwort setzen", "Password Set": "Passwort setzen",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Eigenschaften", "Properties": "Eigenschaften",
"Re-enter New": "Neu erneut eingeben", "Re-enter New": "Neu erneut eingeben",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",

View File

@ -17,6 +17,7 @@
"Auto signin - Tooltip": "Auto signin - Tooltip", "Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip", "Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL", "Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL", "Copy signin page URL": "Copy signin page URL",
@ -28,6 +29,7 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin", "Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip", "Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip", "Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup", "Enable signup": "Enable signup",
"Enable signup - Tooltip": "Enable signup - Tooltip", "Enable signup - Tooltip": "Enable signup - Tooltip",
@ -35,10 +37,11 @@
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip", "Form CSS - Tooltip": "Form CSS - Tooltip",
"From position": "From position", "Form position": "Form position",
"From position - Tooltip": "From position - Tooltip", "Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types", "Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip", "Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application", "New Application": "New Application",
"Password ON": "Password ON", "Password ON": "Password ON",
"Password ON - Tooltip": "Password ON - Tooltip", "Password ON - Tooltip": "Password ON - Tooltip",
@ -49,9 +52,13 @@
"Redirect URLs - Tooltip": "Redirect URLs - Tooltip", "Redirect URLs - Tooltip": "Redirect URLs - Tooltip",
"Refresh token expire": "Refresh token expire", "Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip", "Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
"Right": "Right",
"SAML metadata": "SAML metadata", "SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip", "SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully", "SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser", "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Signin session", "Signin session": "Signin session",
"Signup items": "Signup items", "Signup items": "Signup items",
@ -61,7 +68,9 @@
"Token expire - Tooltip": "Token expire - Tooltip", "Token expire - Tooltip": "Token expire - Tooltip",
"Token format": "Token format", "Token format": "Token format",
"Token format - Tooltip": "Token format - Tooltip", "Token format - Tooltip": "Token format - Tooltip",
"rule": "rule" "Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Bit size", "Bit size": "Bit size",
@ -688,6 +697,7 @@
"Old Password": "Old Password", "Old Password": "Old Password",
"Password": "Password", "Password": "Password",
"Password Set": "Password Set", "Password Set": "Password Set",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties", "Properties": "Properties",
"Re-enter New": "Re-enter New", "Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",

View File

@ -45,7 +45,9 @@
"Token expire - Tooltip": "Expiración del Token - Tooltip", "Token expire - Tooltip": "Expiración del Token - Tooltip",
"Token format": "Formato del Token", "Token format": "Formato del Token",
"Token format - Tooltip": "Formato del Token - Tooltip", "Token format - Tooltip": "Formato del Token - Tooltip",
"rule": "rule" "Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Tamaño del Bit", "Bit size": "Tamaño del Bit",

View File

@ -17,6 +17,7 @@
"Auto signin - Tooltip": "Auto signin - Tooltip", "Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip", "Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL", "Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL", "Copy signin page URL": "Copy signin page URL",
@ -28,6 +29,7 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Activer la connexion au code", "Enable code signin": "Activer la connexion au code",
"Enable code signin - Tooltip": "Activer la connexion au code - infobulle", "Enable code signin - Tooltip": "Activer la connexion au code - infobulle",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Activer la session de connexion - infobulle", "Enable signin session - Tooltip": "Activer la session de connexion - infobulle",
"Enable signup": "Activer l'inscription", "Enable signup": "Activer l'inscription",
"Enable signup - Tooltip": "Whether to allow users to sign up", "Enable signup - Tooltip": "Whether to allow users to sign up",
@ -35,10 +37,11 @@
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip", "Form CSS - Tooltip": "Form CSS - Tooltip",
"From position": "From position", "Form position": "Form position",
"From position - Tooltip": "From position - Tooltip", "Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types", "Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip", "Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application", "New Application": "New Application",
"Password ON": "Mot de passe activé", "Password ON": "Mot de passe activé",
"Password ON - Tooltip": "Whether to allow password login", "Password ON - Tooltip": "Whether to allow password login",
@ -49,9 +52,13 @@
"Redirect URLs - Tooltip": "List of redirect addresses after successful login", "Redirect URLs - Tooltip": "List of redirect addresses after successful login",
"Refresh token expire": "Expiration du jeton d'actualisation", "Refresh token expire": "Expiration du jeton d'actualisation",
"Refresh token expire - Tooltip": "Expiration du jeton d'actualisation - infobulle", "Refresh token expire - Tooltip": "Expiration du jeton d'actualisation - infobulle",
"Right": "Right",
"SAML metadata": "SAML metadata", "SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip", "SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully", "SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser", "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Connexion à la session", "Signin session": "Connexion à la session",
"Signup items": "Inscrire des éléments", "Signup items": "Inscrire des éléments",
@ -61,7 +68,9 @@
"Token expire - Tooltip": "Expiration du jeton - Info-bulle", "Token expire - Tooltip": "Expiration du jeton - Info-bulle",
"Token format": "Format du jeton", "Token format": "Format du jeton",
"Token format - Tooltip": "Format du jeton - infobulle", "Token format - Tooltip": "Format du jeton - infobulle",
"rule": "rule" "Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Taille du bit", "Bit size": "Taille du bit",
@ -688,6 +697,7 @@
"Old Password": "Ancien mot de passe", "Old Password": "Ancien mot de passe",
"Password": "Mot de passe", "Password": "Mot de passe",
"Password Set": "Mot de passe défini", "Password Set": "Mot de passe défini",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Propriétés", "Properties": "Propriétés",
"Re-enter New": "Nouvelle entrée", "Re-enter New": "Nouvelle entrée",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",

View File

@ -17,6 +17,7 @@
"Auto signin - Tooltip": "Auto signin - Tooltip", "Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip", "Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL", "Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL", "Copy signin page URL": "Copy signin page URL",
@ -28,6 +29,7 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "コードサインインを有効にする", "Enable code signin": "コードサインインを有効にする",
"Enable code signin - Tooltip": "Enable code signin - Tooltip", "Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip", "Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "サインアップを有効にする", "Enable signup": "サインアップを有効にする",
"Enable signup - Tooltip": "Whether to allow users to sign up", "Enable signup - Tooltip": "Whether to allow users to sign up",
@ -35,10 +37,11 @@
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip", "Form CSS - Tooltip": "Form CSS - Tooltip",
"From position": "From position", "Form position": "Form position",
"From position - Tooltip": "From position - Tooltip", "Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types", "Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip", "Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application", "New Application": "New Application",
"Password ON": "パスワードON", "Password ON": "パスワードON",
"Password ON - Tooltip": "Whether to allow password login", "Password ON - Tooltip": "Whether to allow password login",
@ -49,9 +52,13 @@
"Redirect URLs - Tooltip": "List of redirect addresses after successful login", "Redirect URLs - Tooltip": "List of redirect addresses after successful login",
"Refresh token expire": "トークンの更新の期限が切れます", "Refresh token expire": "トークンの更新の期限が切れます",
"Refresh token expire - Tooltip": "トークンの有効期限を更新する - ツールチップ", "Refresh token expire - Tooltip": "トークンの有効期限を更新する - ツールチップ",
"Right": "Right",
"SAML metadata": "SAML metadata", "SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip", "SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully", "SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser", "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "サインインセッション", "Signin session": "サインインセッション",
"Signup items": "アイテムの登録", "Signup items": "アイテムの登録",
@ -61,7 +68,9 @@
"Token expire - Tooltip": "トークンの有効期限 - ツールチップ", "Token expire - Tooltip": "トークンの有効期限 - ツールチップ",
"Token format": "トークンのフォーマット", "Token format": "トークンのフォーマット",
"Token format - Tooltip": "トークンフォーマット - ツールチップ", "Token format - Tooltip": "トークンフォーマット - ツールチップ",
"rule": "rule" "Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "ビットサイズ", "Bit size": "ビットサイズ",
@ -688,6 +697,7 @@
"Old Password": "古いパスワード", "Old Password": "古いパスワード",
"Password": "パスワード", "Password": "パスワード",
"Password Set": "パスワード設定", "Password Set": "パスワード設定",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "プロパティー", "Properties": "プロパティー",
"Re-enter New": "新しい入力", "Re-enter New": "新しい入力",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",

View File

@ -17,6 +17,7 @@
"Auto signin - Tooltip": "Auto signin - Tooltip", "Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip", "Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Copy SAML metadata URL", "Copy SAML metadata URL": "Copy SAML metadata URL",
"Copy prompt page URL": "Copy prompt page URL", "Copy prompt page URL": "Copy prompt page URL",
"Copy signin page URL": "Copy signin page URL", "Copy signin page URL": "Copy signin page URL",
@ -28,6 +29,7 @@
"Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip", "Enable WebAuthn signin - Tooltip": "Enable WebAuthn signin - Tooltip",
"Enable code signin": "Enable code signin", "Enable code signin": "Enable code signin",
"Enable code signin - Tooltip": "Enable code signin - Tooltip", "Enable code signin - Tooltip": "Enable code signin - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Enable signin session - Tooltip", "Enable signin session - Tooltip": "Enable signin session - Tooltip",
"Enable signup": "Enable signup", "Enable signup": "Enable signup",
"Enable signup - Tooltip": "Whether to allow users to sign up", "Enable signup - Tooltip": "Whether to allow users to sign up",
@ -35,10 +37,11 @@
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip", "Form CSS - Tooltip": "Form CSS - Tooltip",
"From position": "From position", "Form position": "Form position",
"From position - Tooltip": "From position - Tooltip", "Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Grant types", "Grant types": "Grant types",
"Grant types - Tooltip": "Grant types - Tooltip", "Grant types - Tooltip": "Grant types - Tooltip",
"Left": "Left",
"New Application": "New Application", "New Application": "New Application",
"Password ON": "Password ON", "Password ON": "Password ON",
"Password ON - Tooltip": "Whether to allow password login", "Password ON - Tooltip": "Whether to allow password login",
@ -49,9 +52,13 @@
"Redirect URLs - Tooltip": "List of redirect addresses after successful login", "Redirect URLs - Tooltip": "List of redirect addresses after successful login",
"Refresh token expire": "Refresh token expire", "Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expire - Tooltip", "Refresh token expire - Tooltip": "Refresh token expire - Tooltip",
"Right": "Right",
"SAML metadata": "SAML metadata", "SAML metadata": "SAML metadata",
"SAML metadata - Tooltip": "SAML metadata - Tooltip", "SAML metadata - Tooltip": "SAML metadata - Tooltip",
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully", "SAML metadata URL copied to clipboard successfully": "SAML metadata URL copied to clipboard successfully",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser", "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
"Signin session": "Signin session", "Signin session": "Signin session",
"Signup items": "Signup items", "Signup items": "Signup items",
@ -61,7 +68,9 @@
"Token expire - Tooltip": "Token expire - Tooltip", "Token expire - Tooltip": "Token expire - Tooltip",
"Token format": "Token format", "Token format": "Token format",
"Token format - Tooltip": "Token format - Tooltip", "Token format - Tooltip": "Token format - Tooltip",
"rule": "rule" "Rule": "Rule",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Bit size", "Bit size": "Bit size",
@ -688,6 +697,7 @@
"Old Password": "Old Password", "Old Password": "Old Password",
"Password": "Password", "Password": "Password",
"Password Set": "Password Set", "Password Set": "Password Set",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Properties", "Properties": "Properties",
"Re-enter New": "Re-enter New", "Re-enter New": "Re-enter New",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",

View File

@ -17,6 +17,7 @@
"Auto signin - Tooltip": "Auto signin - Tooltip", "Auto signin - Tooltip": "Auto signin - Tooltip",
"Background URL": "Background URL", "Background URL": "Background URL",
"Background URL - Tooltip": "Background URL - Tooltip", "Background URL - Tooltip": "Background URL - Tooltip",
"Center": "Center",
"Copy SAML metadata URL": "Копировать адрес метаданных SAML", "Copy SAML metadata URL": "Копировать адрес метаданных SAML",
"Copy prompt page URL": "Скопировать URL-адрес страницы запроса", "Copy prompt page URL": "Скопировать URL-адрес страницы запроса",
"Copy signin page URL": "Скопировать URL-адрес страницы входа", "Copy signin page URL": "Скопировать URL-адрес страницы входа",
@ -28,6 +29,7 @@
"Enable WebAuthn signin - Tooltip": "Включить вход с WebAuthn - Подсказка", "Enable WebAuthn signin - Tooltip": "Включить вход с WebAuthn - Подсказка",
"Enable code signin": "Включить кодовый вход", "Enable code signin": "Включить кодовый вход",
"Enable code signin - Tooltip": "Включить вход с кодом - Tooltip", "Enable code signin - Tooltip": "Включить вход с кодом - Tooltip",
"Enable side panel": "Enable side panel",
"Enable signin session - Tooltip": "Включить сеанс входа - Подсказка", "Enable signin session - Tooltip": "Включить сеанс входа - Подсказка",
"Enable signup": "Включить регистрацию", "Enable signup": "Включить регистрацию",
"Enable signup - Tooltip": "Whether to allow users to sign up", "Enable signup - Tooltip": "Whether to allow users to sign up",
@ -35,10 +37,11 @@
"Form CSS": "Form CSS", "Form CSS": "Form CSS",
"Form CSS - Edit": "Form CSS - Edit", "Form CSS - Edit": "Form CSS - Edit",
"Form CSS - Tooltip": "Form CSS - Tooltip", "Form CSS - Tooltip": "Form CSS - Tooltip",
"From position": "From position", "Form position": "Form position",
"From position - Tooltip": "From position - Tooltip", "Form position - Tooltip": "Form position - Tooltip",
"Grant types": "Виды грантов", "Grant types": "Виды грантов",
"Grant types - Tooltip": "Виды грантов - Подсказка", "Grant types - Tooltip": "Виды грантов - Подсказка",
"Left": "Left",
"New Application": "Новое приложение", "New Application": "Новое приложение",
"Password ON": "Пароль ВКЛ", "Password ON": "Пароль ВКЛ",
"Password ON - Tooltip": "Whether to allow password login", "Password ON - Tooltip": "Whether to allow password login",
@ -49,9 +52,13 @@
"Redirect URLs - Tooltip": "List of redirect addresses after successful login", "Redirect URLs - Tooltip": "List of redirect addresses after successful login",
"Refresh token expire": "Срок действия обновления токена истекает", "Refresh token expire": "Срок действия обновления токена истекает",
"Refresh token expire - Tooltip": "Срок обновления токена истекает - Подсказка", "Refresh token expire - Tooltip": "Срок обновления токена истекает - Подсказка",
"Right": "Right",
"SAML metadata": "Метаданные SAML", "SAML metadata": "Метаданные SAML",
"SAML metadata - Tooltip": "Метаданные SAML - Подсказка", "SAML metadata - Tooltip": "Метаданные SAML - Подсказка",
"SAML metadata URL copied to clipboard successfully": "Адрес метаданных SAML скопирован в буфер обмена", "SAML metadata URL copied to clipboard successfully": "Адрес метаданных SAML скопирован в буфер обмена",
"Side panel HTML": "Side panel HTML",
"Side panel HTML - Edit": "Side panel HTML - Edit",
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL страницы входа успешно скопирован в буфер обмена, пожалуйста, вставьте его в окно инкогнито или другой браузер", "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL страницы входа успешно скопирован в буфер обмена, пожалуйста, вставьте его в окно инкогнито или другой браузер",
"Signin session": "Сессия входа", "Signin session": "Сессия входа",
"Signup items": "Элементы регистрации", "Signup items": "Элементы регистрации",
@ -61,7 +68,9 @@
"Token expire - Tooltip": "Истек токен - Подсказка", "Token expire - Tooltip": "Истек токен - Подсказка",
"Token format": "Формат токена", "Token format": "Формат токена",
"Token format - Tooltip": "Формат токена - Подсказка", "Token format - Tooltip": "Формат токена - Подсказка",
"rule": "правило" "Rule": "правило",
"None": "None",
"Always": "Always"
}, },
"cert": { "cert": {
"Bit size": "Размер бита", "Bit size": "Размер бита",
@ -688,6 +697,7 @@
"Old Password": "Старый пароль", "Old Password": "Старый пароль",
"Password": "Пароль", "Password": "Пароль",
"Password Set": "Пароль установлен", "Password Set": "Пароль установлен",
"Please select avatar from resources": "Please select avatar from resources",
"Properties": "Свойства", "Properties": "Свойства",
"Re-enter New": "Введите еще раз", "Re-enter New": "Введите еще раз",
"Reset Email...": "Reset Email...", "Reset Email...": "Reset Email...",

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