Compare commits

...

45 Commits

Author SHA1 Message Date
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
19d351d157 feat: allow non-ASCII characters in username (#1235) 2022-10-22 20:46:50 +08:00
d0751bf2fa feat: add arm docker (#1236) 2022-10-22 11:08:29 +08:00
290cc60f00 feat: non root user for casdoor image (#1234)
Signed-off-by: abingcbc <abingcbc626@gmail.com>

Signed-off-by: abingcbc <abingcbc626@gmail.com>
2022-10-21 17:19:58 +08:00
6a1ec51978 feat: fix SSRF when download avatar (#1193) 2022-10-20 14:47:08 +08:00
dffa68cbce feat: fix SAML login error bug (#1228)
* Update LoginPage.js

* fix saml login error
2022-10-20 01:14:38 +08:00
fad209a7a3 Don't check username in UpdateUser() API 2022-10-19 22:50:19 +08:00
8b222ce2e3 Use Steam ID as username 2022-10-18 22:07:20 +08:00
c5293f428d fix: delete this accidentally added files (#1229)
* fix: delete this accidentally added files

* fix: ignore build result

* fix: remove unnecessary asterisk
2022-10-18 21:55:34 +08:00
146aec9ee8 feat: skip username restriction for new users coming from OAuth providers. (#1225) 2022-10-17 18:01:01 +08:00
50a52de856 feat: support database version control (#1221)
* feat: support Database version control

* Update adapter.go

* fix review problems

* Update adapter.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-15 17:20:20 +08:00
8f7a8d7d4f fix: translation without reloading (#1215)
* fix: translation without reloading

* fix: language switch
2022-10-12 19:52:02 +08:00
23f3fe1e3c feat: update code format (#1214)
* feat: doc

* feat: doc

* Update model.go

Co-authored-by: Gucheng <85475922+nomeguy@users.noreply.github.com>
2022-10-12 11:42:14 +08:00
59ff5e02ab fix: Add support for including underscores for username (#1210)
* fix: Add support for including underscores for username

* Update check.go

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-11 19:39:19 +08:00
8d41508d6b fix: center loading in account page (#1209)
* fix: center loading in account page

* Update UserEditPage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-11 00:52:08 +08:00
04f70cf012 Improve renderRightDropdown() 2022-10-10 22:53:47 +08:00
83724c73f9 feat: fix pad and mobile views (#1202)
* fix figure width

* fix: pad resolution menu

* feat: drawer style mobile menu

* fix: menu button i18n
2022-10-10 22:37:25 +08:00
33e419e133 Show more items to org admin 2022-10-10 21:58:17 +08:00
b832c304ae Can get owner in getObject() 2022-10-10 20:56:55 +08:00
4c7f6fda37 fix: Add restriction to username when signing up (#1203) 2022-10-10 19:58:02 +08:00
e4a54fe375 fix: disable roles inputbox when model doesn't support RBAC (#1201)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems

* fix: fix ldapserver crash bug

* Update ldapserver.go

* fix: fix dulpicate go routines

* fix gofumpt problems

* fix: fix UserList error

* feat:disable 'sub role' when model is incorrect

* feat:disable 'sub role' when model is incorrect

* feat:disable 'sub role' when model is incorrect

* delete useless output

* update func name

* Update PermissionEditPage.js

* Update PermissionEditPage.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-10-10 00:53:55 +08:00
87da3dad76 Remove useless file 2022-10-09 22:18:38 +08:00
44ad88353f Add error to GetDefaultApplication() 2022-10-09 10:39:33 +08:00
a955fb57d6 feat: fix UserList error (#1194)
* feat:Support simple ldap server

* fix:fix review problems

* fix:fix review problems

* fix: fix ldapserver crash bug

* Update ldapserver.go

* fix: fix dulpicate go routines

* fix gofumpt problems

* fix: fix UserList error
2022-10-08 20:00:45 +08:00
d2960ad66b Fix README typo 2022-10-08 16:00:08 +08:00
5243aabf43 docs: Create SECURITY.md (#1192) 2022-10-07 19:02:35 +08:00
d3a2c2a66e Improve org admin permissions 2022-10-07 16:27:21 +08:00
0a9058a585 Improve user list page 2022-10-07 15:43:50 +08:00
225719810b Update link typo in README 2022-10-06 19:37:00 +08:00
c634d4a891 feat: add some css style for the custom Provider button (#1185)
* fix: add some css style for the custom button

* fix: refactor previous code

* fix: add i18 adaptation

* fix: modifiy the saml codition
2022-10-06 19:28:02 +08:00
3dc01ec85d fix: language widget poisition without border css (#1188) 2022-10-06 17:26:12 +08:00
a7324f1da1 Improve className 2022-10-03 22:45:36 +08:00
118 changed files with 2939 additions and 578 deletions

0
$env
View File

View File

@ -125,26 +125,36 @@ jobs:
fi fi
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up buildx
id: buildx
uses: docker/setup-buildx-action@v2
with:
version: latest
- name: Log in to Docker Hub - name: Log in to Docker Hub
uses: docker/login-action@v1 uses: docker/login-action@v1
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' &&steps.should_push.outputs.push=='true' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with: with:
username: ${{ secrets.DOCKERHUB_USERNAME }} username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }} password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Push to Docker Hub - name: Push to Docker Hub
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with: with:
target: STANDARD target: STANDARD
platforms: linux/amd64,linux/arm64
push: true push: true
tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest tags: casbin/casdoor:${{steps.get-current-tag.outputs.tag }},casbin/casdoor:latest
- name: Push All In One Version to Docker Hub - name: Push All In One Version to Docker Hub
uses: docker/build-push-action@v2 uses: docker/build-push-action@v3
if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true' if: github.repository == 'casdoor/casdoor' && github.event_name == 'push' && steps.should_push.outputs.push=='true'
with: with:
target: ALLINONE target: ALLINONE
platforms: linux/amd64,linux/arm64
push: true push: true
tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest tags: casbin/casdoor-all-in-one:${{steps.get-current-tag.outputs.tag }},casbin/casdoor-all-in-one:latest

3
.gitignore vendored
View File

@ -27,3 +27,6 @@ logs/
files/ files/
lastupdate.tmp lastupdate.tmp
commentsRouter*.go commentsRouter*.go
# ignore build result
casdoor

View File

@ -2,7 +2,7 @@ FROM node:16.13.0 AS FRONT
WORKDIR /web WORKDIR /web
COPY ./web . COPY ./web .
RUN yarn config set registry https://registry.npmmirror.com RUN yarn config set registry https://registry.npmmirror.com
RUN yarn install && yarn run build RUN yarn install --frozen-lockfile --network-timeout 1000000 && yarn run build
FROM golang:1.17.5 AS BACK FROM golang:1.17.5 AS BACK
@ -13,16 +13,29 @@ 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 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 curl RUN apk add curl
RUN apk add ca-certificates && update-ca-certificates RUN apk add ca-certificates && update-ca-certificates
RUN adduser -D $USER -u 1000 \
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/$USER \
&& chmod 0440 /etc/sudoers.d/$USER \
&& mkdir logs \
&& chown -R $USER:$USER logs
USER 1000
WORKDIR / WORKDIR /
COPY --from=BACK /go/src/casdoor/server ./server COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/server_${BUILDX_ARCH} ./server
COPY --from=BACK /go/src/casdoor/swagger ./swagger COPY --from=BACK --chown=$USER:$USER /go/src/casdoor/swagger ./swagger
COPY --from=BACK /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 /web/build ./web/build COPY --from=FRONT --chown=$USER:$USER /web/build ./web/build
ENTRYPOINT ["/server"] ENTRYPOINT ["/server"]
@ -36,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

@ -51,7 +51,7 @@
## Documentation ## Documentation
- International: https://casdoor.org - International: https://casdoor.org
- Asian mirror: https://docs.casdoor.cn - Asian mirror: https://casdoor.cn
## Install ## Install
@ -69,7 +69,7 @@ https://casdoor.org/docs/how-to-connect/overview
## Integrations ## Integrations
https://casdoor.org/docs/integration/apisix https://casdoor.org/docs/category/integrations
## How to contact? ## How to contact?

9
SECURITY.md Normal file
View File

@ -0,0 +1,9 @@
# Security Policy
## Reporting a Vulnerability
We are grateful for security researchers and users reporting a vulnerability to us first. To ensure that your request is handled in a timely manner and we can keep users safe, please follow the below guidelines.
- **Please do not report security vulnerabilities directly on GitHub.**
- To report a vulnerability, please email [admin@casdoor.org](admin@casdoor.org).

View File

@ -15,12 +15,14 @@
package authz package authz
import ( import (
"fmt"
"strings" "strings"
"github.com/casbin/casbin/v2" "github.com/casbin/casbin/v2"
"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/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object"
stringadapter "github.com/qiangmzsx/string-adapter/v2" stringadapter "github.com/qiangmzsx/string-adapter/v2"
) )
@ -138,6 +140,12 @@ func IsAllowed(subOwner string, subName string, method string, urlPath string, o
} }
} }
userId := fmt.Sprintf("%s/%s", subOwner, subName)
user := object.GetUser(userId)
if user != nil && user.IsAdmin && subOwner == objOwner {
return true
}
res, err := Enforcer.Enforce(subOwner, subName, method, urlPath, objOwner, objName) res, err := Enforcer.Enforce(subOwner, subName, method, urlPath, objOwner, objName)
if err != nil { if err != nil {
panic(err) panic(err)

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)
}

BIN
casdoor

Binary file not shown.

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
} }
@ -309,7 +313,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

@ -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

@ -133,11 +133,12 @@ func (c *ApiController) GetDefaultApplication() {
userId := c.GetSessionUsername() userId := c.GetSessionUsername()
id := c.Input().Get("id") id := c.Input().Get("id")
application := object.GetMaskedApplication(object.GetDefaultApplication(id), userId) application, err := object.GetDefaultApplication(id)
if application == nil { if err != nil {
c.ResponseError("Please set a default application for this organization") c.ResponseError(err.Error())
return return
} }
c.ResponseOk(application) maskedApplication := object.GetMaskedApplication(application, userId)
c.ResponseOk(maskedApplication)
} }

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,6 +183,12 @@ func (c *ApiController) AddUser() {
return return
} }
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
if msg != "" {
c.ResponseError(msg)
return
}
c.Data["json"] = wrapActionResponse(object.AddUser(&user)) c.Data["json"] = wrapActionResponse(object.AddUser(&user))
c.ServeJSON() c.ServeJSON()
} }
@ -224,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
} }
@ -265,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
@ -274,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
@ -282,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
} }
@ -309,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,7 @@ 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.ResponseError(fmt.Sprintf(c.T("UserErr.DoNotExist"), userId))
return nil, false return nil, false
} }
return user, true return user, true
@ -112,7 +126,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 +139,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

@ -282,7 +282,7 @@ func getUser(gothUser goth.User, provider string) *UserInfo {
} }
} }
if provider == "steam" { if provider == "steam" {
user.Username = user.DisplayName user.Username = user.Id
user.Email = "" user.Email = ""
} }
return &user return &user

View File

@ -19,11 +19,13 @@ import (
"runtime" "runtime"
"github.com/beego/beego" "github.com/beego/beego"
xormadapter "github.com/casbin/xorm-adapter/v3"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
_ "github.com/denisenkom/go-mssqldb" // db = mssql _ "github.com/denisenkom/go-mssqldb" // db = mssql
_ "github.com/go-sql-driver/mysql" // db = mysql _ "github.com/go-sql-driver/mysql" // db = mysql
_ "github.com/lib/pq" // db = postgres _ "github.com/lib/pq" // db = postgres
"xorm.io/xorm/migrate"
//_ "github.com/mattn/go-sqlite3" // db = sqlite3 //_ "github.com/mattn/go-sqlite3" // db = sqlite3
"xorm.io/core" "xorm.io/core"
"xorm.io/xorm" "xorm.io/xorm"
@ -40,6 +42,7 @@ func InitConfig() {
beego.BConfig.WebConfig.Session.SessionOn = true beego.BConfig.WebConfig.Session.SessionOn = true
InitAdapter(true) InitAdapter(true)
initMigrations()
} }
func InitAdapter(createDatabase bool) { func InitAdapter(createDatabase bool) {
@ -214,6 +217,11 @@ func (a *Adapter) createTable() {
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = a.Engine.Sync2(new(xormadapter.CasbinRule))
if err != nil {
panic(err)
}
} }
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session { func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
@ -239,3 +247,22 @@ func GetSession(owner string, offset, limit int, field, value, sortField, sortOr
} }
return session return session
} }
func initMigrations() {
migrations := []*migrate.Migration{
{
ID: "20221015CasbinRule--fill ptype field with p",
Migrate: func(tx *xorm.Engine) error {
_, err := tx.Cols("ptype").Update(&xormadapter.CasbinRule{
Ptype: "p",
})
return err
},
Rollback: func(tx *xorm.Engine) error {
return tx.DropTables(&xormadapter.CasbinRule{})
},
},
}
m := migrate.New(adapter.Engine, migrate.DefaultOptions, migrations)
m.Migrate()
}

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"`
} }

View File

@ -50,7 +50,7 @@ func downloadFile(url string) (*bytes.Buffer, error) {
return fileBuffer, nil return fileBuffer, nil
} }
func getPermanentAvatarUrl(organization string, username string, url string) string { func getPermanentAvatarUrl(organization string, username string, url string, upload bool) string {
if url == "" { if url == "" {
return "" return ""
} }
@ -62,6 +62,14 @@ func getPermanentAvatarUrl(organization string, username string, url string) str
fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username) fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username)
uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false) uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false)
if upload {
DownloadAndUpload(url, fullFilePath)
}
return uploadedFileUrl
}
func DownloadAndUpload(url string, fullFilePath string) {
fileBuffer, err := downloadFile(url) fileBuffer, err := downloadFile(url)
if err != nil { if err != nil {
panic(err) panic(err)
@ -71,6 +79,4 @@ func getPermanentAvatarUrl(organization string, username string, url string) str
if err != nil { if err != nil {
panic(err) panic(err)
} }
return uploadedFileUrl
} }

View File

@ -32,7 +32,7 @@ func TestSyncPermanentAvatars(t *testing.T) {
continue continue
} }
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
updateUserColumn("permanent_avatar", user) updateUserColumn("permanent_avatar", user)
fmt.Printf("[%d/%d]: Update user: [%s]'s permanent avatar: %s\n", i, len(users), user.GetId(), user.PermanentAvatar) fmt.Printf("[%d/%d]: Update user: [%s]'s permanent avatar: %s\n", i, len(users), user.GetId(), user.PermanentAvatar)
} }

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

@ -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,84 +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, lang)
if 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")
} }
} }
} }
@ -126,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)
@ -141,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
@ -153,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)
@ -180,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 {
@ -204,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
@ -215,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
} }
@ -246,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
@ -266,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
@ -281,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) {
@ -313,3 +319,41 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
} }
return allowed, err return allowed, err
} }
func CheckUsername(username string, lang string) string {
if username == "" {
return i18n.Translate(lang, "UserErr.NameEmptyErr")
} else if len(username) > 39 {
return i18n.Translate(lang, "UserErr.NameTooLang")
}
exclude, _ := regexp.Compile("^[\u0021-\u007E]+$")
if !exclude.MatchString(username) {
return ""
}
// 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]+))*$")
if !re.MatchString(username) {
return i18n.Translate(lang, "UserErr.NameFormatErr")
}
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

@ -409,6 +409,7 @@ func SyncLdapUsers(owner string, users []LdapRespUser, ldapId string) (*[]LdapRe
} }
} }
} }
if !found && !AddUser(&User{ if !found && !AddUser(&User{
Owner: owner, Owner: owner,
Name: buildLdapUserName(user.Uid, user.UidNumber), Name: buildLdapUserName(user.Uid, user.UidNumber),

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

@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"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"
) )
@ -202,30 +203,30 @@ 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, ""
} }
func GetDefaultApplication(id string) *Application { func GetDefaultApplication(id string) (*Application, error) {
organization := GetOrganization(id) organization := GetOrganization(id)
if organization == nil { if organization == nil {
return nil return nil, fmt.Errorf("The organization: %s does not exist", id)
} }
if organization.DefaultApplication != "" { if organization.DefaultApplication != "" {
return getApplication("admin", organization.DefaultApplication) return getApplication("admin", organization.DefaultApplication), fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
} }
applications := []*Application{} applications := []*Application{}
@ -235,7 +236,7 @@ func GetDefaultApplication(id string) *Application {
} }
if len(applications) == 0 { if len(applications) == 0 {
return nil return nil, fmt.Errorf("The application does not exist")
} }
defaultApplication := applications[0] defaultApplication := applications[0]
@ -249,5 +250,5 @@ func GetDefaultApplication(id string) *Application {
extendApplicationWithProviders(defaultApplication) extendApplicationWithProviders(defaultApplication)
extendApplicationWithOrg(defaultApplication) extendApplicationWithOrg(defaultApplication)
return defaultApplication return defaultApplication, nil
} }

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"
@ -228,7 +229,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,26 +238,26 @@ 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

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

@ -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

@ -120,7 +120,7 @@ func (syncer *Syncer) updateUserForOriginalFields(user *User) (bool, error) {
} }
if user.Avatar != oldUser.Avatar && user.Avatar != "" { if user.Avatar != oldUser.Avatar && user.Avatar != "" {
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
} }
columns := syncer.getCasdoorColumns() columns := syncer.getCasdoorColumns()

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 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

@ -386,7 +386,7 @@ func UpdateUser(id string, user *User, columns []string, isGlobalAdmin bool) boo
user.UpdateUserHash() user.UpdateUserHash()
if user.Avatar != oldUser.Avatar && user.Avatar != "" && user.PermanentAvatar != "*" { if user.Avatar != oldUser.Avatar && user.Avatar != "" && user.PermanentAvatar != "*" {
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
} }
if len(columns) == 0 { if len(columns) == 0 {
@ -419,7 +419,7 @@ func UpdateUserForAllFields(id string, user *User) bool {
user.UpdateUserHash() user.UpdateUserHash()
if user.Avatar != oldUser.Avatar && user.Avatar != "" { if user.Avatar != oldUser.Avatar && user.Avatar != "" {
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
} }
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(user) affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(user)
@ -449,7 +449,7 @@ func AddUser(user *User) bool {
user.UpdateUserHash() user.UpdateUserHash()
user.PreHash = user.Hash user.PreHash = user.Hash
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
user.Ranking = GetUserCount(user.Owner, "", "") + 1 user.Ranking = GetUserCount(user.Owner, "", "") + 1
@ -474,7 +474,7 @@ func AddUsers(users []*User) bool {
user.UpdateUserHash() user.UpdateUserHash()
user.PreHash = user.Hash user.PreHash = user.Hash
user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar) user.PermanentAvatar = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, true)
} }
affected, err := adapter.Engine.Insert(users) affected, err := adapter.Engine.Insert(users)

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 {
@ -159,7 +160,7 @@ func DisableVerificationCode(dest string) {
} }
} }
// from Casnode/object/validateCode.go line 116 // From Casnode/object/validateCode.go line 116
var stdNums = []byte("0123456789") var stdNums = []byte("0123456789")
func getRandomCode(length int) string { func getRandomCode(length int) string {

View File

@ -63,11 +63,16 @@ func getObject(ctx *context.Context) (string, string) {
if method == http.MethodGet { if method == http.MethodGet {
// query == "?id=built-in/admin" // query == "?id=built-in/admin"
id := ctx.Input.Query("id") id := ctx.Input.Query("id")
if id == "" { if id != "" {
return "", "" return util.GetOwnerAndNameFromId(id)
} }
return util.GetOwnerAndNameFromId(id) owner := ctx.Input.Query("owner")
if owner != "" {
return owner, ""
}
return "", ""
} else { } else {
body := ctx.Input.RequestBody body := ctx.Input.RequestBody

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

@ -16,8 +16,8 @@ import React, {Component} from "react";
import "./App.less"; import "./App.less";
import {Helmet} from "react-helmet"; import {Helmet} from "react-helmet";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons"; import {BarsOutlined, DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
import {Avatar, BackTop, Button, Card, Dropdown, Layout, Menu, Result} from "antd"; import {Avatar, BackTop, Button, Card, Drawer, Dropdown, Layout, Menu, Result} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom"; import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import OrganizationListPage from "./OrganizationListPage"; import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage"; import OrganizationEditPage from "./OrganizationEditPage";
@ -74,6 +74,7 @@ import ModelEditPage from "./ModelEditPage";
import SystemInfo from "./SystemInfo"; import SystemInfo from "./SystemInfo";
import AdapterListPage from "./AdapterListPage"; import AdapterListPage from "./AdapterListPage";
import AdapterEditPage from "./AdapterEditPage"; import AdapterEditPage from "./AdapterEditPage";
import {withTranslation} from "react-i18next";
const {Header, Footer} = Layout; const {Header, Footer} = Layout;
@ -85,6 +86,7 @@ class App extends Component {
selectedMenuKey: 0, selectedMenuKey: 0,
account: undefined, account: undefined,
uri: null, uri: null,
menuVisible: false,
}; };
Setting.initServerUrl(); Setting.initServerUrl();
@ -219,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}`);
} }
} }
@ -298,12 +299,12 @@ class App extends Component {
<Menu onClick={this.handleRightDropdownClick.bind(this)}> <Menu onClick={this.handleRightDropdownClick.bind(this)}>
<Menu.Item key="/account"> <Menu.Item key="/account">
<SettingOutlined /> <SettingOutlined />
&nbsp; &nbsp;&nbsp;
{i18next.t("account:My Account")} {i18next.t("account:My Account")}
</Menu.Item> </Menu.Item>
<Menu.Item key="/logout"> <Menu.Item key="/logout">
<LogoutOutlined /> <LogoutOutlined />
&nbsp; &nbsp;&nbsp;
{i18next.t("account:Logout")} {i18next.t("account:Logout")}
</Menu.Item> </Menu.Item>
</Menu> </Menu>
@ -378,6 +379,9 @@ class App extends Component {
</Link> </Link>
</Menu.Item> </Menu.Item>
); );
}
if (Setting.isLocalAdminUser(this.state.account)) {
res.push( res.push(
<Menu.Item key="/users"> <Menu.Item key="/users">
<Link to="/users"> <Link to="/users">
@ -592,6 +596,18 @@ class App extends Component {
); );
} }
onClose = () => {
this.setState({
menuVisible: false,
});
};
showMenu = () => {
this.setState({
menuVisible: true,
});
};
renderContent() { renderContent() {
if (!Setting.isMobile()) { if (!Setting.isMobile()) {
return ( return (
@ -610,7 +626,7 @@ class App extends Component {
// theme="dark" // theme="dark"
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"} mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"}
selectedKeys={[`${this.state.selectedMenuKey}`]} selectedKeys={[`${this.state.selectedMenuKey}`]}
style={{lineHeight: "64px", width: "78%", position: "absolute", left: "145px"}} style={{lineHeight: "64px", position: "absolute", left: "145px", right: "200px"}}
> >
{ {
this.renderMenu() this.renderMenu()
@ -643,22 +659,28 @@ class App extends Component {
</Link> </Link>
) )
} }
<Menu <Drawer title={i18next.t("general:Close")} placement="left" visible={this.state.menuVisible} onClose={this.onClose}>
// theme="dark" <Menu
mode={(Setting.isMobile() && this.isStartPages()) ? "inline" : "horizontal"} // theme="dark"
selectedKeys={[`${this.state.selectedMenuKey}`]} mode={(Setting.isMobile()) ? "inline" : "horizontal"}
style={{lineHeight: "64px"}} selectedKeys={[`${this.state.selectedMenuKey}`]}
> style={{lineHeight: "64px"}}
{ onClick={this.onClose}
this.renderMenu() >
}
<div style = {{float: "right"}}>
{ {
this.renderAccount() this.renderMenu()
} }
<SelectLanguageBox /> </Menu>
</div> </Drawer>
</Menu> <Button icon={<BarsOutlined />} onClick={this.showMenu} type="text">
{i18next.t("general:Menu")}
</Button>
<div style = {{float: "right"}}>
{
this.renderAccount()
}
<SelectLanguageBox />
</div>
</Header> </Header>
{ {
this.renderRouter() this.renderRouter()
@ -776,4 +798,4 @@ class App extends Component {
} }
} }
export default withRouter(App); export default withRouter(withTranslation()(App));

View File

@ -65,7 +65,7 @@
right: 0; right: 0;
} }
.language_box { .language-box {
background: url("@{StaticBaseUrl}/img/muti_language.svg"); background: url("@{StaticBaseUrl}/img/muti_language.svg");
background-size: 25px, 25px; background-size: 25px, 25px;
background-position: center; background-position: center;
@ -94,7 +94,39 @@
align-items: stretch; align-items: stretch;
} }
.login-content{ .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 {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
@ -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

@ -12,19 +12,21 @@
// 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, {useState} from "react"; import React, {useEffect, useState} from "react";
import Cropper from "react-cropper"; import Cropper from "react-cropper";
import "cropperjs/dist/cropper.css"; import "cropperjs/dist/cropper.css";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {Button, Col, Modal, Row} from "antd"; import {Button, Col, Modal, Row, Select} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import * as ResourceBackend from "./backend/ResourceBackend"; import * as ResourceBackend from "./backend/ResourceBackend";
export const CropperDiv = (props) => { export const CropperDiv = (props) => {
const [loading, setLoading] = useState(true);
const [options, setOptions] = useState([]);
const [image, setImage] = useState(""); const [image, setImage] = useState("");
const [cropper, setCropper] = useState(); const [cropper, setCropper] = useState();
const [visible, setVisible] = React.useState(false); const [visible, setVisible] = useState(false);
const [confirmLoading, setConfirmLoading] = React.useState(false); const [confirmLoading, setConfirmLoading] = useState(false);
const {title} = props; const {title} = props;
const {user} = props; const {user} = props;
const {buttonText} = props; const {buttonText} = props;
@ -60,7 +62,7 @@ export const CropperDiv = (props) => {
ResourceBackend.uploadResource(user.owner, user.name, "avatar", "CropperDiv", fullFilePath, blob) ResourceBackend.uploadResource(user.owner, user.name, "avatar", "CropperDiv", fullFilePath, blob)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
window.location.href = "/account"; window.location.href = window.location.pathname;
} else { } else {
Setting.showMessage("error", res.msg); Setting.showMessage("error", res.msg);
} }
@ -88,6 +90,48 @@ export const CropperDiv = (props) => {
uploadButton.click(); uploadButton.click();
}; };
const getOptions = (data) => {
const options = [];
if (props.account.organization.defaultAvatar !== null) {
options.push({value: props.account.organization.defaultAvatar});
}
for (let i = 0; i < data.length; i++) {
if (data[i].fileType === "image") {
const url = `${data[i].url}`;
options.push({
value: url,
});
}
}
return options;
};
const getBase64Image = (src) => {
return new Promise((resolve) => {
const image = new Image();
image.src = src;
image.setAttribute("crossOrigin", "anonymous");
image.onload = () => {
const canvas = document.createElement("canvas");
canvas.width = image.width;
canvas.height = image.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(image, 0, 0, image.width, image.height);
const dataURL = canvas.toDataURL("image/png");
resolve(dataURL);
};
});
};
useEffect(() => {
setLoading(true);
ResourceBackend.getResources(props.account.owner, props.account.name, "", "", "", "", "", "")
.then((res) => {
setLoading(false);
setOptions(getOptions(res));
});
}, []);
return ( return (
<div> <div>
<Button type="default" onClick={showModal}> <Button type="default" onClick={showModal}>
@ -105,10 +149,20 @@ export const CropperDiv = (props) => {
[<Button block key="submit" type="primary" onClick={handleOk}>{i18next.t("user:Set new profile picture")}</Button>] [<Button block key="submit" type="primary" onClick={handleOk}>{i18next.t("user:Set new profile picture")}</Button>]
} }
> >
<Col style={{margin: "0px auto 40px auto", width: 1000, height: 300}}> <Col style={{margin: "0px auto 60px auto", width: 1000, height: 350}}>
<Row style={{width: "100%", marginBottom: "20px"}}> <Row style={{width: "100%", marginBottom: "20px"}}>
<input style={{display: "none"}} ref={input => uploadButton = input} type="file" accept="image/*" onChange={onChange} /> <input style={{display: "none"}} ref={input => uploadButton = input} type="file" accept="image/*" onChange={onChange} />
<Button block onClick={selectFile}>{i18next.t("user:Select a photo...")}</Button> <Button block onClick={selectFile}>{i18next.t("user:Select a photo...")}</Button>
<Select
style={{width: "100%"}}
loading={loading}
placeholder={i18next.t("user:Please select avatar from resources")}
onChange={(async value => {
setImage(await getBase64Image(value));
})}
options={options}
allowClear={true}
/>
</Row> </Row>
<Cropper <Cropper
style={{height: "100%"}} style={{height: "100%"}}

View File

@ -35,6 +35,7 @@ class PermissionEditPage extends React.Component {
permissionName: props.match.params.permissionName, permissionName: props.match.params.permissionName,
permission: null, permission: null,
organizations: [], organizations: [],
model: null,
users: [], users: [],
roles: [], roles: [],
models: [], models: [],
@ -59,6 +60,7 @@ class PermissionEditPage extends React.Component {
this.getRoles(permission.owner); this.getRoles(permission.owner);
this.getModels(permission.owner); this.getModels(permission.owner);
this.getResources(permission.owner); this.getResources(permission.owner);
this.getModel(permission.owner, permission.model);
}); });
} }
@ -98,6 +100,15 @@ class PermissionEditPage extends React.Component {
}); });
} }
getModel(organizationName, modelName) {
ModelBackend.getModel(organizationName, modelName)
.then((res) => {
this.setState({
model: res,
});
});
}
getResources(organizationName) { getResources(organizationName) {
ApplicationBackend.getApplicationsByOrganization("admin", organizationName) ApplicationBackend.getApplicationsByOrganization("admin", organizationName)
.then((res) => { .then((res) => {
@ -115,6 +126,10 @@ class PermissionEditPage extends React.Component {
} }
updatePermissionField(key, value) { updatePermissionField(key, value) {
if (key === "model") {
this.getModel(this.state.permission.owner, value);
}
value = this.parsePermissionField(key, value); value = this.parsePermissionField(key, value);
const permission = this.state.permission; const permission = this.state.permission;
@ -124,6 +139,13 @@ class PermissionEditPage extends React.Component {
}); });
} }
hasRoleDefinition(model) {
if (model !== null) {
return model.modelText.includes("role_definition");
}
return false;
}
renderPermission() { renderPermission() {
return ( return (
<Card size="small" title={ <Card size="small" title={
@ -214,7 +236,7 @@ class PermissionEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub roles"), i18next.t("role:Sub roles - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.roles} onChange={(value => {this.updatePermissionField("roles", value);})}> <Select virtual={false} disabled={!this.hasRoleDefinition(this.state.model)} mode="tags" style={{width: "100%"}} value={this.state.permission.roles} onChange={(value => {this.updatePermissionField("roles", value);})}>
{ {
this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission, index) => <Option key={index} value={`${permission.owner}/${permission.name}`}>{`${permission.owner}/${permission.name}`}</Option>) this.state.roles.filter(roles => (roles.owner !== this.state.roles.owner || roles.name !== this.state.roles.name)).map((permission, index) => <Option key={index} value={`${permission.owner}/${permission.name}`}>{`${permission.owner}/${permission.name}`}</Option>)
} }

View File

@ -341,7 +341,7 @@ class PermissionListPage extends BaseListPage {
this.setState({loading: true}); this.setState({loading: true});
const getPermissions = Setting.isLocalAdminUser(this.props.account) ? PermissionBackend.getPermissions : PermissionBackend.getPermissionsBySubmitter; const getPermissions = Setting.isLocalAdminUser(this.props.account) ? PermissionBackend.getPermissions : PermissionBackend.getPermissionsBySubmitter;
getPermissions("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) getPermissions(Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({

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

@ -25,7 +25,7 @@ class RoleListPage extends BaseListPage {
newRole() { newRole() {
const randomName = Setting.getRandomName(); const randomName = Setting.getRandomName();
return { return {
owner: "built-in", owner: this.props.account.owner,
name: `role_${randomName}`, name: `role_${randomName}`,
createdTime: moment().format(), createdTime: moment().format(),
displayName: `New Role - ${randomName}`, displayName: `New Role - ${randomName}`,
@ -211,7 +211,7 @@ class RoleListPage extends BaseListPage {
value = params.type; value = params.type;
} }
this.setState({loading: true}); this.setState({loading: true});
RoleBackend.getRoles("", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) RoleBackend.getRoles(Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({

View File

@ -49,7 +49,7 @@ class SelectLanguageBox extends React.Component {
return ( return (
<Dropdown overlay={menu} > <Dropdown overlay={menu} >
<div className="language_box" id={this.props.id} style={this.props.style} /> <div className="language-box" id={this.props.id} style={this.props.style} />
</Dropdown> </Dropdown>
); );
} }

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 = "";
@ -554,7 +556,11 @@ export function changeLanguage(language) {
localStorage.setItem("language", language); localStorage.setItem("language", language);
changeMomentLanguage(language); changeMomentLanguage(language);
i18next.changeLanguage(language); i18next.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) {
@ -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

@ -17,7 +17,6 @@ import {Button, Card, Col, Input, Result, Row, Select, Spin, Switch} from "antd"
import * as UserBackend from "./backend/UserBackend"; import * as UserBackend from "./backend/UserBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {LinkOutlined} from "@ant-design/icons";
import i18next from "i18next"; import i18next from "i18next";
import CropperDiv from "./CropperDiv.js"; import CropperDiv from "./CropperDiv.js";
import * as ApplicationBackend from "./backend/ApplicationBackend"; import * as ApplicationBackend from "./backend/ApplicationBackend";
@ -232,16 +231,6 @@ class UserEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Avatar"), i18next.t("general:Avatar - Tooltip"))} : {Setting.getLabel(i18next.t("general:Avatar"), i18next.t("general:Avatar - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("general:URL")}:
</Col>
<Col span={22} >
<Input prefix={<LinkOutlined />} value={this.state.user.avatar} onChange={e => {
this.updateUserField("avatar", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{i18next.t("general:Preview")}: {i18next.t("general:Preview")}:
@ -661,7 +650,7 @@ class UserEditPage extends React.Component {
return ( return (
<div> <div>
{ {
this.state.loading ? <Spin size="large" /> : ( this.state.loading ? <Spin size="large" style={{marginLeft: "50%", marginTop: "10%"}} /> : (
this.state.user !== null ? this.renderUser() : this.state.user !== null ? this.renderUser() :
<Result <Result
status="404" status="404"

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;
@ -374,7 +374,7 @@ class UserListPage extends BaseListPage {
const sortField = params.sortField, sortOrder = params.sortOrder; const sortField = params.sortField, sortOrder = params.sortOrder;
this.setState({loading: true}); this.setState({loading: true});
if (this.state.organizationName === undefined) { if (this.state.organizationName === undefined) {
UserBackend.getGlobalUsers(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) (Setting.isAdminUser(this.props.account) ? UserBackend.getGlobalUsers(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) : UserBackend.getUsers(this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({

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";
@ -29,6 +28,8 @@ import i18next from "i18next";
import CustomGithubCorner from "../CustomGithubCorner"; 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 {CaptchaModal} from "../common/CaptchaModal";
const {TabPane} = Tabs; const {TabPane} = Tabs;
@ -48,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) {
@ -68,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)
@ -138,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 !== "") {
@ -155,8 +183,8 @@ class LoginPage extends React.Component {
values["type"] = "saml"; values["type"] = "saml";
} }
if (this.state.owner !== null && this.state.owner !== undefined) { if (this.state.application.organization !== null && this.state.application.organization !== undefined) {
values["organization"] = this.state.owner; values["organization"] = this.state.application.organization;
} }
} }
postCodeLoginAction(res) { postCodeLoginAction(res) {
@ -213,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
@ -227,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);
@ -234,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}`);
} }
}); });
@ -246,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");
@ -263,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}`);
} }
}); });
@ -289,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>
@ -392,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
@ -410,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)
} }
@ -452,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 {
@ -472,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>
</> </>
) )
} }
@ -694,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: "80px", right: "5px"}} /> <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)
@ -723,11 +809,11 @@ class LoginPage extends React.Component {
</div> </div>
</div> </div>
</div> </div>
</Col> </div>
</Row> </div>
</div> </div>
); );
} }
} }
export default LoginPage; export default withTranslation()(LoginPage);

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

@ -129,6 +129,32 @@ export function renderProviderLogo(provider, application, width, margin, size, l
); );
} }
} else if (provider.type === "Custom") {
// style definition
const text = i18next.t("login:Sign in with {type}").replace("{type}", provider.displayName);
const customAStyle = {display: "block", height: "55px", color: "#000"};
const customButtonStyle = {display: "flex", alignItems: "center", width: "calc(100% - 10px)", height: "50px", margin: "5px", padding: "0 10px", backgroundColor: "transparent", boxShadow: "0px 1px 3px rgba(0,0,0,0.5)", border: "0px", borderRadius: "3px", cursor: "pointer"};
const customImgStyle = {justfyContent: "space-between"};
const customSpanStyle = {textAlign: "center", lineHeight: "50px", width: "100%", fontSize: "19px"};
if (provider.category === "OAuth") {
return (
<a key={provider.displayName} href={Provider.getAuthUrl(application, provider, "signup")} style={customAStyle}>
<button style={customButtonStyle}>
<img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} style={customImgStyle} />
<span style={customSpanStyle}>{text}</span>
</button>
</a>
);
} else if (provider.category === "SAML") {
return (
<a key={provider.displayName} onClick={() => getSamlUrl(provider, location)} style={customAStyle}>
<button style={customButtonStyle}>
<img width={26} src={getProviderLogoURL(provider)} alt={provider.displayName} style={customImgStyle} />
<span style={customSpanStyle}>{text}</span>
</button>
</a>
);
}
} else { } else {
return ( return (
<div key={provider.displayName} style={{marginBottom: "10px"}}> <div key={provider.displayName} style={{marginBottom: "10px"}}>

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: "3px", right: "5px"}} /> <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());
} }

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