mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-16 00:53:49 +08:00
Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
9b33800b4c | |||
ec98785172 | |||
45dd4cc344 | |||
1adb172d6b | |||
c08f2b1f3f | |||
62bb257c6d | |||
230a77e3e3 | |||
dce0a96dea | |||
65563fa0cd | |||
f2a94f671a | |||
1460a0498f | |||
adc63ea726 | |||
0b8be016c5 | |||
986dcbbda1 | |||
7d3920fb1f | |||
b794ef87ee | |||
a0d6f2125e | |||
85cbb7d074 | |||
fdc1be9452 | |||
2bd7dabd33 | |||
9b9a58e7ac | |||
38e389e8c8 | |||
ab5fcf848e | |||
b4e51b4631 | |||
45e25acc80 | |||
97dcf24a91 | |||
4c0fff66ff | |||
e7230700e0 | |||
f21aa9c0d2 | |||
4b2b875b2d | |||
df2a5681cc | |||
ac102480c7 | |||
feff47d2dc | |||
79b934d6c2 | |||
365449695b | |||
55a52093e8 | |||
e65fdeb1e0 | |||
a46c1cc775 | |||
5629343466 | |||
3718d2dc04 | |||
38b9ad1d9f | |||
5a92411006 | |||
52eaf6c822 | |||
cc84709151 | |||
22fca78be9 | |||
effd257040 | |||
a38747d90e | |||
da70682cd1 | |||
4a3bd84f84 | |||
7f2869cecb | |||
cef2ab213b | |||
cc979c310e | |||
13d73732ce | |||
5686fe5d22 | |||
d8cb82f67a | |||
cad2e1bcc3 |
6
.github/workflows/build.yml
vendored
6
.github/workflows/build.yml
vendored
@ -35,7 +35,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: ./web/yarn.lock
|
cache-dependency-path: ./web/yarn.lock
|
||||||
- run: yarn install && CI=false yarn run build
|
- run: yarn install && CI=false yarn run build
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
working-directory: ./
|
working-directory: ./
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
cache: 'yarn'
|
cache: 'yarn'
|
||||||
cache-dependency-path: ./web/yarn.lock
|
cache-dependency-path: ./web/yarn.lock
|
||||||
- run: yarn install
|
- run: yarn install
|
||||||
@ -138,7 +138,7 @@ jobs:
|
|||||||
- name: Setup Node.js
|
- name: Setup Node.js
|
||||||
uses: actions/setup-node@v3
|
uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 20
|
||||||
|
|
||||||
- name: Fetch Previous version
|
- name: Fetch Previous version
|
||||||
id: get-previous-tag
|
id: get-previous-tag
|
||||||
|
@ -21,6 +21,7 @@ originFrontend =
|
|||||||
staticBaseUrl = "https://cdn.casbin.org"
|
staticBaseUrl = "https://cdn.casbin.org"
|
||||||
isDemoMode = false
|
isDemoMode = false
|
||||||
batchSize = 100
|
batchSize = 100
|
||||||
|
enableErrorMask = false
|
||||||
enableGzip = true
|
enableGzip = true
|
||||||
ldapServerPort = 389
|
ldapServerPort = 389
|
||||||
radiusServerPort = 1812
|
radiusServerPort = 1812
|
||||||
|
@ -665,6 +665,11 @@ func (c *ApiController) Login() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.IsSignupItemRequired("Invitation code") {
|
||||||
|
c.ResponseError(c.T("check:Invitation code cannot be blank"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Handle username conflicts
|
// Handle username conflicts
|
||||||
var tmpUser *object.User
|
var tmpUser *object.User
|
||||||
tmpUser, err = object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
tmpUser, err = object.GetUser(util.GetId(application.Organization, userInfo.Username))
|
||||||
|
@ -16,6 +16,7 @@ package controllers
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/beego/beego/utils/pagination"
|
"github.com/beego/beego/utils/pagination"
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
@ -163,11 +164,17 @@ func (c *ApiController) GetPolicies() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if adapter == nil {
|
||||||
|
c.ResponseError(fmt.Sprintf(c.T("the adapter: %s is not found"), adapterId))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
err = adapter.InitAdapter()
|
err = adapter.InitAdapter()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk()
|
c.ResponseOk()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,6 @@ func (c *ApiController) Unlink() {
|
|||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if application == nil {
|
if application == nil {
|
||||||
c.ResponseError(c.T("link:You can't unlink yourself, you are not a member of any application"))
|
c.ResponseError(c.T("link:You can't unlink yourself, you are not a member of any application"))
|
||||||
return
|
return
|
||||||
|
@ -257,7 +257,7 @@ func (c *ApiController) UploadResource() {
|
|||||||
fileType, _ = util.GetOwnerAndNameFromIdNoCheck(mimeType + "/")
|
fileType, _ = util.GetOwnerAndNameFromIdNoCheck(mimeType + "/")
|
||||||
}
|
}
|
||||||
|
|
||||||
fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 175)
|
fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 450)
|
||||||
if tag != "avatar" && tag != "termsOfUse" && !strings.HasPrefix(tag, "idCard") {
|
if tag != "avatar" && tag != "termsOfUse" && !strings.HasPrefix(tag, "idCard") {
|
||||||
ext := filepath.Ext(filepath.Base(fullFilePath))
|
ext := filepath.Ext(filepath.Base(fullFilePath))
|
||||||
index := len(fullFilePath) - len(ext)
|
index := len(fullFilePath) - len(ext)
|
||||||
|
@ -46,10 +46,10 @@ func (c *ApiController) GetSystemInfo() {
|
|||||||
// @Success 200 {object} util.VersionInfo The Response object
|
// @Success 200 {object} util.VersionInfo The Response object
|
||||||
// @router /get-version-info [get]
|
// @router /get-version-info [get]
|
||||||
func (c *ApiController) GetVersionInfo() {
|
func (c *ApiController) GetVersionInfo() {
|
||||||
|
errInfo := ""
|
||||||
versionInfo, err := util.GetVersionInfo()
|
versionInfo, err := util.GetVersionInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
errInfo = "Git error: " + err.Error()
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if versionInfo.Version != "" {
|
if versionInfo.Version != "" {
|
||||||
@ -59,9 +59,11 @@ func (c *ApiController) GetVersionInfo() {
|
|||||||
|
|
||||||
versionInfo, err = util.GetVersionInfoFromFile()
|
versionInfo, err = util.GetVersionInfoFromFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
errInfo = errInfo + ", File error: " + err.Error()
|
||||||
|
c.ResponseError(errInfo)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(versionInfo)
|
c.ResponseOk(versionInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -333,6 +333,35 @@ func (c *ApiController) IntrospectToken() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.TokenFormat == "JWT-Standard" {
|
||||||
|
jwtToken, err := object.ParseStandardJwtTokenByApplication(tokenValue, application)
|
||||||
|
if err != nil || jwtToken.Valid() != nil {
|
||||||
|
// and token revoked case. but we not implement
|
||||||
|
// TODO: 2022-03-03 add token revoked check, when we implemented the Token Revocation(rfc7009) Specs.
|
||||||
|
// refs: https://tools.ietf.org/html/rfc7009
|
||||||
|
c.Data["json"] = &object.IntrospectionResponse{Active: false}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = &object.IntrospectionResponse{
|
||||||
|
Active: true,
|
||||||
|
Scope: jwtToken.Scope,
|
||||||
|
ClientId: clientId,
|
||||||
|
Username: token.User,
|
||||||
|
TokenType: token.TokenType,
|
||||||
|
Exp: jwtToken.ExpiresAt.Unix(),
|
||||||
|
Iat: jwtToken.IssuedAt.Unix(),
|
||||||
|
Nbf: jwtToken.NotBefore.Unix(),
|
||||||
|
Sub: jwtToken.Subject,
|
||||||
|
Aud: jwtToken.Audience,
|
||||||
|
Iss: jwtToken.Issuer,
|
||||||
|
Jti: jwtToken.ID,
|
||||||
|
}
|
||||||
|
c.ServeJSON()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
jwtToken, err := object.ParseJwtTokenByApplication(tokenValue, application)
|
jwtToken, err := object.ParseJwtTokenByApplication(tokenValue, application)
|
||||||
if err != nil || jwtToken.Valid() != nil {
|
if err != nil || jwtToken.Valid() != nil {
|
||||||
// and token revoked case. but we not implement
|
// and token revoked case. but we not implement
|
||||||
|
@ -289,6 +289,16 @@ func (c *ApiController) UpdateUser() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.MfaEmailEnabled && user.Email == "" {
|
||||||
|
c.ResponseError(c.T("user:MFA email is enabled but email is empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if user.MfaPhoneEnabled && user.Phone == "" {
|
||||||
|
c.ResponseError(c.T("user:MFA phone is enabled but phone number is empty"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
|
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
|
||||||
c.ResponseError(msg)
|
c.ResponseError(msg)
|
||||||
return
|
return
|
||||||
|
@ -45,6 +45,13 @@ func (c *ApiController) ResponseOk(data ...interface{}) {
|
|||||||
|
|
||||||
// ResponseError ...
|
// ResponseError ...
|
||||||
func (c *ApiController) ResponseError(error string, data ...interface{}) {
|
func (c *ApiController) ResponseError(error string, data ...interface{}) {
|
||||||
|
enableErrorMask := conf.GetConfigBool("enableErrorMask")
|
||||||
|
if enableErrorMask {
|
||||||
|
if strings.HasPrefix(error, "The user: ") && strings.HasSuffix(error, " doesn't exist") || strings.HasPrefix(error, "用户: ") && strings.HasSuffix(error, "不存在") {
|
||||||
|
error = c.T("check:password or code is incorrect")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp := &Response{Status: "error", Msg: error}
|
resp := &Response{Status: "error", Msg: error}
|
||||||
c.ResponseJsonData(resp, data...)
|
c.ResponseJsonData(resp, data...)
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,18 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func deployStaticFiles(provider *object.Provider) {
|
func deployStaticFiles(provider *object.Provider) {
|
||||||
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
|
certificate := ""
|
||||||
|
if provider.Category == "Storage" && provider.Type == "Casdoor" {
|
||||||
|
cert, err := object.GetCert(util.GetId(provider.Owner, provider.Cert))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
certificate = cert.Certificate
|
||||||
|
}
|
||||||
|
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint, certificate, provider.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
4
go.mod
4
go.mod
@ -12,7 +12,7 @@ require (
|
|||||||
github.com/casdoor/go-sms-sender v0.24.0
|
github.com/casdoor/go-sms-sender v0.24.0
|
||||||
github.com/casdoor/gomail/v2 v2.0.1
|
github.com/casdoor/gomail/v2 v2.0.1
|
||||||
github.com/casdoor/notify v0.45.0
|
github.com/casdoor/notify v0.45.0
|
||||||
github.com/casdoor/oss v1.6.0
|
github.com/casdoor/oss v1.8.0
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
github.com/casdoor/xorm-adapter/v3 v3.1.0
|
||||||
github.com/casvisor/casvisor-go-sdk v1.4.0
|
github.com/casvisor/casvisor-go-sdk v1.4.0
|
||||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||||
@ -30,7 +30,7 @@ require (
|
|||||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||||
github.com/go-webauthn/webauthn v0.6.0
|
github.com/go-webauthn/webauthn v0.6.0
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/google/uuid v1.4.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
github.com/lestrrat-go/jwx v1.2.29
|
github.com/lestrrat-go/jwx v1.2.29
|
||||||
github.com/lib/pq v1.10.9
|
github.com/lib/pq v1.10.9
|
||||||
|
9
go.sum
9
go.sum
@ -1083,6 +1083,8 @@ github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRt
|
|||||||
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||||
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
|
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
|
||||||
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
|
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
|
||||||
|
github.com/casdoor/casdoor-go-sdk v0.50.0 h1:bUYbz/MzJuWfLKJbJM0+U0YpYewAur+THp5TKnufWZM=
|
||||||
|
github.com/casdoor/casdoor-go-sdk v0.50.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
|
||||||
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
|
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
|
||||||
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
|
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
|
||||||
github.com/casdoor/go-sms-sender v0.24.0 h1:LNLsce3EG/87I3JS6UiajF3LlQmdIiCgebEu0IE4wSM=
|
github.com/casdoor/go-sms-sender v0.24.0 h1:LNLsce3EG/87I3JS6UiajF3LlQmdIiCgebEu0IE4wSM=
|
||||||
@ -1091,8 +1093,8 @@ github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR
|
|||||||
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
|
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
|
||||||
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
|
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
|
||||||
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
|
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
|
||||||
github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
|
github.com/casdoor/oss v1.8.0 h1:uuyKhDIp7ydOtV4lpqhAY23Ban2Ln8La8+QT36CwylM=
|
||||||
github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
|
github.com/casdoor/oss v1.8.0/go.mod h1:uaqO7KBI2lnZcnB8rF7O6C2bN7llIbfC5Ql8ex1yR1U=
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
|
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
|
||||||
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
|
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
|
||||||
github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=
|
github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=
|
||||||
@ -1460,8 +1462,9 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
|
|||||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
|
|
||||||
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
|
||||||
|
@ -45,6 +45,8 @@ func TestGenerateI18nFrontend(t *testing.T) {
|
|||||||
applyToOtherLanguage("frontend", "uk", data)
|
applyToOtherLanguage("frontend", "uk", data)
|
||||||
applyToOtherLanguage("frontend", "kk", data)
|
applyToOtherLanguage("frontend", "kk", data)
|
||||||
applyToOtherLanguage("frontend", "fa", data)
|
applyToOtherLanguage("frontend", "fa", data)
|
||||||
|
applyToOtherLanguage("frontend", "cs", data)
|
||||||
|
applyToOtherLanguage("frontend", "sk", data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateI18nBackend(t *testing.T) {
|
func TestGenerateI18nBackend(t *testing.T) {
|
||||||
@ -73,4 +75,6 @@ func TestGenerateI18nBackend(t *testing.T) {
|
|||||||
applyToOtherLanguage("backend", "uk", data)
|
applyToOtherLanguage("backend", "uk", data)
|
||||||
applyToOtherLanguage("backend", "kk", data)
|
applyToOtherLanguage("backend", "kk", data)
|
||||||
applyToOtherLanguage("backend", "fa", data)
|
applyToOtherLanguage("backend", "fa", data)
|
||||||
|
applyToOtherLanguage("backend", "cs", data)
|
||||||
|
applyToOtherLanguage("backend", "sk", data)
|
||||||
}
|
}
|
||||||
|
167
i18n/locales/cs/data.json
Normal file
167
i18n/locales/cs/data.json
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"Failed to add user": "Nepodařilo se přidat uživatele",
|
||||||
|
"Get init score failed, error: %w": "Nepodařilo se získat počáteční skóre, chyba: %w",
|
||||||
|
"Please sign out first": "Nejprve se prosím odhlaste",
|
||||||
|
"The application does not allow to sign up new account": "Aplikace neumožňuje registraci nového účtu"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"Challenge method should be S256": "Metoda výzvy by měla být S256",
|
||||||
|
"Failed to create user, user information is invalid: %s": "Nepodařilo se vytvořit uživatele, informace o uživateli jsou neplatné: %s",
|
||||||
|
"Failed to login in: %s": "Nepodařilo se přihlásit: %s",
|
||||||
|
"Invalid token": "Neplatný token",
|
||||||
|
"State expected: %s, but got: %s": "Očekávaný stav: %s, ale získán: %s",
|
||||||
|
"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": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet přes %%s, prosím použijte jiný způsob registrace",
|
||||||
|
"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": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) neexistuje a není povoleno se registrovat jako nový účet, prosím kontaktujte svou IT podporu",
|
||||||
|
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pro poskytovatele: %s a uživatelské jméno: %s (%s) je již propojen s jiným účtem: %s (%s)",
|
||||||
|
"The application: %s does not exist": "Aplikace: %s neexistuje",
|
||||||
|
"The login method: login with LDAP is not enabled for the application": "Metoda přihlášení: přihlášení pomocí LDAP není pro aplikaci povolena",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "Metoda přihlášení: přihlášení pomocí SMS není pro aplikaci povolena",
|
||||||
|
"The login method: login with email is not enabled for the application": "Metoda přihlášení: přihlášení pomocí emailu není pro aplikaci povolena",
|
||||||
|
"The login method: login with face is not enabled for the application": "Metoda přihlášení: přihlášení pomocí obličeje není pro aplikaci povolena",
|
||||||
|
"The login method: login with password is not enabled for the application": "Metoda přihlášení: přihlášení pomocí hesla není pro aplikaci povolena",
|
||||||
|
"The organization: %s does not exist": "Organizace: %s neexistuje",
|
||||||
|
"The provider: %s is not enabled for the application": "Poskytovatel: %s není pro aplikaci povolen",
|
||||||
|
"Unauthorized operation": "Neoprávněná operace",
|
||||||
|
"Unknown authentication type (not password or provider), form = %s": "Neznámý typ autentizace (není heslo nebo poskytovatel), formulář = %s",
|
||||||
|
"User's tag: %s is not listed in the application's tags": "Štítek uživatele: %s není uveden v štítcích aplikace",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "Placený uživatel %s nemá aktivní nebo čekající předplatné a aplikace: %s nemá výchozí ceny"
|
||||||
|
},
|
||||||
|
"cas": {
|
||||||
|
"Service %s and %s do not match": "Služba %s a %s se neshodují"
|
||||||
|
},
|
||||||
|
"check": {
|
||||||
|
"Affiliation cannot be blank": "Příslušnost nemůže být prázdná",
|
||||||
|
"Default code does not match the code's matching rules": "Výchozí kód neodpovídá pravidlům pro shodu kódů",
|
||||||
|
"DisplayName cannot be blank": "Zobrazované jméno nemůže být prázdné",
|
||||||
|
"DisplayName is not valid real name": "Zobrazované jméno není platné skutečné jméno",
|
||||||
|
"Email already exists": "Email již existuje",
|
||||||
|
"Email cannot be empty": "Email nemůže být prázdný",
|
||||||
|
"Email is invalid": "Email je neplatný",
|
||||||
|
"Empty username.": "Prázdné uživatelské jméno.",
|
||||||
|
"Face data does not exist, cannot log in": "Data obličeje neexistují, nelze se přihlásit",
|
||||||
|
"Face data mismatch": "Neshoda dat obličeje",
|
||||||
|
"FirstName cannot be blank": "Křestní jméno nemůže být prázdné",
|
||||||
|
"Invitation code cannot be blank": "Pozvánkový kód nemůže být prázdný",
|
||||||
|
"Invitation code exhausted": "Pozvánkový kód vyčerpán",
|
||||||
|
"Invitation code is invalid": "Pozvánkový kód je neplatný",
|
||||||
|
"Invitation code suspended": "Pozvánkový kód pozastaven",
|
||||||
|
"LDAP user name or password incorrect": "Uživatelské jméno nebo heslo LDAP je nesprávné",
|
||||||
|
"LastName cannot be blank": "Příjmení nemůže být prázdné",
|
||||||
|
"Multiple accounts with same uid, please check your ldap server": "Více účtů se stejným uid, prosím zkontrolujte svůj ldap server",
|
||||||
|
"Organization does not exist": "Organizace neexistuje",
|
||||||
|
"Phone already exists": "Telefon již existuje",
|
||||||
|
"Phone cannot be empty": "Telefon nemůže být prázdný",
|
||||||
|
"Phone number is invalid": "Telefonní číslo je neplatné",
|
||||||
|
"Please register using the email corresponding to the invitation code": "Prosím zaregistrujte se pomocí emailu odpovídajícího pozvánkovému kódu",
|
||||||
|
"Please register using the phone corresponding to the invitation code": "Prosím zaregistrujte se pomocí telefonu odpovídajícího pozvánkovému kódu",
|
||||||
|
"Please register using the username corresponding to the invitation code": "Prosím zaregistrujte se pomocí uživatelského jména odpovídajícího pozvánkovému kódu",
|
||||||
|
"Session outdated, please login again": "Relace je zastaralá, prosím přihlaste se znovu",
|
||||||
|
"The invitation code has already been used": "Pozvánkový kód již byl použit",
|
||||||
|
"The user is forbidden to sign in, please contact the administrator": "Uživatel má zakázáno se přihlásit, prosím kontaktujte administrátora",
|
||||||
|
"The user: %s doesn't exist in LDAP server": "Uživatel: %s neexistuje na LDAP serveru",
|
||||||
|
"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.": "Uživatelské jméno může obsahovat pouze alfanumerické znaky, podtržítka nebo pomlčky, nemůže mít po sobě jdoucí pomlčky nebo podtržítka a nemůže začínat nebo končit pomlčkou nebo podtržítkem.",
|
||||||
|
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Hodnota \\\"%s\\\" pro pole účtu \\\"%s\\\" neodpovídá regulárnímu výrazu položky účtu",
|
||||||
|
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Hodnota \\\"%s\\\" pro pole registrace \\\"%s\\\" neodpovídá regulárnímu výrazu položky registrace aplikace \\\"%s\\\"",
|
||||||
|
"Username already exists": "Uživatelské jméno již existuje",
|
||||||
|
"Username cannot be an email address": "Uživatelské jméno nemůže být emailová adresa",
|
||||||
|
"Username cannot contain white spaces": "Uživatelské jméno nemůže obsahovat mezery",
|
||||||
|
"Username cannot start with a digit": "Uživatelské jméno nemůže začínat číslicí",
|
||||||
|
"Username is too long (maximum is 39 characters).": "Uživatelské jméno je příliš dlouhé (maximálně 39 znaků).",
|
||||||
|
"Username must have at least 2 characters": "Uživatelské jméno musí mít alespoň 2 znaky",
|
||||||
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali jste špatné heslo nebo kód příliš mnohokrát, prosím počkejte %d minut a zkuste to znovu",
|
||||||
|
"Your region is not allow to signup by phone": "Vaše oblast neumožňuje registraci pomocí telefonu",
|
||||||
|
"password or code is incorrect": "heslo nebo kód je nesprávné",
|
||||||
|
"password or code is incorrect, you have %d remaining chances": "heslo nebo kód je nesprávné, máte %d zbývajících pokusů",
|
||||||
|
"unsupported password type: %s": "nepodporovaný typ hesla: %s"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"Missing parameter": "Chybějící parametr",
|
||||||
|
"Please login first": "Prosím, přihlaste se nejprve",
|
||||||
|
"The organization: %s should have one application at least": "Organizace: %s by měla mít alespoň jednu aplikaci",
|
||||||
|
"The user: %s doesn't exist": "Uživatel: %s neexistuje",
|
||||||
|
"don't support captchaProvider: ": "nepodporuje captchaProvider: ",
|
||||||
|
"this operation is not allowed in demo mode": "tato operace není povolena v demo režimu",
|
||||||
|
"this operation requires administrator to perform": "tato operace vyžaduje administrátora"
|
||||||
|
},
|
||||||
|
"ldap": {
|
||||||
|
"Ldap server exist": "Ldap server existuje"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"Please link first": "Prosím, nejprve propojte",
|
||||||
|
"This application has no providers": "Tato aplikace nemá žádné poskytovatele",
|
||||||
|
"This application has no providers of type": "Tato aplikace nemá žádné poskytovatele typu",
|
||||||
|
"This provider can't be unlinked": "Tento poskytovatel nemůže být odpojen",
|
||||||
|
"You are not the global admin, you can't unlink other users": "Nejste globální administrátor, nemůžete odpojovat jiné uživatele",
|
||||||
|
"You can't unlink yourself, you are not a member of any application": "Nemůžete odpojit sami sebe, nejste členem žádné aplikace"
|
||||||
|
},
|
||||||
|
"organization": {
|
||||||
|
"Only admin can modify the %s.": "Pouze administrátor může upravit %s.",
|
||||||
|
"The %s is immutable.": "%s je neměnný.",
|
||||||
|
"Unknown modify rule %s.": "Neznámé pravidlo úpravy %s."
|
||||||
|
},
|
||||||
|
"permission": {
|
||||||
|
"The permission: \\\"%s\\\" doesn't exist": "Oprávnění: \\\"%s\\\" neexistuje"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"Invalid application id": "Neplatné ID aplikace",
|
||||||
|
"the provider: %s does not exist": "poskytovatel: %s neexistuje"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"User is nil for tag: avatar": "Uživatel je nil pro tag: avatar",
|
||||||
|
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Uživatelské jméno nebo úplná cesta k souboru je prázdná: uživatelské jméno = %s, úplná cesta k souboru = %s"
|
||||||
|
},
|
||||||
|
"saml": {
|
||||||
|
"Application %s not found": "Aplikace %s nebyla nalezena"
|
||||||
|
},
|
||||||
|
"saml_sp": {
|
||||||
|
"provider %s's category is not SAML": "poskytovatel %s není kategorie SAML"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"Empty parameters for emailForm: %v": "Prázdné parametry pro emailForm: %v",
|
||||||
|
"Invalid Email receivers: %s": "Neplatní příjemci emailu: %s",
|
||||||
|
"Invalid phone receivers: %s": "Neplatní příjemci telefonu: %s"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"The objectKey: %s is not allowed": "objectKey: %s není povolen",
|
||||||
|
"The provider type: %s is not supported": "typ poskytovatele: %s není podporován"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"Grant_type: %s is not supported in this application": "Grant_type: %s není v této aplikaci podporován",
|
||||||
|
"Invalid application or wrong clientSecret": "Neplatná aplikace nebo špatný clientSecret",
|
||||||
|
"Invalid client_id": "Neplatné client_id",
|
||||||
|
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Přesměrovací URI: %s neexistuje v seznamu povolených přesměrovacích URI",
|
||||||
|
"Token not found, invalid accessToken": "Token nenalezen, neplatný accessToken"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"Display name cannot be empty": "Zobrazované jméno nemůže být prázdné",
|
||||||
|
"New password cannot contain blank space.": "Nové heslo nemůže obsahovat prázdné místo."
|
||||||
|
},
|
||||||
|
"user_upload": {
|
||||||
|
"Failed to import users": "Nepodařilo se importovat uživatele"
|
||||||
|
},
|
||||||
|
"util": {
|
||||||
|
"No application is found for userId: %s": "Pro userId: %s nebyla nalezena žádná aplikace",
|
||||||
|
"No provider for category: %s is found for application: %s": "Pro kategorii: %s nebyl nalezen žádný poskytovatel pro aplikaci: %s",
|
||||||
|
"The provider: %s is not found": "Poskytovatel: %s nebyl nalezen"
|
||||||
|
},
|
||||||
|
"verification": {
|
||||||
|
"Invalid captcha provider.": "Neplatný poskytovatel captcha.",
|
||||||
|
"Phone number is invalid in your region %s": "Telefonní číslo je ve vaší oblasti %s neplatné",
|
||||||
|
"The verification code has not been sent yet!": "Ověřovací kód ještě nebyl odeslán!",
|
||||||
|
"The verification code has not been sent yet, or has already been used!": "Ověřovací kód ještě nebyl odeslán, nebo již byl použit!",
|
||||||
|
"Turing test failed.": "Turingův test selhal.",
|
||||||
|
"Unable to get the email modify rule.": "Nelze získat pravidlo pro úpravu emailu.",
|
||||||
|
"Unable to get the phone modify rule.": "Nelze získat pravidlo pro úpravu telefonu.",
|
||||||
|
"Unknown type": "Neznámý typ",
|
||||||
|
"Wrong verification code!": "Špatný ověřovací kód!",
|
||||||
|
"You should verify your code in %d min!": "Měli byste ověřit svůj kód do %d minut!",
|
||||||
|
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "prosím přidejte poskytovatele SMS do seznamu \\\"Providers\\\" pro aplikaci: %s",
|
||||||
|
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "prosím přidejte poskytovatele emailu do seznamu \\\"Providers\\\" pro aplikaci: %s",
|
||||||
|
"the user does not exist, please sign up first": "uživatel neexistuje, prosím nejprve se zaregistrujte"
|
||||||
|
},
|
||||||
|
"webauthn": {
|
||||||
|
"Found no credentials for this user": "Nebyly nalezeny žádné přihlašovací údaje pro tohoto uživatele",
|
||||||
|
"Please call WebAuthnSigninBegin first": "Prosím, nejprve zavolejte WebAuthnSigninBegin"
|
||||||
|
}
|
||||||
|
}
|
167
i18n/locales/sk/data.json
Normal file
167
i18n/locales/sk/data.json
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"Failed to add user": "Nepodarilo sa pridať používateľa",
|
||||||
|
"Get init score failed, error: %w": "Získanie počiatočného skóre zlyhalo, chyba: %w",
|
||||||
|
"Please sign out first": "Najskôr sa prosím odhláste",
|
||||||
|
"The application does not allow to sign up new account": "Aplikácia neumožňuje registráciu nového účtu"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"Challenge method should be S256": "Metóda výzvy by mala byť S256",
|
||||||
|
"Failed to create user, user information is invalid: %s": "Nepodarilo sa vytvoriť používateľa, informácie o používateľovi sú neplatné: %s",
|
||||||
|
"Failed to login in: %s": "Prihlásenie zlyhalo: %s",
|
||||||
|
"Invalid token": "Neplatný token",
|
||||||
|
"State expected: %s, but got: %s": "Očakávaný stav: %s, ale dostali sme: %s",
|
||||||
|
"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": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet cez %%s, prosím použite iný spôsob registrácie",
|
||||||
|
"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": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) neexistuje a nie je povolené zaregistrovať nový účet, prosím kontaktujte vašu IT podporu",
|
||||||
|
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Účet pre poskytovateľa: %s a používateľské meno: %s (%s) je už prepojený s iným účtom: %s (%s)",
|
||||||
|
"The application: %s does not exist": "Aplikácia: %s neexistuje",
|
||||||
|
"The login method: login with LDAP is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou LDAP nie je pre aplikáciu povolená",
|
||||||
|
"The login method: login with SMS is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou SMS nie je pre aplikáciu povolená",
|
||||||
|
"The login method: login with email is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou e-mailu nie je pre aplikáciu povolená",
|
||||||
|
"The login method: login with face is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou tváre nie je pre aplikáciu povolená",
|
||||||
|
"The login method: login with password is not enabled for the application": "Metóda prihlásenia: prihlásenie pomocou hesla nie je pre aplikáciu povolená",
|
||||||
|
"The organization: %s does not exist": "Organizácia: %s neexistuje",
|
||||||
|
"The provider: %s is not enabled for the application": "Poskytovateľ: %s nie je pre aplikáciu povolený",
|
||||||
|
"Unauthorized operation": "Neautorizovaná operácia",
|
||||||
|
"Unknown authentication type (not password or provider), form = %s": "Neznámy typ autentifikácie (nie heslo alebo poskytovateľ), forma = %s",
|
||||||
|
"User's tag: %s is not listed in the application's tags": "Štítok používateľa: %s nie je uvedený v štítkoch aplikácie",
|
||||||
|
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "platiaci používateľ %s nemá aktívne alebo čakajúce predplatné a aplikácia: %s nemá predvolenú cenovú politiku"
|
||||||
|
},
|
||||||
|
"cas": {
|
||||||
|
"Service %s and %s do not match": "Služba %s a %s sa nezhodujú"
|
||||||
|
},
|
||||||
|
"check": {
|
||||||
|
"Affiliation cannot be blank": "Príslušnosť nemôže byť prázdna",
|
||||||
|
"Default code does not match the code's matching rules": "Predvolený kód nezodpovedá pravidlám zodpovedania kódu",
|
||||||
|
"DisplayName cannot be blank": "Zobrazované meno nemôže byť prázdne",
|
||||||
|
"DisplayName is not valid real name": "Zobrazované meno nie je platné skutočné meno",
|
||||||
|
"Email already exists": "E-mail už existuje",
|
||||||
|
"Email cannot be empty": "E-mail nemôže byť prázdny",
|
||||||
|
"Email is invalid": "E-mail je neplatný",
|
||||||
|
"Empty username.": "Prázdne používateľské meno.",
|
||||||
|
"Face data does not exist, cannot log in": "Dáta o tvári neexistujú, nemožno sa prihlásiť",
|
||||||
|
"Face data mismatch": "Nesúlad dát o tvári",
|
||||||
|
"FirstName cannot be blank": "Meno nemôže byť prázdne",
|
||||||
|
"Invitation code cannot be blank": "Kód pozvania nemôže byť prázdny",
|
||||||
|
"Invitation code exhausted": "Kód pozvania bol vyčerpaný",
|
||||||
|
"Invitation code is invalid": "Kód pozvania je neplatný",
|
||||||
|
"Invitation code suspended": "Kód pozvania bol pozastavený",
|
||||||
|
"LDAP user name or password incorrect": "LDAP používateľské meno alebo heslo sú nesprávne",
|
||||||
|
"LastName cannot be blank": "Priezvisko nemôže byť prázdne",
|
||||||
|
"Multiple accounts with same uid, please check your ldap server": "Viacero účtov s rovnakým uid, skontrolujte svoj ldap server",
|
||||||
|
"Organization does not exist": "Organizácia neexistuje",
|
||||||
|
"Phone already exists": "Telefón už existuje",
|
||||||
|
"Phone cannot be empty": "Telefón nemôže byť prázdny",
|
||||||
|
"Phone number is invalid": "Telefónne číslo je neplatné",
|
||||||
|
"Please register using the email corresponding to the invitation code": "Prosím, zaregistrujte sa pomocou e-mailu zodpovedajúceho kódu pozvania",
|
||||||
|
"Please register using the phone corresponding to the invitation code": "Prosím, zaregistrujte sa pomocou telefónu zodpovedajúceho kódu pozvania",
|
||||||
|
"Please register using the username corresponding to the invitation code": "Prosím, zaregistrujte sa pomocou používateľského mena zodpovedajúceho kódu pozvania",
|
||||||
|
"Session outdated, please login again": "Relácia je zastaraná, prosím, prihláste sa znova",
|
||||||
|
"The invitation code has already been used": "Kód pozvania už bol použitý",
|
||||||
|
"The user is forbidden to sign in, please contact the administrator": "Používateľovi je zakázané prihlásenie, prosím, kontaktujte administrátora",
|
||||||
|
"The user: %s doesn't exist in LDAP server": "Používateľ: %s neexistuje na LDAP serveri",
|
||||||
|
"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.": "Používateľské meno môže obsahovať iba alfanumerické znaky, podtržníky alebo pomlčky, nemôže obsahovať po sebe idúce pomlčky alebo podtržníky a nemôže začínať alebo končiť pomlčkou alebo podtržníkom.",
|
||||||
|
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Hodnota \\\"%s\\\" pre pole účtu \\\"%s\\\" nezodpovedá regulárnemu výrazu položky účtu",
|
||||||
|
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Hodnota \\\"%s\\\" pre pole registrácie \\\"%s\\\" nezodpovedá regulárnemu výrazu položky registrácie aplikácie \\\"%s\\\"",
|
||||||
|
"Username already exists": "Používateľské meno už existuje",
|
||||||
|
"Username cannot be an email address": "Používateľské meno nemôže byť e-mailová adresa",
|
||||||
|
"Username cannot contain white spaces": "Používateľské meno nemôže obsahovať medzery",
|
||||||
|
"Username cannot start with a digit": "Používateľské meno nemôže začínať číslicou",
|
||||||
|
"Username is too long (maximum is 39 characters).": "Používateľské meno je príliš dlhé (maximum je 39 znakov).",
|
||||||
|
"Username must have at least 2 characters": "Používateľské meno musí mať aspoň 2 znaky",
|
||||||
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "Zadali ste nesprávne heslo alebo kód príliš veľa krát, prosím, počkajte %d minút a skúste to znova",
|
||||||
|
"Your region is not allow to signup by phone": "Váš región neumožňuje registráciu cez telefón",
|
||||||
|
"password or code is incorrect": "heslo alebo kód je nesprávne",
|
||||||
|
"password or code is incorrect, you have %d remaining chances": "heslo alebo kód je nesprávne, máte %d zostávajúcich pokusov",
|
||||||
|
"unsupported password type: %s": "nepodporovaný typ hesla: %s"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"Missing parameter": "Chýbajúci parameter",
|
||||||
|
"Please login first": "Najskôr sa prosím prihláste",
|
||||||
|
"The organization: %s should have one application at least": "Organizácia: %s by mala mať aspoň jednu aplikáciu",
|
||||||
|
"The user: %s doesn't exist": "Používateľ: %s neexistuje",
|
||||||
|
"don't support captchaProvider: ": "nepodporuje captchaProvider: ",
|
||||||
|
"this operation is not allowed in demo mode": "táto operácia nie je povolená v demo režime",
|
||||||
|
"this operation requires administrator to perform": "táto operácia vyžaduje vykonanie administrátorom"
|
||||||
|
},
|
||||||
|
"ldap": {
|
||||||
|
"Ldap server exist": "LDAP server existuje"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"Please link first": "Najskôr sa prosím prepojte",
|
||||||
|
"This application has no providers": "Táto aplikácia nemá žiadnych poskytovateľov",
|
||||||
|
"This application has no providers of type": "Táto aplikácia nemá poskytovateľov typu",
|
||||||
|
"This provider can't be unlinked": "Tento poskytovateľ nemôže byť odpojený",
|
||||||
|
"You are not the global admin, you can't unlink other users": "Nie ste globálny administrátor, nemôžete odpojiť iných používateľov",
|
||||||
|
"You can't unlink yourself, you are not a member of any application": "Nemôžete sa odpojiť, nie ste členom žiadnej aplikácie"
|
||||||
|
},
|
||||||
|
"organization": {
|
||||||
|
"Only admin can modify the %s.": "Len administrátor môže upravovať %s.",
|
||||||
|
"The %s is immutable.": "%s je nemenný.",
|
||||||
|
"Unknown modify rule %s.": "Neznáme pravidlo úprav %s."
|
||||||
|
},
|
||||||
|
"permission": {
|
||||||
|
"The permission: \\\"%s\\\" doesn't exist": "Povolenie: \\\"%s\\\" neexistuje"
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"Invalid application id": "Neplatné id aplikácie",
|
||||||
|
"the provider: %s does not exist": "poskytovateľ: %s neexistuje"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"User is nil for tag: avatar": "Používateľ je nil pre tag: avatar",
|
||||||
|
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Používateľské meno alebo fullFilePath je prázdny: používateľské meno = %s, fullFilePath = %s"
|
||||||
|
},
|
||||||
|
"saml": {
|
||||||
|
"Application %s not found": "Aplikácia %s nebola nájdená"
|
||||||
|
},
|
||||||
|
"saml_sp": {
|
||||||
|
"provider %s's category is not SAML": "kategória poskytovateľa %s nie je SAML"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"Empty parameters for emailForm: %v": "Prázdne parametre pre emailForm: %v",
|
||||||
|
"Invalid Email receivers: %s": "Neplatní príjemcovia e-mailu: %s",
|
||||||
|
"Invalid phone receivers: %s": "Neplatní príjemcovia telefónu: %s"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"The objectKey: %s is not allowed": "objectKey: %s nie je povolený",
|
||||||
|
"The provider type: %s is not supported": "Typ poskytovateľa: %s nie je podporovaný"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"Grant_type: %s is not supported in this application": "Grant_type: %s nie je podporovaný v tejto aplikácii",
|
||||||
|
"Invalid application or wrong clientSecret": "Neplatná aplikácia alebo nesprávny clientSecret",
|
||||||
|
"Invalid client_id": "Neplatný client_id",
|
||||||
|
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s neexistuje v zozname povolených Redirect URI",
|
||||||
|
"Token not found, invalid accessToken": "Token nebol nájdený, neplatný accessToken"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"Display name cannot be empty": "Zobrazované meno nemôže byť prázdne",
|
||||||
|
"New password cannot contain blank space.": "Nové heslo nemôže obsahovať medzery."
|
||||||
|
},
|
||||||
|
"user_upload": {
|
||||||
|
"Failed to import users": "Nepodarilo sa importovať používateľov"
|
||||||
|
},
|
||||||
|
"util": {
|
||||||
|
"No application is found for userId: %s": "Nebola nájdená žiadna aplikácia pre userId: %s",
|
||||||
|
"No provider for category: %s is found for application: %s": "Pre aplikáciu: %s nebol nájdený žiadny poskytovateľ pre kategóriu: %s",
|
||||||
|
"The provider: %s is not found": "Poskytovateľ: %s nebol nájdený"
|
||||||
|
},
|
||||||
|
"verification": {
|
||||||
|
"Invalid captcha provider.": "Neplatný captcha poskytovateľ.",
|
||||||
|
"Phone number is invalid in your region %s": "Telefónne číslo je neplatné vo vašom regióne %s",
|
||||||
|
"The verification code has not been sent yet!": "Overovací kód ešte nebol odoslaný!",
|
||||||
|
"The verification code has not been sent yet, or has already been used!": "Overovací kód ešte nebol odoslaný, alebo bol už použitý!",
|
||||||
|
"Turing test failed.": "Test Turinga zlyhal.",
|
||||||
|
"Unable to get the email modify rule.": "Nepodarilo sa získať pravidlo úpravy e-mailu.",
|
||||||
|
"Unable to get the phone modify rule.": "Nepodarilo sa získať pravidlo úpravy telefónu.",
|
||||||
|
"Unknown type": "Neznámy typ",
|
||||||
|
"Wrong verification code!": "Nesprávny overovací kód!",
|
||||||
|
"You should verify your code in %d min!": "Overte svoj kód za %d minút!",
|
||||||
|
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "prosím pridajte SMS poskytovateľa do zoznamu \\\"Poskytovatelia\\\" pre aplikáciu: %s",
|
||||||
|
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "prosím pridajte e-mailového poskytovateľa do zoznamu \\\"Poskytovatelia\\\" pre aplikáciu: %s",
|
||||||
|
"the user does not exist, please sign up first": "používateľ neexistuje, prosím, zaregistrujte sa najskôr"
|
||||||
|
},
|
||||||
|
"webauthn": {
|
||||||
|
"Found no credentials for this user": "Nenašli sa žiadne prihlasovacie údaje pre tohto používateľa",
|
||||||
|
"Please call WebAuthnSigninBegin first": "Najskôr prosím zavolajte WebAuthnSigninBegin"
|
||||||
|
}
|
||||||
|
}
|
18
idp/lark.go
18
idp/lark.go
@ -22,6 +22,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/nyaruka/phonenumbers"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -199,12 +200,25 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var phoneNumber string
|
||||||
|
var countryCode string
|
||||||
|
if len(larkUserInfo.Data.Mobile) != 0 {
|
||||||
|
phoneNumberParsed, err := phonenumbers.Parse(larkUserInfo.Data.Mobile, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
countryCode = phonenumbers.GetRegionCodeForNumber(phoneNumberParsed)
|
||||||
|
phoneNumber = fmt.Sprintf("%d", phoneNumberParsed.GetNationalNumber())
|
||||||
|
}
|
||||||
|
|
||||||
userInfo := UserInfo{
|
userInfo := UserInfo{
|
||||||
Id: larkUserInfo.Data.OpenId,
|
Id: larkUserInfo.Data.OpenId,
|
||||||
DisplayName: larkUserInfo.Data.EnName,
|
DisplayName: larkUserInfo.Data.Name,
|
||||||
Username: larkUserInfo.Data.Name,
|
Username: larkUserInfo.Data.UserId,
|
||||||
Email: larkUserInfo.Data.Email,
|
Email: larkUserInfo.Data.Email,
|
||||||
AvatarUrl: larkUserInfo.Data.AvatarUrl,
|
AvatarUrl: larkUserInfo.Data.AvatarUrl,
|
||||||
|
Phone: phoneNumber,
|
||||||
|
CountryCode: countryCode,
|
||||||
}
|
}
|
||||||
return &userInfo, nil
|
return &userInfo, nil
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@
|
|||||||
"FI",
|
"FI",
|
||||||
"SE",
|
"SE",
|
||||||
"UA",
|
"UA",
|
||||||
"KZ"
|
"KZ",
|
||||||
|
"CZ",
|
||||||
|
"SK"
|
||||||
],
|
],
|
||||||
"defaultAvatar": "",
|
"defaultAvatar": "",
|
||||||
"defaultApplication": "",
|
"defaultApplication": "",
|
||||||
@ -62,7 +64,9 @@
|
|||||||
"sv",
|
"sv",
|
||||||
"uk",
|
"uk",
|
||||||
"kk",
|
"kk",
|
||||||
"fa"
|
"fa",
|
||||||
|
"cs",
|
||||||
|
"sk"
|
||||||
],
|
],
|
||||||
"masterPassword": "",
|
"masterPassword": "",
|
||||||
"defaultPassword": "",
|
"defaultPassword": "",
|
||||||
|
@ -59,7 +59,15 @@ func handleBind(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bindPassword := string(r.AuthenticationSimple())
|
bindPassword := string(r.AuthenticationSimple())
|
||||||
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en")
|
|
||||||
|
enableCaptcha := false
|
||||||
|
isSigninViaLdap := false
|
||||||
|
isPasswordWithLdapEnabled := false
|
||||||
|
if bindPassword != "" {
|
||||||
|
isPasswordWithLdapEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
|
bindUser, err := object.CheckUserPassword(bindOrg, bindUsername, bindPassword, "en", enableCaptcha, isSigninViaLdap, isPasswordWithLdapEnabled)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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(ldap.LDAPResultInvalidCredentials)
|
res.SetResultCode(ldap.LDAPResultInvalidCredentials)
|
||||||
@ -122,6 +130,9 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
|
|||||||
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
|
e.AddAttribute("homeDirectory", message.AttributeValue("/home/"+user.Name))
|
||||||
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
e.AddAttribute("cn", message.AttributeValue(user.Name))
|
||||||
e.AddAttribute("uid", message.AttributeValue(user.Id))
|
e.AddAttribute("uid", message.AttributeValue(user.Id))
|
||||||
|
for _, group := range user.Groups {
|
||||||
|
e.AddAttribute(ldapMemberOfAttr, message.AttributeValue(group))
|
||||||
|
}
|
||||||
attrs := r.Attributes()
|
attrs := r.Attributes()
|
||||||
for _, attr := range attrs {
|
for _, attr := range attrs {
|
||||||
if string(attr) == "*" {
|
if string(attr) == "*" {
|
||||||
|
21
ldap/util.go
21
ldap/util.go
@ -79,6 +79,8 @@ var ldapAttributesMapping = map[string]FieldRelation{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ldapMemberOfAttr = "memberOf"
|
||||||
|
|
||||||
var AdditionalLdapAttributes []message.LDAPString
|
var AdditionalLdapAttributes []message.LDAPString
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -180,7 +182,22 @@ func buildUserFilterCondition(filter interface{}) (builder.Cond, error) {
|
|||||||
}
|
}
|
||||||
return builder.Not{cond}, nil
|
return builder.Not{cond}, nil
|
||||||
case message.FilterEqualityMatch:
|
case message.FilterEqualityMatch:
|
||||||
field, err := getUserFieldFromAttribute(string(f.AttributeDesc()))
|
attr := string(f.AttributeDesc())
|
||||||
|
|
||||||
|
if attr == ldapMemberOfAttr {
|
||||||
|
groupId := string(f.AssertionValue())
|
||||||
|
users, err := object.GetGroupUsers(groupId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var names []string
|
||||||
|
for _, user := range users {
|
||||||
|
names = append(names, user.Name)
|
||||||
|
}
|
||||||
|
return builder.In("name", names), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
field, err := getUserFieldFromAttribute(attr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -246,7 +263,7 @@ func GetFilteredUsers(m *ldap.Message) (filteredUsers []*object.User, code int)
|
|||||||
return nil, code
|
return nil, code
|
||||||
}
|
}
|
||||||
|
|
||||||
if name == "*" && m.Client.IsOrgAdmin { // get all users from organization 'org'
|
if name == "*" { // get all users from organization 'org'
|
||||||
if m.Client.IsGlobalAdmin && org == "*" {
|
if m.Client.IsGlobalAdmin && org == "*" {
|
||||||
filteredUsers, err = object.GetGlobalUsersWithFilter(buildSafeCondition(r.Filter()))
|
filteredUsers, err = object.GetGlobalUsersWithFilter(buildSafeCondition(r.Filter()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,11 +91,13 @@ type Application struct {
|
|||||||
CertPublicKey string `xorm:"-" json:"certPublicKey"`
|
CertPublicKey string `xorm:"-" json:"certPublicKey"`
|
||||||
Tags []string `xorm:"mediumtext" json:"tags"`
|
Tags []string `xorm:"mediumtext" json:"tags"`
|
||||||
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
|
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
|
||||||
|
IsShared bool `json:"isShared"`
|
||||||
|
|
||||||
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
ClientId string `xorm:"varchar(100)" json:"clientId"`
|
||||||
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
|
||||||
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
|
||||||
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
|
||||||
|
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
|
||||||
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
|
||||||
ExpireInHours int `json:"expireInHours"`
|
ExpireInHours int `json:"expireInHours"`
|
||||||
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
RefreshExpireInHours int `json:"refreshExpireInHours"`
|
||||||
@ -123,9 +125,9 @@ func GetApplicationCount(owner, field, value string) (int64, error) {
|
|||||||
return session.Count(&Application{})
|
return session.Count(&Application{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOrganizationApplicationCount(owner, Organization, field, value string) (int64, error) {
|
func GetOrganizationApplicationCount(owner, organization, field, value string) (int64, error) {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
return session.Count(&Application{Organization: Organization})
|
return session.Where("organization = ? or is_shared = ? ", organization, true).Count(&Application{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApplications(owner string) ([]*Application, error) {
|
func GetApplications(owner string) ([]*Application, error) {
|
||||||
@ -140,7 +142,7 @@ func GetApplications(owner string) ([]*Application, error) {
|
|||||||
|
|
||||||
func GetOrganizationApplications(owner string, organization string) ([]*Application, error) {
|
func GetOrganizationApplications(owner string, organization string) ([]*Application, error) {
|
||||||
applications := []*Application{}
|
applications := []*Application{}
|
||||||
err := ormer.Engine.Desc("created_time").Find(&applications, &Application{Organization: organization})
|
err := ormer.Engine.Desc("created_time").Where("organization = ? or is_shared = ? ", organization, true).Find(&applications, &Application{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return applications, err
|
return applications, err
|
||||||
}
|
}
|
||||||
@ -162,7 +164,7 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
|
|||||||
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Application, error) {
|
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Application, error) {
|
||||||
applications := []*Application{}
|
applications := []*Application{}
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&applications, &Application{Organization: organization})
|
err := session.Where("organization = ? or is_shared = ? ", organization, true).Find(&applications, &Application{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return applications, err
|
return applications, err
|
||||||
}
|
}
|
||||||
@ -337,12 +339,18 @@ func getApplication(owner string, name string) (*Application, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
application := Application{Owner: owner, Name: name}
|
realApplicationName, sharedOrg := util.GetSharedOrgFromApp(name)
|
||||||
|
|
||||||
|
application := Application{Owner: owner, Name: realApplicationName}
|
||||||
existed, err := ormer.Engine.Get(&application)
|
existed, err := ormer.Engine.Get(&application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.IsShared && sharedOrg != "" {
|
||||||
|
application.Organization = sharedOrg
|
||||||
|
}
|
||||||
|
|
||||||
if existed {
|
if existed {
|
||||||
err = extendApplicationWithProviders(&application)
|
err = extendApplicationWithProviders(&application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -428,11 +436,18 @@ func GetApplicationByUserId(userId string) (application *Application, err error)
|
|||||||
|
|
||||||
func GetApplicationByClientId(clientId string) (*Application, error) {
|
func GetApplicationByClientId(clientId string) (*Application, error) {
|
||||||
application := Application{}
|
application := Application{}
|
||||||
existed, err := ormer.Engine.Where("client_id=?", clientId).Get(&application)
|
|
||||||
|
realClientId, sharedOrg := util.GetSharedOrgFromApp(clientId)
|
||||||
|
|
||||||
|
existed, err := ormer.Engine.Where("client_id=?", realClientId).Get(&application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.IsShared && sharedOrg != "" {
|
||||||
|
application.Organization = sharedOrg
|
||||||
|
}
|
||||||
|
|
||||||
if existed {
|
if existed {
|
||||||
err = extendApplicationWithProviders(&application)
|
err = extendApplicationWithProviders(&application)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -626,6 +641,10 @@ func UpdateApplication(id string, application *Application) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.IsShared == true && application.Organization != "built-in" {
|
||||||
|
return false, fmt.Errorf("only applications belonging to built-in organization can be shared")
|
||||||
|
}
|
||||||
|
|
||||||
for _, providerItem := range application.Providers {
|
for _, providerItem := range application.Providers {
|
||||||
providerItem.Provider = nil
|
providerItem.Provider = nil
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,9 @@ func GetFailedSigninConfigByUser(user *User) (int, int, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, err
|
return 0, 0, err
|
||||||
}
|
}
|
||||||
|
if application == nil {
|
||||||
|
return 0, 0, fmt.Errorf("the application for user %s is not found", user.GetId())
|
||||||
|
}
|
||||||
|
|
||||||
failedSigninLimit := application.FailedSigninLimit
|
failedSigninLimit := application.FailedSigninLimit
|
||||||
if failedSigninLimit == 0 {
|
if failedSigninLimit == 0 {
|
||||||
|
@ -78,6 +78,7 @@ func getBuiltInAccountItems() []*AccountItem {
|
|||||||
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
|
{Name: "MFA accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +110,7 @@ func initBuiltInOrganization() bool {
|
|||||||
EnableSoftDeletion: false,
|
EnableSoftDeletion: false,
|
||||||
IsProfilePublic: false,
|
IsProfilePublic: false,
|
||||||
UseEmailAsUsername: false,
|
UseEmailAsUsername: false,
|
||||||
|
EnableTour: true,
|
||||||
}
|
}
|
||||||
_, err = AddOrganization(organization)
|
_, err = AddOrganization(organization)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -32,6 +32,7 @@ type Ldap struct {
|
|||||||
BaseDn string `xorm:"varchar(100)" json:"baseDn"`
|
BaseDn string `xorm:"varchar(100)" json:"baseDn"`
|
||||||
Filter string `xorm:"varchar(200)" json:"filter"`
|
Filter string `xorm:"varchar(200)" json:"filter"`
|
||||||
FilterFields []string `xorm:"varchar(100)" json:"filterFields"`
|
FilterFields []string `xorm:"varchar(100)" json:"filterFields"`
|
||||||
|
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
|
||||||
|
|
||||||
AutoSync int `json:"autoSync"`
|
AutoSync int `json:"autoSync"`
|
||||||
LastSync string `xorm:"varchar(100)" json:"lastSync"`
|
LastSync string `xorm:"varchar(100)" json:"lastSync"`
|
||||||
@ -148,7 +149,7 @@ func UpdateLdap(ldap *Ldap) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
affected, err := ormer.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
|
affected, err := ormer.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
|
||||||
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync").Update(ldap)
|
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync", "default_group").Update(ldap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
@ -339,6 +339,10 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
|
|||||||
Ldap: syncUser.Uuid,
|
Ldap: syncUser.Uuid,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ldap.DefaultGroup != "" {
|
||||||
|
newUser.Groups = []string{ldap.DefaultGroup}
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := AddUser(newUser)
|
affected, err := AddUser(newUser)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -112,7 +112,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
|
|||||||
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
|
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
|
||||||
GrantTypesSupported: []string{"password", "authorization_code"},
|
GrantTypesSupported: []string{"password", "authorization_code"},
|
||||||
SubjectTypesSupported: []string{"public"},
|
SubjectTypesSupported: []string{"public"},
|
||||||
IdTokenSigningAlgValuesSupported: []string{"RS256"},
|
IdTokenSigningAlgValuesSupported: []string{"RS256", "RS512", "ES256", "ES384", "ES512"},
|
||||||
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
|
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
|
||||||
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
|
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
|
||||||
RequestParameterSupported: true,
|
RequestParameterSupported: true,
|
||||||
|
@ -73,6 +73,7 @@ type Organization struct {
|
|||||||
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
EnableSoftDeletion bool `json:"enableSoftDeletion"`
|
||||||
IsProfilePublic bool `json:"isProfilePublic"`
|
IsProfilePublic bool `json:"isProfilePublic"`
|
||||||
UseEmailAsUsername bool `json:"useEmailAsUsername"`
|
UseEmailAsUsername bool `json:"useEmailAsUsername"`
|
||||||
|
EnableTour bool `json:"enableTour"`
|
||||||
|
|
||||||
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
|
||||||
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
|
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
|
||||||
@ -318,6 +319,7 @@ func GetDefaultApplication(id string) (*Application, error) {
|
|||||||
if defaultApplication == nil {
|
if defaultApplication == nil {
|
||||||
return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
|
return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
|
||||||
} else {
|
} else {
|
||||||
|
defaultApplication.Organization = organization.Name
|
||||||
return defaultApplication, nil
|
return defaultApplication, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,6 +357,11 @@ func GetDefaultApplication(id string) (*Application, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = extendApplicationWithSigninMethods(defaultApplication)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return defaultApplication, nil
|
return defaultApplication, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,7 +201,7 @@ func notifyPayment(body []byte, owner string, paymentName string) (*Payment, *pp
|
|||||||
}
|
}
|
||||||
|
|
||||||
if payment.IsRecharge {
|
if payment.IsRecharge {
|
||||||
err = updateUserBalance(payment.Owner, payment.User, payment.Price)
|
err = UpdateUserBalance(payment.Owner, payment.User, payment.Price)
|
||||||
return payment, notifyResult, err
|
return payment, notifyResult, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +222,19 @@ func NotifyPayment(body []byte, owner string, paymentName string) (*Payment, err
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transaction, err := GetTransaction(payment.GetId())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if transaction != nil {
|
||||||
|
transaction.State = payment.State
|
||||||
|
_, err = UpdateTransaction(transaction.GetId(), transaction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return payment, nil
|
return payment, nil
|
||||||
|
@ -181,15 +181,15 @@ func UpdatePermission(id string, permission *Permission) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
|
// if oldPermission.Adapter != "" && oldPermission.Adapter != permission.Adapter {
|
||||||
isEmpty, _ := ormer.Engine.IsTableEmpty(oldPermission.Adapter)
|
// isEmpty, _ := ormer.Engine.IsTableEmpty(oldPermission.Adapter)
|
||||||
if isEmpty {
|
// if isEmpty {
|
||||||
err = ormer.Engine.DropTables(oldPermission.Adapter)
|
// err = ormer.Engine.DropTables(oldPermission.Adapter)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return false, err
|
// return false, err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
err = addGroupingPolicies(permission)
|
err = addGroupingPolicies(permission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -312,15 +312,15 @@ func DeletePermission(permission *Permission) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if permission.Adapter != "" && permission.Adapter != "permission_rule" {
|
// if permission.Adapter != "" && permission.Adapter != "permission_rule" {
|
||||||
isEmpty, _ := ormer.Engine.IsTableEmpty(permission.Adapter)
|
// isEmpty, _ := ormer.Engine.IsTableEmpty(permission.Adapter)
|
||||||
if isEmpty {
|
// if isEmpty {
|
||||||
err = ormer.Engine.DropTables(permission.Adapter)
|
// err = ormer.Engine.DropTables(permission.Adapter)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return false, err
|
// return false, err
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return affected, nil
|
return affected, nil
|
||||||
|
@ -227,13 +227,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
NotifyUrl: notifyUrl,
|
NotifyUrl: notifyUrl,
|
||||||
PaymentEnv: paymentEnv,
|
PaymentEnv: paymentEnv,
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom process for WeChat & WeChat Pay
|
// custom process for WeChat & WeChat Pay
|
||||||
if provider.Type == "WeChat Pay" {
|
if provider.Type == "WeChat Pay" {
|
||||||
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
payReq.PayerId, err = getUserExtraProperty(user, "WeChat", idp.BuildWechatOpenIdKey(provider.ClientId2))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
} else if provider.Type == "Balance" {
|
||||||
|
payReq.PayerId = user.GetId()
|
||||||
}
|
}
|
||||||
|
|
||||||
payResp, err := pProvider.Pay(payReq)
|
payResp, err := pProvider.Pay(payReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@ -264,12 +268,46 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
OutOrderId: payResp.OrderId,
|
OutOrderId: payResp.OrderId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transaction := &Transaction{
|
||||||
|
Owner: payment.Owner,
|
||||||
|
Name: payment.Name,
|
||||||
|
DisplayName: payment.DisplayName,
|
||||||
|
Provider: provider.Name,
|
||||||
|
Category: provider.Category,
|
||||||
|
Type: provider.Type,
|
||||||
|
|
||||||
|
ProductName: product.Name,
|
||||||
|
ProductDisplayName: product.DisplayName,
|
||||||
|
Detail: product.Detail,
|
||||||
|
Tag: product.Tag,
|
||||||
|
Currency: product.Currency,
|
||||||
|
Amount: payment.Price,
|
||||||
|
ReturnUrl: payment.ReturnUrl,
|
||||||
|
|
||||||
|
User: payment.User,
|
||||||
|
Application: owner,
|
||||||
|
Payment: payment.GetId(),
|
||||||
|
|
||||||
|
State: pp.PaymentStateCreated,
|
||||||
|
}
|
||||||
|
|
||||||
if provider.Type == "Dummy" {
|
if provider.Type == "Dummy" {
|
||||||
payment.State = pp.PaymentStatePaid
|
payment.State = pp.PaymentStatePaid
|
||||||
err = updateUserBalance(user.Owner, user.Name, payment.Price)
|
err = UpdateUserBalance(user.Owner, user.Name, payment.Price)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
} else if provider.Type == "Balance" {
|
||||||
|
if product.Price > user.Balance {
|
||||||
|
return nil, nil, fmt.Errorf("insufficient user balance")
|
||||||
|
}
|
||||||
|
transaction.Amount = -transaction.Amount
|
||||||
|
err = UpdateUserBalance(user.Owner, user.Name, -product.Price)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
payment.State = pp.PaymentStatePaid
|
||||||
|
transaction.State = pp.PaymentStatePaid
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := AddPayment(payment)
|
affected, err := AddPayment(payment)
|
||||||
@ -280,6 +318,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
|
|||||||
if !affected {
|
if !affected {
|
||||||
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
return nil, nil, fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if product.IsRecharge || provider.Type == "Balance" {
|
||||||
|
affected, err = AddTransaction(transaction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if !affected {
|
||||||
|
return nil, nil, fmt.Errorf("failed to add transaction: %s", util.StructToJson(payment))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return payment, payResp.AttachInfo, nil
|
return payment, payResp.AttachInfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,6 +309,12 @@ func GetPaymentProvider(p *Provider) (pp.PaymentProvider, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return pp, nil
|
return pp, nil
|
||||||
|
} else if typ == "Balance" {
|
||||||
|
pp, err := pp.NewBalancePaymentProvider()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
} else {
|
} else {
|
||||||
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
return nil, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@ type Resource struct {
|
|||||||
FileType string `xorm:"varchar(100)" json:"fileType"`
|
FileType string `xorm:"varchar(100)" json:"fileType"`
|
||||||
FileFormat string `xorm:"varchar(100)" json:"fileFormat"`
|
FileFormat string `xorm:"varchar(100)" json:"fileFormat"`
|
||||||
FileSize int `json:"fileSize"`
|
FileSize int `json:"fileSize"`
|
||||||
Url string `xorm:"varchar(255)" json:"url"`
|
Url string `xorm:"varchar(500)" json:"url"`
|
||||||
Description string `xorm:"varchar(255)" json:"description"`
|
Description string `xorm:"varchar(255)" json:"description"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +30,13 @@ import (
|
|||||||
|
|
||||||
var isCloudIntranet bool
|
var isCloudIntranet bool
|
||||||
|
|
||||||
|
const (
|
||||||
|
ProviderTypeGoogleCloudStorage = "Google Cloud Storage"
|
||||||
|
ProviderTypeTencentCloudCOS = "Tencent Cloud COS"
|
||||||
|
ProviderTypeAzureBlob = "Azure Blob"
|
||||||
|
ProviderTypeLocalFileSystem = "Local File System"
|
||||||
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
isCloudIntranet = conf.GetConfigBool("isCloudIntranet")
|
isCloudIntranet = conf.GetConfigBool("isCloudIntranet")
|
||||||
}
|
}
|
||||||
@ -80,27 +87,28 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
||||||
|
|
||||||
host := ""
|
host := ""
|
||||||
if provider.Type != "Local File System" {
|
if provider.Type != ProviderTypeLocalFileSystem {
|
||||||
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
// provider.Domain = "https://cdn.casbin.com/casdoor/"
|
||||||
host = util.GetUrlHost(provider.Domain)
|
host = util.GetUrlHost(provider.Domain)
|
||||||
} else {
|
} else {
|
||||||
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
|
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
|
||||||
host = util.UrlJoin(provider.Domain, "/files")
|
host = util.UrlJoin(provider.Domain, "/files")
|
||||||
}
|
}
|
||||||
if provider.Type == "Azure Blob" {
|
if provider.Type == ProviderTypeAzureBlob || provider.Type == ProviderTypeGoogleCloudStorage {
|
||||||
host = util.UrlJoin(host, provider.Bucket)
|
host = util.UrlJoin(host, provider.Bucket)
|
||||||
}
|
}
|
||||||
|
|
||||||
fileUrl := ""
|
fileUrl := ""
|
||||||
if host != "" {
|
if host != "" {
|
||||||
fileUrl = util.UrlJoin(host, escapePath(objectKey))
|
// fileUrl = util.UrlJoin(host, escapePath(objectKey))
|
||||||
|
fileUrl = util.UrlJoin(host, objectKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fileUrl != "" && hasTimestamp {
|
// if fileUrl != "" && hasTimestamp {
|
||||||
fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
|
// fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
|
||||||
}
|
// }
|
||||||
|
|
||||||
if provider.Type == "Tencent Cloud COS" {
|
if provider.Type == ProviderTypeTencentCloudCOS {
|
||||||
objectKey = escapePath(objectKey)
|
objectKey = escapePath(objectKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +117,18 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
|
|||||||
|
|
||||||
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
|
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
|
||||||
endpoint := getProviderEndpoint(provider)
|
endpoint := getProviderEndpoint(provider)
|
||||||
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
|
certificate := ""
|
||||||
|
if provider.Category == "Storage" && provider.Type == "Casdoor" {
|
||||||
|
cert, err := GetCert(util.GetId(provider.Owner, provider.Cert))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if cert == nil {
|
||||||
|
return nil, fmt.Errorf("no cert for %s", provider.Cert)
|
||||||
|
}
|
||||||
|
certificate = cert.Certificate
|
||||||
|
}
|
||||||
|
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint, certificate, provider.Content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -135,17 +154,17 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
|||||||
}
|
}
|
||||||
|
|
||||||
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
||||||
|
objectKeyRefined := refineObjectKey(provider, objectKey)
|
||||||
|
|
||||||
objectKeyRefined := objectKey
|
object, err := storageProvider.Put(objectKeyRefined, fileBuffer)
|
||||||
if provider.Type == "Google Cloud Storage" {
|
|
||||||
objectKeyRefined = strings.TrimPrefix(objectKeyRefined, "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = storageProvider.Put(objectKeyRefined, fileBuffer)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if provider.Type == "Casdoor" {
|
||||||
|
fileUrl = object.Path
|
||||||
|
}
|
||||||
|
|
||||||
return fileUrl, objectKey, nil
|
return fileUrl, objectKey, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,5 +203,13 @@ func DeleteFile(provider *Provider, objectKey string, lang string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return storageProvider.Delete(objectKey)
|
objectKeyRefined := refineObjectKey(provider, objectKey)
|
||||||
|
return storageProvider.Delete(objectKeyRefined)
|
||||||
|
}
|
||||||
|
|
||||||
|
func refineObjectKey(provider *Provider, objectKey string) string {
|
||||||
|
if provider.Type == ProviderTypeGoogleCloudStorage {
|
||||||
|
return strings.TrimPrefix(objectKey, "/")
|
||||||
|
}
|
||||||
|
return objectKey
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,6 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if application == nil {
|
if application == nil {
|
||||||
return "", "", fmt.Errorf("the application for user %s is not found", userId)
|
return "", "", fmt.Errorf("the application for user %s is not found", userId)
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
@ -128,7 +129,7 @@ type UserWithoutThirdIdp struct {
|
|||||||
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
|
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
|
||||||
SigninWrongTimes int `json:"signinWrongTimes"`
|
SigninWrongTimes int `json:"signinWrongTimes"`
|
||||||
|
|
||||||
// ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
|
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ClaimsShort struct {
|
type ClaimsShort struct {
|
||||||
@ -139,6 +140,15 @@ type ClaimsShort struct {
|
|||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OIDCAddress struct {
|
||||||
|
Formatted string `json:"formatted"`
|
||||||
|
StreetAddress string `json:"street_address"`
|
||||||
|
Locality string `json:"locality"`
|
||||||
|
Region string `json:"region"`
|
||||||
|
PostalCode string `json:"postal_code"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
}
|
||||||
|
|
||||||
type ClaimsWithoutThirdIdp struct {
|
type ClaimsWithoutThirdIdp struct {
|
||||||
*UserWithoutThirdIdp
|
*UserWithoutThirdIdp
|
||||||
TokenType string `json:"tokenType,omitempty"`
|
TokenType string `json:"tokenType,omitempty"`
|
||||||
@ -245,6 +255,8 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
|
|||||||
|
|
||||||
LastSigninWrongTime: user.LastSigninWrongTime,
|
LastSigninWrongTime: user.LastSigninWrongTime,
|
||||||
SigninWrongTimes: user.SigninWrongTimes,
|
SigninWrongTimes: user.SigninWrongTimes,
|
||||||
|
|
||||||
|
ManagedAccounts: user.ManagedAccounts,
|
||||||
}
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
@ -356,6 +368,10 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if application.IsShared {
|
||||||
|
claims.Audience = []string{application.ClientId + "-org-" + user.Owner}
|
||||||
|
}
|
||||||
|
|
||||||
var token *jwt.Token
|
var token *jwt.Token
|
||||||
var refreshToken *jwt.Token
|
var refreshToken *jwt.Token
|
||||||
|
|
||||||
@ -363,29 +379,52 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
|||||||
application.TokenFormat = "JWT"
|
application.TokenFormat = "JWT"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var jwtMethod jwt.SigningMethod
|
||||||
|
|
||||||
|
if application.TokenSigningMethod == "RS256" {
|
||||||
|
jwtMethod = jwt.SigningMethodRS256
|
||||||
|
} else if application.TokenSigningMethod == "RS512" {
|
||||||
|
jwtMethod = jwt.SigningMethodRS512
|
||||||
|
} else if application.TokenSigningMethod == "ES256" {
|
||||||
|
jwtMethod = jwt.SigningMethodES256
|
||||||
|
} else if application.TokenSigningMethod == "ES512" {
|
||||||
|
jwtMethod = jwt.SigningMethodES512
|
||||||
|
} else if application.TokenSigningMethod == "ES384" {
|
||||||
|
jwtMethod = jwt.SigningMethodES384
|
||||||
|
} else {
|
||||||
|
jwtMethod = jwt.SigningMethodRS256
|
||||||
|
}
|
||||||
|
|
||||||
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
|
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
|
||||||
if application.TokenFormat == "JWT" {
|
if application.TokenFormat == "JWT" {
|
||||||
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
|
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
|
||||||
|
|
||||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
|
token = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp)
|
||||||
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
claimsWithoutThirdIdp.TokenType = "refresh-token"
|
claimsWithoutThirdIdp.TokenType = "refresh-token"
|
||||||
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
|
refreshToken = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp)
|
||||||
} else if application.TokenFormat == "JWT-Empty" {
|
} else if application.TokenFormat == "JWT-Empty" {
|
||||||
claimsShort := getShortClaims(claims)
|
claimsShort := getShortClaims(claims)
|
||||||
|
|
||||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
|
token = jwt.NewWithClaims(jwtMethod, claimsShort)
|
||||||
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
claimsShort.TokenType = "refresh-token"
|
claimsShort.TokenType = "refresh-token"
|
||||||
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
|
refreshToken = jwt.NewWithClaims(jwtMethod, claimsShort)
|
||||||
} else if application.TokenFormat == "JWT-Custom" {
|
} else if application.TokenFormat == "JWT-Custom" {
|
||||||
claimsCustom := getClaimsCustom(claims, application.TokenFields)
|
claimsCustom := getClaimsCustom(claims, application.TokenFields)
|
||||||
|
|
||||||
token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsCustom)
|
token = jwt.NewWithClaims(jwtMethod, claimsCustom)
|
||||||
refreshClaims := getClaimsCustom(claims, application.TokenFields)
|
refreshClaims := getClaimsCustom(claims, application.TokenFields)
|
||||||
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
|
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
|
||||||
refreshClaims["TokenType"] = "refresh-token"
|
refreshClaims["TokenType"] = "refresh-token"
|
||||||
refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, refreshClaims)
|
refreshToken = jwt.NewWithClaims(jwtMethod, refreshClaims)
|
||||||
|
} else if application.TokenFormat == "JWT-Standard" {
|
||||||
|
claimsStandard := getStandardClaims(claims)
|
||||||
|
|
||||||
|
token = jwt.NewWithClaims(jwtMethod, claimsStandard)
|
||||||
|
claimsStandard.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
|
||||||
|
claimsStandard.TokenType = "refresh-token"
|
||||||
|
refreshToken = jwt.NewWithClaims(jwtMethod, claimsStandard)
|
||||||
} else {
|
} else {
|
||||||
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
|
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
|
||||||
}
|
}
|
||||||
@ -403,34 +442,57 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSA private key
|
var (
|
||||||
key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
|
tokenString string
|
||||||
|
refreshTokenString string
|
||||||
|
key interface{}
|
||||||
|
)
|
||||||
|
|
||||||
|
if strings.Contains(application.TokenSigningMethod, "RS") || application.TokenSigningMethod == "" {
|
||||||
|
// RSA private key
|
||||||
|
key, err = jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
|
||||||
|
} else if strings.Contains(application.TokenSigningMethod, "ES") {
|
||||||
|
// ES private key
|
||||||
|
key, err = jwt.ParseECPrivateKeyFromPEM([]byte(cert.PrivateKey))
|
||||||
|
} else if strings.Contains(application.TokenSigningMethod, "Ed") {
|
||||||
|
// Ed private key
|
||||||
|
key, err = jwt.ParseEdPrivateKeyFromPEM([]byte(cert.PrivateKey))
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return "", "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
token.Header["kid"] = cert.Name
|
token.Header["kid"] = cert.Name
|
||||||
tokenString, err := token.SignedString(key)
|
tokenString, err = token.SignedString(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", "", err
|
return "", "", "", err
|
||||||
}
|
}
|
||||||
refreshTokenString, err := refreshToken.SignedString(key)
|
refreshTokenString, err = refreshToken.SignedString(key)
|
||||||
|
|
||||||
return tokenString, refreshTokenString, name, err
|
return tokenString, refreshTokenString, name, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
|
func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
|
||||||
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
var (
|
||||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
certificate interface{}
|
||||||
}
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
if cert.Certificate == "" {
|
if cert.Certificate == "" {
|
||||||
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
|
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RSA certificate
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); ok {
|
||||||
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
|
// RSA certificate
|
||||||
|
certificate, err = jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
|
||||||
|
} else if _, ok := token.Method.(*jwt.SigningMethodECDSA); ok {
|
||||||
|
// ES certificate
|
||||||
|
certificate, err = jwt.ParseECPublicKeyFromPEM([]byte(cert.Certificate))
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -309,12 +309,22 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ParseJwtToken(refreshToken, cert)
|
if application.TokenFormat == "JWT-Standard" {
|
||||||
if err != nil {
|
_, err = ParseStandardJwtToken(refreshToken, cert)
|
||||||
return &TokenError{
|
if err != nil {
|
||||||
Error: InvalidGrant,
|
return &TokenError{
|
||||||
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
Error: InvalidGrant,
|
||||||
}, nil
|
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err = ParseJwtToken(refreshToken, cert)
|
||||||
|
if err != nil {
|
||||||
|
return &TokenError{
|
||||||
|
Error: InvalidGrant,
|
||||||
|
ErrorDescription: fmt.Sprintf("parse refresh token error: %s", err.Error()),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate a new token
|
// generate a new token
|
||||||
@ -418,22 +428,26 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
|||||||
if token == nil {
|
if token == nil {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "authorization code is invalid",
|
ErrorDescription: fmt.Sprintf("authorization code: [%s] is invalid", code),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.CodeIsUsed {
|
if token.CodeIsUsed {
|
||||||
// anti replay attacks
|
// anti replay attacks
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "authorization code has been used",
|
ErrorDescription: fmt.Sprintf("authorization code has been used for token: [%s]", token.GetId()),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
|
if token.CodeChallenge != "" {
|
||||||
return nil, &TokenError{
|
challengeAnswer := pkceChallenge(verifier)
|
||||||
Error: InvalidGrant,
|
if challengeAnswer != token.CodeChallenge {
|
||||||
ErrorDescription: "verifier is invalid",
|
return nil, &TokenError{
|
||||||
}, nil
|
Error: InvalidGrant,
|
||||||
|
ErrorDescription: fmt.Sprintf("verifier is invalid, challengeAnswer: [%s], token.CodeChallenge: [%s]", challengeAnswer, token.CodeChallenge),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if application.ClientSecret != clientSecret {
|
if application.ClientSecret != clientSecret {
|
||||||
@ -442,13 +456,13 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
|||||||
if token.CodeChallenge == "" {
|
if token.CodeChallenge == "" {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidClient,
|
Error: InvalidClient,
|
||||||
ErrorDescription: "client_secret is invalid",
|
ErrorDescription: fmt.Sprintf("client_secret is invalid for application: [%s], token.CodeChallenge: empty", application.GetId()),
|
||||||
}, nil
|
}, nil
|
||||||
} else {
|
} else {
|
||||||
if clientSecret != "" {
|
if clientSecret != "" {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidClient,
|
Error: InvalidClient,
|
||||||
ErrorDescription: "client_secret is invalid",
|
ErrorDescription: fmt.Sprintf("client_secret is invalid for application: [%s], token.CodeChallenge: [%s]", application.GetId(), token.CodeChallenge),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -457,15 +471,16 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
|
|||||||
if application.Name != token.Application {
|
if application.Name != token.Application {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "the token is for wrong application (client_id)",
|
ErrorDescription: fmt.Sprintf("the token is for wrong application (client_id), application.Name: [%s], token.Application: [%s]", application.Name, token.Application),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().Unix() > token.CodeExpireIn {
|
nowUnix := time.Now().Unix()
|
||||||
|
if nowUnix > token.CodeExpireIn {
|
||||||
// code must be used within 5 minutes
|
// code must be used within 5 minutes
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
ErrorDescription: "authorization code has expired",
|
ErrorDescription: fmt.Sprintf("authorization code has expired, nowUnix: [%s], token.CodeExpireIn: [%s]", time.Unix(nowUnix, 0).Format(time.RFC3339), time.Unix(token.CodeExpireIn, 0).Format(time.RFC3339)),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return token, nil, nil
|
return token, nil, nil
|
||||||
|
121
object/token_standard_jwt.go
Normal file
121
object/token_standard_jwt.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
// Copyright 2024 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClaimsStandard struct {
|
||||||
|
*UserShort
|
||||||
|
EmailVerified bool `json:"email_verified,omitempty"`
|
||||||
|
PhoneNumber string `json:"phone_number,omitempty"`
|
||||||
|
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"`
|
||||||
|
Gender string `json:"gender,omitempty"`
|
||||||
|
TokenType string `json:"tokenType,omitempty"`
|
||||||
|
Nonce string `json:"nonce,omitempty"`
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
|
Address OIDCAddress `json:"address,omitempty"`
|
||||||
|
|
||||||
|
jwt.RegisteredClaims
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStreetAddress(user *User) string {
|
||||||
|
var addrs string
|
||||||
|
for _, addr := range user.Address {
|
||||||
|
addrs += addr + "\n"
|
||||||
|
}
|
||||||
|
return addrs
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStandardClaims(claims Claims) ClaimsStandard {
|
||||||
|
res := ClaimsStandard{
|
||||||
|
UserShort: getShortUser(claims.User),
|
||||||
|
EmailVerified: claims.User.EmailVerified,
|
||||||
|
TokenType: claims.TokenType,
|
||||||
|
Nonce: claims.Nonce,
|
||||||
|
Scope: claims.Scope,
|
||||||
|
RegisteredClaims: claims.RegisteredClaims,
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Phone = ""
|
||||||
|
var scopes []string
|
||||||
|
|
||||||
|
if strings.Contains(claims.Scope, ",") {
|
||||||
|
scopes = strings.Split(claims.Scope, ",")
|
||||||
|
} else {
|
||||||
|
scopes = strings.Split(claims.Scope, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, scope := range scopes {
|
||||||
|
if scope == "address" {
|
||||||
|
res.Address = OIDCAddress{StreetAddress: getStreetAddress(claims.User)}
|
||||||
|
} else if scope == "profile" {
|
||||||
|
res.Gender = claims.User.Gender
|
||||||
|
} else if scope == "phone" && claims.User.Phone != "" {
|
||||||
|
res.PhoneNumberVerified = true
|
||||||
|
phoneNumber, ok := util.GetE164Number(claims.User.Phone, claims.User.CountryCode)
|
||||||
|
if !ok {
|
||||||
|
res.PhoneNumberVerified = false
|
||||||
|
} else {
|
||||||
|
res.PhoneNumber = phoneNumber
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseStandardJwtToken(token string, cert *Cert) (*ClaimsStandard, error) {
|
||||||
|
t, err := jwt.ParseWithClaims(token, &ClaimsStandard{}, func(token *jwt.Token) (interface{}, error) {
|
||||||
|
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
|
||||||
|
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||||
|
}
|
||||||
|
|
||||||
|
if cert.Certificate == "" {
|
||||||
|
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RSA certificate
|
||||||
|
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return certificate, nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if t != nil {
|
||||||
|
if claims, ok := t.Claims.(*ClaimsStandard); ok && t.Valid {
|
||||||
|
return claims, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseStandardJwtTokenByApplication(token string, application *Application) (*ClaimsStandard, error) {
|
||||||
|
cert, err := getCertByApplication(application)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseStandardJwtToken(token, cert)
|
||||||
|
}
|
@ -17,6 +17,7 @@ package object
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/pp"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
@ -43,7 +44,7 @@ type Transaction struct {
|
|||||||
Application string `xorm:"varchar(100)" json:"application"`
|
Application string `xorm:"varchar(100)" json:"application"`
|
||||||
Payment string `xorm:"varchar(100)" json:"payment"`
|
Payment string `xorm:"varchar(100)" json:"payment"`
|
||||||
|
|
||||||
State string `xorm:"varchar(100)" json:"state"`
|
State pp.PaymentState `xorm:"varchar(100)" json:"state"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetTransactionCount(owner, field, value string) (int64, error) {
|
func GetTransactionCount(owner, field, value string) (int64, error) {
|
||||||
|
@ -204,6 +204,7 @@ type User struct {
|
|||||||
SigninWrongTimes int `json:"signinWrongTimes"`
|
SigninWrongTimes int `json:"signinWrongTimes"`
|
||||||
|
|
||||||
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
|
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
|
||||||
|
MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"`
|
||||||
NeedUpdatePassword bool `json:"needUpdatePassword"`
|
NeedUpdatePassword bool `json:"needUpdatePassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,6 +231,12 @@ type ManagedAccount struct {
|
|||||||
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MfaAccount struct {
|
||||||
|
AccountName string `xorm:"varchar(100)" json:"accountName"`
|
||||||
|
Issuer string `xorm:"varchar(100)" json:"issuer"`
|
||||||
|
SecretKey string `xorm:"varchar(100)" json:"secretKey"`
|
||||||
|
}
|
||||||
|
|
||||||
type FaceId struct {
|
type FaceId struct {
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
FaceIdData []float64 `json:"faceIdData"`
|
FaceIdData []float64 `json:"faceIdData"`
|
||||||
@ -603,6 +610,12 @@ func GetMaskedUser(user *User, isAdminOrSelf bool, errs ...error) (*User, error)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.MfaAccounts != nil {
|
||||||
|
for _, mfaAccount := range user.MfaAccounts {
|
||||||
|
mfaAccount.SecretKey = "***"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if user.TotpSecret != "" {
|
if user.TotpSecret != "" {
|
||||||
user.TotpSecret = ""
|
user.TotpSecret = ""
|
||||||
}
|
}
|
||||||
@ -675,7 +688,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
columns = []string{
|
columns = []string{
|
||||||
"owner", "display_name", "avatar", "first_name", "last_name",
|
"owner", "display_name", "avatar", "first_name", "last_name",
|
||||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||||
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids",
|
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids", "mfaAccounts",
|
||||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled",
|
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled",
|
||||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||||
@ -1125,7 +1138,7 @@ func (user *User) IsApplicationAdmin(application *Application) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin()
|
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin() || (user.IsAdmin && application.IsShared)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) IsGlobalAdmin() bool {
|
func (user *User) IsGlobalAdmin() bool {
|
||||||
@ -1158,7 +1171,7 @@ func GenerateIdForNewUser(application *Application) (string, error) {
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateUserBalance(owner string, name string, balance float64) error {
|
func UpdateUserBalance(owner string, name string, balance float64) error {
|
||||||
user, err := getUser(owner, name)
|
user, err := getUser(owner, name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -393,6 +393,20 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
|||||||
itemsChanged = append(itemsChanged, item)
|
itemsChanged = append(itemsChanged, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldUser.Address == nil {
|
||||||
|
oldUser.Address = []string{}
|
||||||
|
}
|
||||||
|
oldUserAddressJson, _ := json.Marshal(oldUser.Address)
|
||||||
|
|
||||||
|
if newUser.Address == nil {
|
||||||
|
newUser.Address = []string{}
|
||||||
|
}
|
||||||
|
newUserAddressJson, _ := json.Marshal(newUser.Address)
|
||||||
|
if string(oldUserAddressJson) != string(newUserAddressJson) {
|
||||||
|
item := GetAccountItemByName("Address", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
if newUser.FaceIds != nil {
|
if newUser.FaceIds != nil {
|
||||||
item := GetAccountItemByName("Face ID", organization)
|
item := GetAccountItemByName("Face ID", organization)
|
||||||
itemsChanged = append(itemsChanged, item)
|
itemsChanged = append(itemsChanged, item)
|
||||||
@ -426,6 +440,31 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
|||||||
itemsChanged = append(itemsChanged, item)
|
itemsChanged = append(itemsChanged, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if oldUser.Karma != newUser.Karma {
|
||||||
|
item := GetAccountItemByName("Karma", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldUser.Language != newUser.Language {
|
||||||
|
item := GetAccountItemByName("Language", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldUser.Ranking != newUser.Ranking {
|
||||||
|
item := GetAccountItemByName("Ranking", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldUser.Currency != newUser.Currency {
|
||||||
|
item := GetAccountItemByName("Currency", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
if oldUser.Hash != newUser.Hash {
|
||||||
|
item := GetAccountItemByName("Hash", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
for _, accountItem := range itemsChanged {
|
for _, accountItem := range itemsChanged {
|
||||||
|
|
||||||
if pass, err := CheckAccountItemModifyRule(accountItem, isAdmin, lang); !pass {
|
if pass, err := CheckAccountItemModifyRule(accountItem, isAdmin, lang); !pass {
|
||||||
|
50
pp/balance.go
Normal file
50
pp/balance.go
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
// Copyright 2024 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 pp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BalancePaymentProvider struct{}
|
||||||
|
|
||||||
|
func NewBalancePaymentProvider() (*BalancePaymentProvider, error) {
|
||||||
|
pp := &BalancePaymentProvider{}
|
||||||
|
return pp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *BalancePaymentProvider) Pay(r *PayReq) (*PayResp, error) {
|
||||||
|
owner, _ := util.GetOwnerAndNameFromId(r.PayerId)
|
||||||
|
return &PayResp{
|
||||||
|
PayUrl: r.ReturnUrl,
|
||||||
|
OrderId: fmt.Sprintf("%s/%s", owner, r.PaymentName),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *BalancePaymentProvider) Notify(body []byte, orderId string) (*NotifyResult, error) {
|
||||||
|
return &NotifyResult{
|
||||||
|
PaymentStatus: PaymentStatePaid,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *BalancePaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *BalancePaymentProvider) GetResponseError(err error) string {
|
||||||
|
return ""
|
||||||
|
}
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/object"
|
"github.com/casdoor/casdoor/object"
|
||||||
@ -27,6 +28,14 @@ import (
|
|||||||
"layeh.com/radius/rfc2866"
|
"layeh.com/radius/rfc2866"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var StateMap map[string]AccessStateContent
|
||||||
|
|
||||||
|
const StateExpiredTime = time.Second * 120
|
||||||
|
|
||||||
|
type AccessStateContent struct {
|
||||||
|
ExpiredAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
func StartRadiusServer() {
|
func StartRadiusServer() {
|
||||||
secret := conf.GetConfigString("radiusSecret")
|
secret := conf.GetConfigString("radiusSecret")
|
||||||
server := radius.PacketServer{
|
server := radius.PacketServer{
|
||||||
@ -55,6 +64,7 @@ func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
|
|||||||
username := rfc2865.UserName_GetString(r.Packet)
|
username := rfc2865.UserName_GetString(r.Packet)
|
||||||
password := rfc2865.UserPassword_GetString(r.Packet)
|
password := rfc2865.UserPassword_GetString(r.Packet)
|
||||||
organization := rfc2865.Class_GetString(r.Packet)
|
organization := rfc2865.Class_GetString(r.Packet)
|
||||||
|
state := rfc2865.State_GetString(r.Packet)
|
||||||
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
|
log.Printf("handleAccessRequest() username=%v, org=%v, password=%v", username, organization, password)
|
||||||
|
|
||||||
if organization == "" {
|
if organization == "" {
|
||||||
@ -62,12 +72,75 @@ func handleAccessRequest(w radius.ResponseWriter, r *radius.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := object.CheckUserPassword(organization, username, password, "en")
|
var user *object.User
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if state == "" {
|
||||||
|
user, err = object.CheckUserPassword(organization, username, password, "en")
|
||||||
|
} else {
|
||||||
|
user, err = object.GetUser(fmt.Sprintf("%s/%s", organization, username))
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
w.Write(r.Response(radius.CodeAccessReject))
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.IsMfaEnabled() {
|
||||||
|
mfaProp := user.GetMfaProps(object.TotpType, false)
|
||||||
|
if mfaProp == nil {
|
||||||
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if StateMap == nil {
|
||||||
|
StateMap = map[string]AccessStateContent{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if state != "" {
|
||||||
|
stateContent, ok := StateMap[state]
|
||||||
|
if !ok {
|
||||||
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(StateMap, state)
|
||||||
|
if stateContent.ExpiredAt.Before(time.Now()) {
|
||||||
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
mfaUtil := object.GetMfaUtil(mfaProp.MfaType, mfaProp)
|
||||||
|
if mfaUtil.Verify(password) != nil {
|
||||||
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(r.Response(radius.CodeAccessAccept))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
responseState := util.GenerateId()
|
||||||
|
StateMap[responseState] = AccessStateContent{
|
||||||
|
time.Now().Add(StateExpiredTime),
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rfc2865.State_Set(r.Packet, []byte(responseState))
|
||||||
|
if err != nil {
|
||||||
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = rfc2865.ReplyMessage_Set(r.Packet, []byte("please enter OTP"))
|
||||||
|
if err != nil {
|
||||||
|
w.Write(r.Response(radius.CodeAccessReject))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r.Packet.Code = radius.CodeAccessChallenge
|
||||||
|
w.Write(r.Packet)
|
||||||
|
}
|
||||||
|
|
||||||
w.Write(r.Response(radius.CodeAccessAccept))
|
w.Write(r.Response(radius.CodeAccessAccept))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ func getSubject(ctx *context.Context) (string, string) {
|
|||||||
return util.GetOwnerAndNameFromId(username)
|
return util.GetOwnerAndNameFromId(username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getObject(ctx *context.Context) (string, string) {
|
func getObject(ctx *context.Context) (string, string, error) {
|
||||||
method := ctx.Request.Method
|
method := ctx.Request.Method
|
||||||
path := ctx.Request.URL.Path
|
path := ctx.Request.URL.Path
|
||||||
|
|
||||||
@ -65,13 +65,13 @@ func getObject(ctx *context.Context) (string, string) {
|
|||||||
if ctx.Input.Query("id") == "/" {
|
if ctx.Input.Query("id") == "/" {
|
||||||
adapterId := ctx.Input.Query("adapterId")
|
adapterId := ctx.Input.Query("adapterId")
|
||||||
if adapterId != "" {
|
if adapterId != "" {
|
||||||
return util.GetOwnerAndNameFromIdNoCheck(adapterId)
|
return util.GetOwnerAndNameFromIdWithError(adapterId)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 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 util.GetOwnerAndNameFromIdNoCheck(id)
|
return util.GetOwnerAndNameFromIdWithError(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,34 +80,34 @@ func getObject(ctx *context.Context) (string, string) {
|
|||||||
// 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 util.GetOwnerAndNameFromIdNoCheck(id)
|
return util.GetOwnerAndNameFromIdWithError(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := ctx.Input.Query("owner")
|
owner := ctx.Input.Query("owner")
|
||||||
if owner != "" {
|
if owner != "" {
|
||||||
return owner, ""
|
return owner, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", ""
|
return "", "", nil
|
||||||
} else {
|
} else {
|
||||||
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" {
|
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" {
|
||||||
id := ctx.Input.Query("id")
|
id := ctx.Input.Query("id")
|
||||||
if id != "" {
|
if id != "" {
|
||||||
return util.GetOwnerAndNameFromIdNoCheck(id)
|
return util.GetOwnerAndNameFromIdWithError(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body := ctx.Input.RequestBody
|
body := ctx.Input.RequestBody
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return ctx.Request.Form.Get("owner"), ctx.Request.Form.Get("name")
|
return ctx.Request.Form.Get("owner"), ctx.Request.Form.Get("name"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var obj Object
|
var obj Object
|
||||||
err := json.Unmarshal(body, &obj)
|
err := json.Unmarshal(body, &obj)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// panic(err)
|
// this is not error
|
||||||
return "", ""
|
return "", "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if path == "/api/delete-resource" {
|
if path == "/api/delete-resource" {
|
||||||
@ -117,7 +117,7 @@ func getObject(ctx *context.Context) (string, string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj.Owner, obj.Name
|
return obj.Owner, obj.Name, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +183,12 @@ func ApiFilter(ctx *context.Context) {
|
|||||||
|
|
||||||
objOwner, objName := "", ""
|
objOwner, objName := "", ""
|
||||||
if urlPath != "/api/get-app-login" && urlPath != "/api/get-resource" {
|
if urlPath != "/api/get-app-login" && urlPath != "/api/get-resource" {
|
||||||
objOwner, objName = getObject(ctx)
|
var err error
|
||||||
|
objOwner, objName, err = getObject(ctx)
|
||||||
|
if err != nil {
|
||||||
|
responseError(ctx, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.HasPrefix(urlPath, "/api/notify-payment") {
|
if strings.HasPrefix(urlPath, "/api/notify-payment") {
|
||||||
|
@ -67,6 +67,17 @@ func AutoSigninFilter(ctx *context.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accessKey := ctx.Input.Query("accessKey")
|
||||||
|
accessSecret := ctx.Input.Query("accessSecret")
|
||||||
|
if accessKey != "" && accessSecret != "" {
|
||||||
|
userId, err := getUsernameByKeys(ctx)
|
||||||
|
if err != nil {
|
||||||
|
responseError(ctx, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
setSessionUser(ctx, userId)
|
||||||
|
}
|
||||||
|
|
||||||
// "/page?clientId=123&clientSecret=456"
|
// "/page?clientId=123&clientSecret=456"
|
||||||
userId, err := getUsernameByClientIdSecret(ctx)
|
userId, err := getUsernameByClientIdSecret(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -58,7 +58,7 @@ func fastAutoSignin(ctx *context.Context) (string, error) {
|
|||||||
redirectUri := ctx.Input.Query("redirect_uri")
|
redirectUri := ctx.Input.Query("redirect_uri")
|
||||||
scope := ctx.Input.Query("scope")
|
scope := ctx.Input.Query("scope")
|
||||||
state := ctx.Input.Query("state")
|
state := ctx.Input.Query("state")
|
||||||
nonce := ""
|
nonce := ctx.Input.Query("nonce")
|
||||||
codeChallenge := ctx.Input.Query("code_challenge")
|
codeChallenge := ctx.Input.Query("code_challenge")
|
||||||
if clientId == "" || responseType != "code" || redirectUri == "" {
|
if clientId == "" || responseType != "code" || redirectUri == "" {
|
||||||
return "", nil
|
return "", nil
|
||||||
|
19
storage/casdoor.go
Normal file
19
storage/casdoor.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package storage
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casdoor/oss"
|
||||||
|
"github.com/casdoor/oss/casdoor"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewCasdoorStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string, cert string, content string) oss.StorageInterface {
|
||||||
|
sp := casdoor.New(&casdoor.Config{
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
endpoint,
|
||||||
|
cert,
|
||||||
|
region,
|
||||||
|
content,
|
||||||
|
bucket,
|
||||||
|
})
|
||||||
|
return sp
|
||||||
|
}
|
@ -16,7 +16,7 @@ package storage
|
|||||||
|
|
||||||
import "github.com/casdoor/oss"
|
import "github.com/casdoor/oss"
|
||||||
|
|
||||||
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
|
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string, cert string, content string) (oss.StorageInterface, error) {
|
||||||
switch providerType {
|
switch providerType {
|
||||||
case "Local File System":
|
case "Local File System":
|
||||||
return NewLocalFileSystemStorageProvider(), nil
|
return NewLocalFileSystemStorageProvider(), nil
|
||||||
@ -36,6 +36,8 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
|
|||||||
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil
|
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil
|
||||||
case "Synology":
|
case "Synology":
|
||||||
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
|
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
|
||||||
|
case "Casdoor":
|
||||||
|
return NewCasdoorStorageProvider(providerType, clientId, clientSecret, region, bucket, endpoint, cert, content), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -131,6 +131,15 @@ func GetOwnerAndNameFromId(id string) (string, string) {
|
|||||||
return tokens[0], tokens[1]
|
return tokens[0], tokens[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetOwnerAndNameFromIdWithError(id string) (string, string, error) {
|
||||||
|
tokens := strings.Split(id, "/")
|
||||||
|
if len(tokens) != 2 {
|
||||||
|
return "", "", errors.New("GetOwnerAndNameFromId() error, wrong token count for ID: " + id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tokens[0], tokens[1], nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetOwnerFromId(id string) string {
|
func GetOwnerFromId(id string) string {
|
||||||
tokens := strings.Split(id, "/")
|
tokens := strings.Split(id, "/")
|
||||||
if len(tokens) != 2 {
|
if len(tokens) != 2 {
|
||||||
@ -154,6 +163,16 @@ func GetOwnerAndNameAndOtherFromId(id string) (string, string, string) {
|
|||||||
return tokens[0], tokens[1], tokens[2]
|
return tokens[0], tokens[1], tokens[2]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSharedOrgFromApp(rawName string) (name string, organization string) {
|
||||||
|
name = rawName
|
||||||
|
splitName := strings.Split(rawName, "-org-")
|
||||||
|
if len(splitName) >= 2 {
|
||||||
|
organization = splitName[len(splitName)-1]
|
||||||
|
name = splitName[0]
|
||||||
|
}
|
||||||
|
return name, organization
|
||||||
|
}
|
||||||
|
|
||||||
func GenerateId() string {
|
func GenerateId() string {
|
||||||
return uuid.NewString()
|
return uuid.NewString()
|
||||||
}
|
}
|
||||||
@ -354,9 +373,16 @@ func StringToInterfaceArray(array []string) []interface{} {
|
|||||||
func StringToInterfaceArray2d(arrays [][]string) [][]interface{} {
|
func StringToInterfaceArray2d(arrays [][]string) [][]interface{} {
|
||||||
var interfaceArrays [][]interface{}
|
var interfaceArrays [][]interface{}
|
||||||
for _, req := range arrays {
|
for _, req := range arrays {
|
||||||
var interfaceArray []interface{}
|
var (
|
||||||
for _, r := range req {
|
interfaceArray []interface{}
|
||||||
interfaceArray = append(interfaceArray, r)
|
elem interface{}
|
||||||
|
)
|
||||||
|
for _, elem = range req {
|
||||||
|
jStruct, err := TryJsonToAnonymousStruct(elem.(string))
|
||||||
|
if err == nil {
|
||||||
|
elem = jStruct
|
||||||
|
}
|
||||||
|
interfaceArray = append(interfaceArray, elem)
|
||||||
}
|
}
|
||||||
interfaceArrays = append(interfaceArrays, interfaceArray)
|
interfaceArrays = append(interfaceArrays, interfaceArray)
|
||||||
}
|
}
|
||||||
|
@ -252,8 +252,8 @@ class AdapterEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("provider:DB test"), i18next.t("provider:DB test - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:DB test"), i18next.t("provider:DB test - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={2} >
|
<Col span={2} >
|
||||||
<Button type={"primary"} onClick={() => {
|
<Button disabled={this.state.organizationName !== this.state.adapter.owner} type={"primary"} onClick={() => {
|
||||||
AdapterBackend.getPolicies("", "", `${this.state.organizationName}/${this.state.adapterName}`)
|
AdapterBackend.getPolicies("", "", `${this.state.adapter.owner}/${this.state.adapter.name}`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("syncer:Connect successfully"));
|
Setting.showMessage("success", i18next.t("syncer:Connect successfully"));
|
||||||
@ -279,13 +279,14 @@ class AdapterEditPage extends React.Component {
|
|||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
|
organizationName: this.state.adapter.owner,
|
||||||
adapterName: this.state.adapter.name,
|
adapterName: this.state.adapter.name,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exitAfterSave) {
|
if (exitAfterSave) {
|
||||||
this.props.history.push("/adapters");
|
this.props.history.push("/adapters");
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(`/adapters/${this.state.organizationName}/${this.state.adapter.name}`);
|
this.props.history.push(`/adapters/${this.state.adapter.owner}/${this.state.adapter.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
@ -56,9 +56,11 @@ class AdapterListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -16,6 +16,7 @@ import React, {Component, Suspense, lazy} 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 {setOrgIsTourVisible, setTourLogo} from "./TourConfig";
|
||||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||||
import {GithubOutlined, InfoCircleFilled, ShareAltOutlined} from "@ant-design/icons";
|
import {GithubOutlined, InfoCircleFilled, ShareAltOutlined} from "@ant-design/icons";
|
||||||
import {Alert, Button, ConfigProvider, Drawer, FloatButton, Layout, Result, Tooltip} from "antd";
|
import {Alert, Button, ConfigProvider, Drawer, FloatButton, Layout, Result, Tooltip} from "antd";
|
||||||
@ -247,6 +248,8 @@ class App extends Component {
|
|||||||
|
|
||||||
this.setLanguage(account);
|
this.setLanguage(account);
|
||||||
this.setTheme(Setting.getThemeData(account.organization), Conf.InitThemeAlgorithm);
|
this.setTheme(Setting.getThemeData(account.organization), Conf.InitThemeAlgorithm);
|
||||||
|
setTourLogo(account.organization.logo);
|
||||||
|
setOrgIsTourVisible(account.organization.enableTour);
|
||||||
} else {
|
} else {
|
||||||
if (res.data !== "Please login first") {
|
if (res.data !== "Please login first") {
|
||||||
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("application:Failed to sign in")}: ${res.msg}`);
|
||||||
@ -341,7 +344,8 @@ class App extends Component {
|
|||||||
window.location.pathname.startsWith("/cas") ||
|
window.location.pathname.startsWith("/cas") ||
|
||||||
window.location.pathname.startsWith("/select-plan") ||
|
window.location.pathname.startsWith("/select-plan") ||
|
||||||
window.location.pathname.startsWith("/buy-plan") ||
|
window.location.pathname.startsWith("/buy-plan") ||
|
||||||
window.location.pathname.startsWith("/qrcode") ;
|
window.location.pathname.startsWith("/qrcode") ||
|
||||||
|
window.location.pathname.startsWith("/captcha");
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick = ({key}) => {
|
onClick = ({key}) => {
|
||||||
|
@ -116,7 +116,6 @@ class ApplicationEditPage extends React.Component {
|
|||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
this.getApplication();
|
this.getApplication();
|
||||||
this.getOrganizations();
|
this.getOrganizations();
|
||||||
this.getProviders();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getApplication() {
|
getApplication() {
|
||||||
@ -145,7 +144,9 @@ class ApplicationEditPage extends React.Component {
|
|||||||
application: application,
|
application: application,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getCerts(application.organization);
|
this.getProviders(application);
|
||||||
|
|
||||||
|
this.getCerts(application);
|
||||||
|
|
||||||
this.getSamlMetadata(application.enableSamlPostBinding);
|
this.getSamlMetadata(application.enableSamlPostBinding);
|
||||||
});
|
});
|
||||||
@ -166,7 +167,11 @@ class ApplicationEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getCerts(owner) {
|
getCerts(application) {
|
||||||
|
let owner = application.organization;
|
||||||
|
if (application.isShared) {
|
||||||
|
owner = this.props.owner;
|
||||||
|
}
|
||||||
CertBackend.getCerts(owner)
|
CertBackend.getCerts(owner)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -175,8 +180,12 @@ class ApplicationEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getProviders() {
|
getProviders(application) {
|
||||||
ProviderBackend.getProviders(this.state.owner)
|
let owner = application.organization;
|
||||||
|
if (application.isShared) {
|
||||||
|
owner = this.props.account.owner;
|
||||||
|
}
|
||||||
|
ProviderBackend.getProviders(owner)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -263,6 +272,16 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Is shared"), i18next.t("general:Is shared - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Switch disabled={Setting.isAdminUser()} checked={this.state.application.isShared} onChange={checked => {
|
||||||
|
this.updateApplicationField("isShared", checked);
|
||||||
|
}} />
|
||||||
|
</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}>
|
||||||
{Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} :
|
||||||
@ -384,7 +403,17 @@ class ApplicationEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}
|
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenFormat} onChange={(value => {this.updateApplicationField("tokenFormat", value);})}
|
||||||
options={["JWT", "JWT-Empty", "JWT-Custom"].map((item) => Setting.getOption(item, item))}
|
options={["JWT", "JWT-Empty", "JWT-Custom", "JWT-Standard"].map((item) => Setting.getOption(item, item))}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("application:Token signing method"), i18next.t("application:Token signing method - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenSigningMethod === "" ? "RS256" : this.state.application.tokenSigningMethod} onChange={(value => {this.updateApplicationField("tokenSigningMethod", value);})}
|
||||||
|
options={["RS256", "RS512", "ES256", "ES512", "ES384"].map((item) => Setting.getOption(item, item))}
|
||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -989,7 +1018,11 @@ class ApplicationEditPage extends React.Component {
|
|||||||
redirectUri = "\"ERROR: You must specify at least one Redirect URL in 'Redirect URLs'\"";
|
redirectUri = "\"ERROR: You must specify at least one Redirect URL in 'Redirect URLs'\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
|
let clientId = this.state.application.clientId;
|
||||||
|
if (this.state.application.isShared) {
|
||||||
|
clientId += `-org-${this.props.account.owner}`;
|
||||||
|
}
|
||||||
|
const signInUrl = `/login/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
|
||||||
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
|
||||||
if (!Setting.isPasswordEnabled(this.state.application)) {
|
if (!Setting.isPasswordEnabled(this.state.application)) {
|
||||||
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
|
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");
|
||||||
|
@ -97,9 +97,11 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
@ -123,7 +125,7 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/applications/${record.organization}/${text}`}>
|
<Link to={`/applications/${record.organization}/${text}`}>
|
||||||
{text}
|
{Setting.getApplicationDisplayName(record)}
|
||||||
</Link>
|
</Link>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
116
web/src/CaptchaPage.js
Normal file
116
web/src/CaptchaPage.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {CaptchaModal} from "./common/modal/CaptchaModal";
|
||||||
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
|
||||||
|
class CaptchaPage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const params = new URLSearchParams(this.props.location.search);
|
||||||
|
this.state = {
|
||||||
|
owner: "admin",
|
||||||
|
application: null,
|
||||||
|
clientId: params.get("client_id"),
|
||||||
|
applicationName: params.get("state"),
|
||||||
|
redirectUri: params.get("redirect_uri"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getApplication();
|
||||||
|
}
|
||||||
|
|
||||||
|
onUpdateApplication(application) {
|
||||||
|
this.setState({
|
||||||
|
application: application,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getApplication() {
|
||||||
|
if (this.state.applicationName === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplicationBackend.getApplication(this.state.owner, this.state.applicationName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "error") {
|
||||||
|
this.onUpdateApplication(null);
|
||||||
|
this.setState({
|
||||||
|
msg: res.msg,
|
||||||
|
});
|
||||||
|
return ;
|
||||||
|
}
|
||||||
|
this.onUpdateApplication(res.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getCaptchaProviderItems(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";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(values) {
|
||||||
|
Setting.goToLink(`${this.state.redirectUri}?code=${values.captchaToken}&type=${values.captchaType}&secret=${values.clientSecret}&applicationId=${values.applicationId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCaptchaModal(application) {
|
||||||
|
const captchaProviderItems = this.getCaptchaProviderItems(application);
|
||||||
|
if (captchaProviderItems === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const alwaysProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Always");
|
||||||
|
const dynamicProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Dynamic");
|
||||||
|
const provider = alwaysProviderItems.length > 0
|
||||||
|
? alwaysProviderItems[0].provider
|
||||||
|
: dynamicProviderItems[0].provider;
|
||||||
|
|
||||||
|
return <CaptchaModal
|
||||||
|
owner={provider.owner}
|
||||||
|
name={provider.name}
|
||||||
|
visible={true}
|
||||||
|
onOk={(captchaType, captchaToken, clientSecret) => {
|
||||||
|
const values = {
|
||||||
|
captchaType: captchaType,
|
||||||
|
captchaToken: captchaToken,
|
||||||
|
clientSecret: clientSecret,
|
||||||
|
applicationId: `${provider.owner}/${provider.name}`,
|
||||||
|
};
|
||||||
|
this.callback(values);
|
||||||
|
}}
|
||||||
|
onCancel={() => this.callback({captchaType: "none", captchaToken: "", clientSecret: ""})}
|
||||||
|
isCurrentProvider={true}
|
||||||
|
/>;
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
this.renderCaptchaModal(this.state.application)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CaptchaPage;
|
@ -288,14 +288,14 @@ class CertEditPage extends React.Component {
|
|||||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
this.setState({
|
this.setState({
|
||||||
certName: this.state.cert.name,
|
certName: this.state.cert.name,
|
||||||
|
}, () => {
|
||||||
|
if (exitAfterSave) {
|
||||||
|
this.props.history.push("/certs");
|
||||||
|
} else {
|
||||||
|
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
|
||||||
|
this.getCert();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (exitAfterSave) {
|
|
||||||
this.props.history.push("/certs");
|
|
||||||
} else {
|
|
||||||
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
|
|
||||||
this.getCert();
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
this.updateCertField("name", this.state.certName);
|
this.updateCertField("name", this.state.certName);
|
||||||
|
@ -73,9 +73,11 @@ class CertListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -55,9 +55,11 @@ class EnforcerListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -32,6 +32,7 @@ import {authConfig} from "./auth/Auth";
|
|||||||
import ProductBuyPage from "./ProductBuyPage";
|
import ProductBuyPage from "./ProductBuyPage";
|
||||||
import PaymentResultPage from "./PaymentResultPage";
|
import PaymentResultPage from "./PaymentResultPage";
|
||||||
import QrCodePage from "./QrCodePage";
|
import QrCodePage from "./QrCodePage";
|
||||||
|
import CaptchaPage from "./CaptchaPage";
|
||||||
import CustomHead from "./basic/CustomHead";
|
import CustomHead from "./basic/CustomHead";
|
||||||
|
|
||||||
class EntryPage extends React.Component {
|
class EntryPage extends React.Component {
|
||||||
@ -120,6 +121,7 @@ class EntryPage extends React.Component {
|
|||||||
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
||||||
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
|
||||||
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
|
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
|
||||||
|
<Route exact path="/captcha" render={(props) => <CaptchaPage {...props} />} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
|
@ -84,9 +84,11 @@ class GroupListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -199,7 +199,7 @@ class InvitationEditPage extends React.Component {
|
|||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.application}
|
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.application}
|
||||||
onChange={(value => {this.updateInvitationField("application", value);})}
|
onChange={(value => {this.updateInvitationField("application", value);})}
|
||||||
options={[
|
options={[
|
||||||
{label: "All", value: i18next.t("general:All")},
|
{label: i18next.t("general:All"), value: "All"},
|
||||||
...this.state.applications.map((application) => Setting.getOption(application.name, application.name)),
|
...this.state.applications.map((application) => Setting.getOption(application.name, application.name)),
|
||||||
]} />
|
]} />
|
||||||
</Col>
|
</Col>
|
||||||
|
@ -68,9 +68,11 @@ class InvitationListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -13,12 +13,13 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
|
import {Button, Card, Col, Input, InputNumber, Row, Select, Space, Switch} from "antd";
|
||||||
import {EyeInvisibleOutlined, EyeTwoTone} from "@ant-design/icons";
|
import {EyeInvisibleOutlined, EyeTwoTone, HolderOutlined, UsergroupAddOutlined} from "@ant-design/icons";
|
||||||
import * as LddpBackend from "./backend/LdapBackend";
|
import * as LddpBackend from "./backend/LdapBackend";
|
||||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
@ -30,12 +31,14 @@ class LdapEditPage extends React.Component {
|
|||||||
organizationName: props.match.params.organizationName,
|
organizationName: props.match.params.organizationName,
|
||||||
ldap: null,
|
ldap: null,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
|
groups: null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillMount() {
|
UNSAFE_componentWillMount() {
|
||||||
this.getLdap();
|
this.getLdap();
|
||||||
this.getOrganizations();
|
this.getOrganizations();
|
||||||
|
this.getGroups();
|
||||||
}
|
}
|
||||||
|
|
||||||
getLdap() {
|
getLdap() {
|
||||||
@ -60,6 +63,17 @@ class LdapEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getGroups() {
|
||||||
|
GroupBackend.getGroups(this.state.organizationName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
groups: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
updateLdapField(key, value) {
|
updateLdapField(key, value) {
|
||||||
this.setState((prevState) => {
|
this.setState((prevState) => {
|
||||||
prevState.ldap[key] = value;
|
prevState.ldap[key] = value;
|
||||||
@ -214,6 +228,31 @@ class LdapEditPage extends React.Component {
|
|||||||
/>
|
/>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
|
||||||
|
{Setting.getLabel(i18next.t("ldap:Default group"), i18next.t("ldap:Default group - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={21}>
|
||||||
|
<Select virtual={false} style={{width: "100%"}} value={this.state.ldap.defaultGroup ?? []} onChange={(value => {
|
||||||
|
this.updateLdapField("defaultGroup", value);
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<Option key={""} value={""}>
|
||||||
|
<Space>
|
||||||
|
{i18next.t("general:Default")}
|
||||||
|
</Space>
|
||||||
|
</Option>
|
||||||
|
{
|
||||||
|
this.state.groups?.map((group) => <Option key={group.name} value={`${group.owner}/${group.name}`}>
|
||||||
|
<Space>
|
||||||
|
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||||
|
{group.displayName}
|
||||||
|
</Space>
|
||||||
|
</Option>)
|
||||||
|
}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}}>
|
<Row style={{marginTop: "20px"}}>
|
||||||
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
|
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
|
||||||
{Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} :
|
{Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} :
|
||||||
|
@ -72,9 +72,11 @@ class ModelListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -360,7 +360,7 @@ class OrganizationEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}
|
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}
|
||||||
options={this.state.applications?.map((item) => Setting.getOption(item.name, item.name))
|
options={this.state.applications?.map((item) => Setting.getOption(Setting.getApplicationDisplayName(item.name), item.name))
|
||||||
} />
|
} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
@ -446,6 +446,16 @@ class OrganizationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Enable tour"), i18next.t("general:Enable tour - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={1} >
|
||||||
|
<Switch checked={this.state.organization.enableTour} onChange={checked => {
|
||||||
|
this.updateOrganizationField("enableTour", checked);
|
||||||
|
}} />
|
||||||
|
</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}>
|
||||||
{Setting.getLabel(i18next.t("organization:Account items"), i18next.t("organization:Account items - Tooltip"))} :
|
{Setting.getLabel(i18next.t("organization:Account items"), i18next.t("organization:Account items - Tooltip"))} :
|
||||||
|
@ -44,6 +44,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
defaultPassword: "",
|
defaultPassword: "",
|
||||||
enableSoftDeletion: false,
|
enableSoftDeletion: false,
|
||||||
isProfilePublic: true,
|
isProfilePublic: true,
|
||||||
|
enableTour: true,
|
||||||
accountItems: [
|
accountItems: [
|
||||||
{name: "Organization", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
{name: "Organization", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||||
{name: "ID", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
{name: "ID", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
@ -87,6 +88,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "WebAuthn credentials", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "Managed accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
|
{Name: "MFA accounts", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -113,11 +115,11 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
|
||||||
pagination: {
|
pagination: {
|
||||||
...this.state.pagination,
|
...this.state.pagination,
|
||||||
total: this.state.pagination.total - 1},
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
window.dispatchEvent(new Event("storageOrganizationsChanged"));
|
window.dispatchEvent(new Event("storageOrganizationsChanged"));
|
||||||
} else {
|
} else {
|
||||||
|
@ -70,9 +70,11 @@ class PaymentListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -122,7 +122,7 @@ class PaymentResultPage extends React.Component {
|
|||||||
payment: payment,
|
payment: payment,
|
||||||
});
|
});
|
||||||
if (payment.state === "Created") {
|
if (payment.state === "Created") {
|
||||||
if (["PayPal", "Stripe", "Alipay", "WeChat Pay"].includes(payment.type)) {
|
if (["PayPal", "Stripe", "Alipay", "WeChat Pay", "Balance"].includes(payment.type)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
timeout: setTimeout(async() => {
|
timeout: setTimeout(async() => {
|
||||||
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
await PaymentBackend.notifyPayment(this.state.owner, this.state.paymentName);
|
||||||
|
@ -69,9 +69,11 @@ class PermissionListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -63,9 +63,11 @@ class PlanListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -59,9 +59,11 @@ class PricingListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -65,9 +65,11 @@ class ProductListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -725,7 +725,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
(this.state.provider.category === "Web3") ||
|
(this.state.provider.category === "Web3") ||
|
||||||
(this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ||
|
(this.state.provider.category === "Storage" && this.state.provider.type === "Local File System") ||
|
||||||
(this.state.provider.category === "SMS" && this.state.provider.type === "Custom HTTP SMS") ||
|
(this.state.provider.category === "SMS" && this.state.provider.type === "Custom HTTP SMS") ||
|
||||||
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP")) ? null : (
|
(this.state.provider.category === "Notification" && (this.state.provider.type === "Google Chat" || this.state.provider.type === "Custom HTTP") || this.state.provider.type === "Balance") ? null : (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
{
|
{
|
||||||
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
|
(this.state.provider.category === "Storage" && this.state.provider.type === "Google Cloud Storage") ||
|
||||||
@ -843,7 +843,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
|
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && (this.state.provider.type !== "Casdoor" && this.state.category !== "Storage") && this.state.provider.type !== "Okta" ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||||
@ -870,7 +870,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
|
{["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
|
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
|
||||||
@ -885,7 +885,9 @@ class ProviderEditPage extends React.Component {
|
|||||||
{["Custom HTTP SMS", "Local File System"].includes(this.state.provider.type) ? null : (
|
{["Custom HTTP SMS", "Local File System"].includes(this.state.provider.type) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
|
{["Casdoor"].includes(this.state.provider.type) ?
|
||||||
|
Setting.getLabel(i18next.t("general:Provider"), i18next.t("provider:Provider - Tooltip"))
|
||||||
|
: Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.provider.bucket} onChange={e => {
|
<Input value={this.state.provider.bucket} onChange={e => {
|
||||||
@ -906,7 +908,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{["Custom HTTP SMS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
|
{["Custom HTTP SMS", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||||
@ -918,10 +920,24 @@ class ProviderEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
)}
|
)}
|
||||||
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? (
|
{["Casdoor"].includes(this.state.provider.type) ? (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={2}>
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.provider.content} onChange={e => {
|
||||||
|
this.updateProviderField("content", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
) : null}
|
||||||
|
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo", "Casdoor"].includes(this.state.provider.type) ? (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={2}>
|
||||||
|
{["Casdoor"].includes(this.state.provider.type) ?
|
||||||
|
Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip")) :
|
||||||
|
Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Input value={this.state.provider.regionId} onChange={e => {
|
<Input value={this.state.provider.regionId} onChange={e => {
|
||||||
@ -1298,7 +1314,7 @@ class ProviderEditPage extends React.Component {
|
|||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
(this.state.provider.type === "Alipay" || this.state.provider.type === "WeChat Pay") ? (
|
(this.state.provider.type === "Alipay" || this.state.provider.type === "WeChat Pay" || this.state.provider.type === "Casdoor") ? (
|
||||||
<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("general:Cert"), i18next.t("general:Cert - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Cert"), i18next.t("general:Cert - Tooltip"))} :
|
||||||
|
@ -76,9 +76,11 @@ class ProviderListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -40,9 +40,11 @@ class ResourceListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -61,9 +61,11 @@ class RoleListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -27,9 +27,11 @@ class SessionListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -56,6 +56,8 @@ export const Countries = [
|
|||||||
{label: "Українська", key: "uk", country: "UA", alt: "Українська"},
|
{label: "Українська", key: "uk", country: "UA", alt: "Українська"},
|
||||||
{label: "Қазақ", key: "kk", country: "KZ", alt: "Қазақ"},
|
{label: "Қазақ", key: "kk", country: "KZ", alt: "Қазақ"},
|
||||||
{label: "فارسی", key: "fa", country: "IR", alt: "فارسی"},
|
{label: "فارسی", key: "fa", country: "IR", alt: "فارسی"},
|
||||||
|
{label: "Čeština", key: "cs", country: "CZ", alt: "Čeština"},
|
||||||
|
{label: "Slovenčina", key: "sk", country: "SK", alt: "Slovenčina"},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function getThemeData(organization, application) {
|
export function getThemeData(organization, application) {
|
||||||
@ -227,6 +229,10 @@ export const OtherProviderInfo = {
|
|||||||
logo: `${StaticBaseUrl}/img/social_synology.png`,
|
logo: `${StaticBaseUrl}/img/social_synology.png`,
|
||||||
url: "https://www.synology.com/en-global/dsm/feature/file_sharing",
|
url: "https://www.synology.com/en-global/dsm/feature/file_sharing",
|
||||||
},
|
},
|
||||||
|
"Casdoor": {
|
||||||
|
logo: `${StaticBaseUrl}/img/casdoor.png`,
|
||||||
|
url: "https://casdoor.org/docs/provider/storage/overview",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
SAML: {
|
SAML: {
|
||||||
"Aliyun IDaaS": {
|
"Aliyun IDaaS": {
|
||||||
@ -247,6 +253,10 @@ export const OtherProviderInfo = {
|
|||||||
logo: `${StaticBaseUrl}/img/payment_paypal.png`,
|
logo: `${StaticBaseUrl}/img/payment_paypal.png`,
|
||||||
url: "",
|
url: "",
|
||||||
},
|
},
|
||||||
|
"Balance": {
|
||||||
|
logo: `${StaticBaseUrl}/img/payment_balance.svg`,
|
||||||
|
url: "",
|
||||||
|
},
|
||||||
"Alipay": {
|
"Alipay": {
|
||||||
logo: `${StaticBaseUrl}/img/payment_alipay.png`,
|
logo: `${StaticBaseUrl}/img/payment_alipay.png`,
|
||||||
url: "https://www.alipay.com/",
|
url: "https://www.alipay.com/",
|
||||||
@ -1056,6 +1066,7 @@ export function getProviderTypeOptions(category) {
|
|||||||
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
|
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
|
||||||
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
|
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
|
||||||
{id: "Synology", name: "Synology"},
|
{id: "Synology", name: "Synology"},
|
||||||
|
{id: "Casdoor", name: "Casdoor"},
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
} else if (category === "SAML") {
|
} else if (category === "SAML") {
|
||||||
@ -1067,6 +1078,7 @@ export function getProviderTypeOptions(category) {
|
|||||||
} else if (category === "Payment") {
|
} else if (category === "Payment") {
|
||||||
return ([
|
return ([
|
||||||
{id: "Dummy", name: "Dummy"},
|
{id: "Dummy", name: "Dummy"},
|
||||||
|
{id: "Balance", name: "Balance"},
|
||||||
{id: "Alipay", name: "Alipay"},
|
{id: "Alipay", name: "Alipay"},
|
||||||
{id: "WeChat Pay", name: "WeChat Pay"},
|
{id: "WeChat Pay", name: "WeChat Pay"},
|
||||||
{id: "PayPal", name: "PayPal"},
|
{id: "PayPal", name: "PayPal"},
|
||||||
@ -1364,6 +1376,13 @@ export function getApplicationName(application) {
|
|||||||
return `${application?.owner}/${application?.name}`;
|
return `${application?.owner}/${application?.name}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getApplicationDisplayName(application) {
|
||||||
|
if (application.isShared) {
|
||||||
|
return `${application.name}(Shared)`;
|
||||||
|
}
|
||||||
|
return application.name;
|
||||||
|
}
|
||||||
|
|
||||||
export function getRandomName() {
|
export function getRandomName() {
|
||||||
return Math.random().toString(36).slice(-6);
|
return Math.random().toString(36).slice(-6);
|
||||||
}
|
}
|
||||||
|
@ -64,9 +64,11 @@ class SubscriptionListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -69,9 +69,11 @@ class SyncerListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -61,9 +61,11 @@ class TokenListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -203,13 +203,24 @@ export function getNextUrl(pathName = window.location.pathname) {
|
|||||||
return TourUrlList[TourUrlList.indexOf(pathName.replace("/", "")) + 1] || "";
|
return TourUrlList[TourUrlList.indexOf(pathName.replace("/", "")) + 1] || "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let orgIsTourVisible = true;
|
||||||
|
|
||||||
|
export function setOrgIsTourVisible(visible) {
|
||||||
|
orgIsTourVisible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
export function setIsTourVisible(visible) {
|
export function setIsTourVisible(visible) {
|
||||||
localStorage.setItem("isTourVisible", visible);
|
localStorage.setItem("isTourVisible", visible);
|
||||||
window.dispatchEvent(new Event("storageTourChanged"));
|
}
|
||||||
|
|
||||||
|
export function setTourLogo(tourLogoSrc) {
|
||||||
|
if (tourLogoSrc !== "") {
|
||||||
|
TourObj["home"][0]["cover"] = (<img alt="casdoor.png" src={tourLogoSrc} />);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTourVisible() {
|
export function getTourVisible() {
|
||||||
return localStorage.getItem("isTourVisible") !== "false";
|
return localStorage.getItem("isTourVisible") !== "false" && orgIsTourVisible;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getNextButtonChild(nextPathName) {
|
export function getNextButtonChild(nextPathName) {
|
||||||
|
@ -125,7 +125,7 @@ class TransactionEditPage extends React.Component {
|
|||||||
application: application,
|
application: application,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getCerts(application.organization);
|
this.getCerts(application);
|
||||||
|
|
||||||
this.getSamlMetadata(application.enableSamlPostBinding);
|
this.getSamlMetadata(application.enableSamlPostBinding);
|
||||||
});
|
});
|
||||||
|
@ -54,9 +54,11 @@ class TransactionListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -41,6 +41,7 @@ import {CheckCircleOutlined, HolderOutlined, UsergroupAddOutlined} from "@ant-de
|
|||||||
import * as MfaBackend from "./backend/MfaBackend";
|
import * as MfaBackend from "./backend/MfaBackend";
|
||||||
import AccountAvatar from "./account/AccountAvatar";
|
import AccountAvatar from "./account/AccountAvatar";
|
||||||
import FaceIdTable from "./table/FaceIdTable";
|
import FaceIdTable from "./table/FaceIdTable";
|
||||||
|
import MfaAccountTable from "./table/MfaAccountTable";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
@ -1039,6 +1040,21 @@ class UserEditPage extends React.Component {
|
|||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
);
|
);
|
||||||
|
} else if (accountItem.name === "MFA accounts") {
|
||||||
|
return (
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("user:MFA accounts"), i18next.t("user:MFA accounts"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<MfaAccountTable
|
||||||
|
title={i18next.t("user:MFA accounts")}
|
||||||
|
table={this.state.user.mfaAccounts}
|
||||||
|
onUpdateTable={(table) => {this.updateUserField("mfaAccounts", table);}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
} else if (accountItem.name === "Need update password") {
|
} else if (accountItem.name === "Need update password") {
|
||||||
return (
|
return (
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
@ -110,9 +110,11 @@ class UserListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -167,6 +167,9 @@ class WebhookEditPage extends React.Component {
|
|||||||
["add", "update", "delete"].forEach(action => {
|
["add", "update", "delete"].forEach(action => {
|
||||||
res.push(`${action}-${obj}`);
|
res.push(`${action}-${obj}`);
|
||||||
});
|
});
|
||||||
|
if (obj === "payment") {
|
||||||
|
res.push("invoice-payment", "notify-payment");
|
||||||
|
}
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -61,9 +61,11 @@ class WebhookListPage extends BaseListPage {
|
|||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
this.setState({
|
this.fetch({
|
||||||
data: Setting.deleteRow(this.state.data, i),
|
pagination: {
|
||||||
pagination: {total: this.state.pagination.total - 1},
|
...this.state.pagination,
|
||||||
|
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React, {Suspense, lazy} from "react";
|
import React, {Suspense, lazy} from "react";
|
||||||
import {Button, Checkbox, Col, Form, Input, Result, Spin, Tabs} from "antd";
|
import {Button, Checkbox, Col, Form, Input, Result, Spin, Tabs, message} from "antd";
|
||||||
import {ArrowLeftOutlined, LockOutlined, UserOutlined} from "@ant-design/icons";
|
import {ArrowLeftOutlined, LockOutlined, UserOutlined} from "@ant-design/icons";
|
||||||
import {withRouter} from "react-router-dom";
|
import {withRouter} from "react-router-dom";
|
||||||
import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
|
import * as UserWebauthnBackend from "../backend/UserWebauthnBackend";
|
||||||
@ -23,7 +23,6 @@ import * as AuthBackend from "./AuthBackend";
|
|||||||
import * as OrganizationBackend from "../backend/OrganizationBackend";
|
import * as OrganizationBackend from "../backend/OrganizationBackend";
|
||||||
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
||||||
import * as Provider from "./Provider";
|
import * as Provider from "./Provider";
|
||||||
import * as ProviderButton from "./ProviderButton";
|
|
||||||
import * as Util from "./Util";
|
import * as Util from "./Util";
|
||||||
import * as Setting from "../Setting";
|
import * as Setting from "../Setting";
|
||||||
import * as AgreementModal from "../common/modal/AgreementModal";
|
import * as AgreementModal from "../common/modal/AgreementModal";
|
||||||
@ -36,6 +35,7 @@ import {CaptchaModal, CaptchaRule} from "../common/modal/CaptchaModal";
|
|||||||
import RedirectForm from "../common/RedirectForm";
|
import RedirectForm from "../common/RedirectForm";
|
||||||
import {MfaAuthVerifyForm, NextMfa, RequiredMfa} from "./mfa/MfaAuthVerifyForm";
|
import {MfaAuthVerifyForm, NextMfa, RequiredMfa} from "./mfa/MfaAuthVerifyForm";
|
||||||
import {GoogleOneTapLoginVirtualButton} from "./GoogleLoginButton";
|
import {GoogleOneTapLoginVirtualButton} from "./GoogleLoginButton";
|
||||||
|
import * as ProviderButton from "./ProviderButton";
|
||||||
const FaceRecognitionModal = lazy(() => import("../common/modal/FaceRecognitionModal"));
|
const FaceRecognitionModal = lazy(() => import("../common/modal/FaceRecognitionModal"));
|
||||||
|
|
||||||
class LoginPage extends React.Component {
|
class LoginPage extends React.Component {
|
||||||
@ -746,8 +746,21 @@ class LoginPage extends React.Component {
|
|||||||
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
<div dangerouslySetInnerHTML={{__html: ("<style>" + signinItem.customCss?.replaceAll("<style>", "").replaceAll("</style>", "") + "</style>")}} />
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
{
|
{
|
||||||
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
|
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map((providerItem, id) => {
|
||||||
return ProviderButton.renderProviderLogo(providerItem.provider, application, null, null, signinItem.rule, this.props.location);
|
return (
|
||||||
|
<span key ={id} onClick={(e) => {
|
||||||
|
const agreementChecked = this.form.current.getFieldValue("agreement");
|
||||||
|
|
||||||
|
if (agreementChecked !== undefined && typeof agreementChecked === "boolean" && !agreementChecked) {
|
||||||
|
e.preventDefault();
|
||||||
|
message.error(i18next.t("signup:Please accept the agreement!"));
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
ProviderButton.renderProviderLogo(providerItem.provider, application, null, null, signinItem.rule, this.props.location)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
@ -61,9 +61,9 @@ const authInfo = {
|
|||||||
},
|
},
|
||||||
WeCom: {
|
WeCom: {
|
||||||
scope: "snsapi_userinfo",
|
scope: "snsapi_userinfo",
|
||||||
endpoint: "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect",
|
endpoint: "https://login.work.weixin.qq.com/wwlogin/sso/login",
|
||||||
silentEndpoint: "https://open.weixin.qq.com/connect/oauth2/authorize",
|
silentEndpoint: "https://open.weixin.qq.com/connect/oauth2/authorize",
|
||||||
internalEndpoint: "https://open.work.weixin.qq.com/wwopen/sso/qrConnect",
|
internalEndpoint: "https://login.work.weixin.qq.com/wwlogin/sso/login",
|
||||||
},
|
},
|
||||||
Lark: {
|
Lark: {
|
||||||
// scope: "email",
|
// scope: "email",
|
||||||
@ -433,7 +433,7 @@ export function getAuthUrl(application, provider, method, code) {
|
|||||||
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&scope=${scope}&response_type=code#wechat_redirect`;
|
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&scope=${scope}&response_type=code#wechat_redirect`;
|
||||||
} else if (provider.method === "Normal") {
|
} else if (provider.method === "Normal") {
|
||||||
endpoint = authInfo[provider.type].internalEndpoint;
|
endpoint = authInfo[provider.type].internalEndpoint;
|
||||||
return `${endpoint}?appid=${provider.clientId}&agentid=${provider.appId}&redirect_uri=${redirectUri}&state=${state}&usertype=member`;
|
return `${endpoint}?login_type=CorpApp&appid=${provider.clientId}&agentid=${provider.appId}&redirect_uri=${redirectUri}&state=${state}`;
|
||||||
} else {
|
} else {
|
||||||
return `https://error:not-supported-provider-method:${provider.method}`;
|
return `https://error:not-supported-provider-method:${provider.method}`;
|
||||||
}
|
}
|
||||||
@ -442,7 +442,8 @@ export function getAuthUrl(application, provider, method, code) {
|
|||||||
endpoint = authInfo[provider.type].silentEndpoint;
|
endpoint = authInfo[provider.type].silentEndpoint;
|
||||||
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&scope=${scope}&response_type=code#wechat_redirect`;
|
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&scope=${scope}&response_type=code#wechat_redirect`;
|
||||||
} else if (provider.method === "Normal") {
|
} else if (provider.method === "Normal") {
|
||||||
return `${endpoint}?appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&usertype=member`;
|
endpoint = authInfo[provider.type].endpoint;
|
||||||
|
return `${endpoint}?login_type=ServiceApp&appid=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}`;
|
||||||
} else {
|
} else {
|
||||||
return `https://error:not-supported-provider-method:${provider.method}`;
|
return `https://error:not-supported-provider-method:${provider.method}`;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {Button, Form, Input, Radio, Result, Row} from "antd";
|
import {Button, Form, Input, Radio, Result, Row, message} 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";
|
||||||
@ -653,8 +653,21 @@ class SignupPage extends React.Component {
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
||||||
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map(providerItem => {
|
application.providers.filter(providerItem => this.isProviderVisible(providerItem)).map((providerItem, id) => {
|
||||||
return ProviderButton.renderProviderLogo(providerItem.provider, application, null, null, signupItem.rule, this.props.location);
|
return (
|
||||||
|
<span key={id} onClick={(e) => {
|
||||||
|
const agreementChecked = this.form.current.getFieldValue("agreement");
|
||||||
|
|
||||||
|
if (agreementChecked !== undefined && typeof agreementChecked === "boolean" && !agreementChecked) {
|
||||||
|
e.preventDefault();
|
||||||
|
message.error(i18next.t("signup:Please accept the agreement!"));
|
||||||
|
}
|
||||||
|
}}>
|
||||||
|
{
|
||||||
|
ProviderButton.renderProviderLogo(providerItem.provider, application, null, null, signupItem.rule, this.props.location)
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
})
|
})
|
||||||
|
|
||||||
);
|
);
|
||||||
|
@ -125,6 +125,14 @@ function initLanguage() {
|
|||||||
case "fa":
|
case "fa":
|
||||||
language = "fa";
|
language = "fa";
|
||||||
break;
|
break;
|
||||||
|
case "cs":
|
||||||
|
case "cs-CZ":
|
||||||
|
language = "cs";
|
||||||
|
break;
|
||||||
|
case "sk":
|
||||||
|
case "sk-SK":
|
||||||
|
language = "sk";
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
language = Conf.DefaultLanguage;
|
language = Conf.DefaultLanguage;
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,12 @@ import * as serviceWorker from "./serviceWorker";
|
|||||||
import {BrowserRouter} from "react-router-dom";
|
import {BrowserRouter} from "react-router-dom";
|
||||||
import "./backend/FetchFilter";
|
import "./backend/FetchFilter";
|
||||||
|
|
||||||
|
if (!String.prototype.replaceAll) {
|
||||||
|
String.prototype.replaceAll = function(search, replace) {
|
||||||
|
return this.split(search).join(replace);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const container = document.getElementById("root");
|
const container = document.getElementById("root");
|
||||||
|
|
||||||
const app = createRoot(container);
|
const app = createRoot(container);
|
||||||
|
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "Token fields - Tooltip",
|
"Token fields - Tooltip": "Token fields - Tooltip",
|
||||||
"Token format": "Token format",
|
"Token format": "Token format",
|
||||||
"Token format - Tooltip": "The format of access token",
|
"Token format - Tooltip": "The format of access token",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enabled successfully": "Enabled successfully",
|
"Enabled successfully": "Enabled successfully",
|
||||||
"Enforcers": "Enforcers",
|
"Enforcers": "Enforcers",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Is enabled",
|
"Is enabled": "Is enabled",
|
||||||
"Is enabled - Tooltip": "Set whether it can use",
|
"Is enabled - Tooltip": "Set whether it can use",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs",
|
"LDAPs": "LDAPs",
|
||||||
"LDAPs - Tooltip": "LDAP servers",
|
"LDAPs - Tooltip": "LDAP servers",
|
||||||
"Languages": "Languages",
|
"Languages": "Languages",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "Base DN",
|
"Base DN": "Base DN",
|
||||||
"Base DN - Tooltip": "Base DN during LDAP search",
|
"Base DN - Tooltip": "Base DN during LDAP search",
|
||||||
"CN": "CN",
|
"CN": "CN",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "Edit LDAP",
|
"Edit LDAP": "Edit LDAP",
|
||||||
"Enable SSL": "Enable SSL",
|
"Enable SSL": "Enable SSL",
|
||||||
"Enable SSL - Tooltip": "Whether to enable SSL",
|
"Enable SSL - Tooltip": "Whether to enable SSL",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Your phone is",
|
"Your phone is": "Your phone is",
|
||||||
"preferred": "preferred"
|
"preferred": "preferred"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Edit Model",
|
"Edit Model": "Edit Model",
|
||||||
"Model text": "Model text",
|
"Model text": "Model text",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Link",
|
"Link": "Link",
|
||||||
"Location": "Location",
|
"Location": "Location",
|
||||||
"Location - Tooltip": "City of residence",
|
"Location - Tooltip": "City of residence",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Managed accounts",
|
"Managed accounts": "Managed accounts",
|
||||||
"Modify password...": "Modify password...",
|
"Modify password...": "Modify password...",
|
||||||
"Multi-factor authentication": "Multi-factor authentication",
|
"Multi-factor authentication": "Multi-factor authentication",
|
||||||
|
1223
web/src/locales/cs/data.json
Normal file
1223
web/src/locales/cs/data.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "Token fields - Tooltip",
|
"Token fields - Tooltip": "Token fields - Tooltip",
|
||||||
"Token format": "Token-Format",
|
"Token format": "Token-Format",
|
||||||
"Token format - Tooltip": "Das Format des Access-Tokens",
|
"Token format - Tooltip": "Das Format des Access-Tokens",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "Sie sind unerwartet auf diese Aufforderungsseite gelangt"
|
"You are unexpected to see this prompt page": "Sie sind unerwartet auf diese Aufforderungsseite gelangt"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enabled successfully": "Enabled successfully",
|
"Enabled successfully": "Enabled successfully",
|
||||||
"Enforcers": "Enforcers",
|
"Enforcers": "Enforcers",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Ist aktiviert",
|
"Is enabled": "Ist aktiviert",
|
||||||
"Is enabled - Tooltip": "Festlegen, ob es verwendet werden kann",
|
"Is enabled - Tooltip": "Festlegen, ob es verwendet werden kann",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs",
|
"LDAPs": "LDAPs",
|
||||||
"LDAPs - Tooltip": "LDAP-Server",
|
"LDAPs - Tooltip": "LDAP-Server",
|
||||||
"Languages": "Sprachen",
|
"Languages": "Sprachen",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "Basis-DN",
|
"Base DN": "Basis-DN",
|
||||||
"Base DN - Tooltip": "Basis-DN während der LDAP-Suche",
|
"Base DN - Tooltip": "Basis-DN während der LDAP-Suche",
|
||||||
"CN": "KN",
|
"CN": "KN",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "LDAP bearbeiten",
|
"Edit LDAP": "LDAP bearbeiten",
|
||||||
"Enable SSL": "Aktivieren Sie SSL",
|
"Enable SSL": "Aktivieren Sie SSL",
|
||||||
"Enable SSL - Tooltip": "Ob SSL aktiviert werden soll",
|
"Enable SSL - Tooltip": "Ob SSL aktiviert werden soll",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Your phone is",
|
"Your phone is": "Your phone is",
|
||||||
"preferred": "preferred"
|
"preferred": "preferred"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Modell bearbeiten",
|
"Edit Model": "Modell bearbeiten",
|
||||||
"Model text": "Modelltext",
|
"Model text": "Modelltext",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Link",
|
"Link": "Link",
|
||||||
"Location": "Ort",
|
"Location": "Ort",
|
||||||
"Location - Tooltip": "Stadt des Wohnsitzes",
|
"Location - Tooltip": "Stadt des Wohnsitzes",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Verwaltete Konten",
|
"Managed accounts": "Verwaltete Konten",
|
||||||
"Modify password...": "Passwort ändern...",
|
"Modify password...": "Passwort ändern...",
|
||||||
"Multi-factor authentication": "Multi-factor authentication",
|
"Multi-factor authentication": "Multi-factor authentication",
|
||||||
|
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "The user fields included in the token",
|
"Token fields - Tooltip": "The user fields included in the token",
|
||||||
"Token format": "Token format",
|
"Token format": "Token format",
|
||||||
"Token format - Tooltip": "The format of access token",
|
"Token format - Tooltip": "The format of access token",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enabled successfully": "Enabled successfully",
|
"Enabled successfully": "Enabled successfully",
|
||||||
"Enforcers": "Enforcers",
|
"Enforcers": "Enforcers",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Is enabled",
|
"Is enabled": "Is enabled",
|
||||||
"Is enabled - Tooltip": "Set whether it can use",
|
"Is enabled - Tooltip": "Set whether it can use",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs",
|
"LDAPs": "LDAPs",
|
||||||
"LDAPs - Tooltip": "LDAP servers",
|
"LDAPs - Tooltip": "LDAP servers",
|
||||||
"Languages": "Languages",
|
"Languages": "Languages",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "Base DN",
|
"Base DN": "Base DN",
|
||||||
"Base DN - Tooltip": "Base DN during LDAP search",
|
"Base DN - Tooltip": "Base DN during LDAP search",
|
||||||
"CN": "CN",
|
"CN": "CN",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "Edit LDAP",
|
"Edit LDAP": "Edit LDAP",
|
||||||
"Enable SSL": "Enable SSL",
|
"Enable SSL": "Enable SSL",
|
||||||
"Enable SSL - Tooltip": "Whether to enable SSL",
|
"Enable SSL - Tooltip": "Whether to enable SSL",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Your phone is",
|
"Your phone is": "Your phone is",
|
||||||
"preferred": "preferred"
|
"preferred": "preferred"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Edit Model",
|
"Edit Model": "Edit Model",
|
||||||
"Model text": "Model text",
|
"Model text": "Model text",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Link",
|
"Link": "Link",
|
||||||
"Location": "Location",
|
"Location": "Location",
|
||||||
"Location - Tooltip": "City of residence",
|
"Location - Tooltip": "City of residence",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Managed accounts",
|
"Managed accounts": "Managed accounts",
|
||||||
"Modify password...": "Modify password...",
|
"Modify password...": "Modify password...",
|
||||||
"Multi-factor authentication": "Multi-factor authentication",
|
"Multi-factor authentication": "Multi-factor authentication",
|
||||||
|
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "Token fields - Tooltip",
|
"Token fields - Tooltip": "Token fields - Tooltip",
|
||||||
"Token format": "Formato del token",
|
"Token format": "Formato del token",
|
||||||
"Token format - Tooltip": "El formato del token de acceso",
|
"Token format - Tooltip": "El formato del token de acceso",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "Es inesperado ver esta página de inicio"
|
"You are unexpected to see this prompt page": "Es inesperado ver esta página de inicio"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enabled successfully": "Enabled successfully",
|
"Enabled successfully": "Enabled successfully",
|
||||||
"Enforcers": "Enforcers",
|
"Enforcers": "Enforcers",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Está habilitado",
|
"Is enabled": "Está habilitado",
|
||||||
"Is enabled - Tooltip": "Establecer si se puede usar",
|
"Is enabled - Tooltip": "Establecer si se puede usar",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs (Secure LDAP)",
|
"LDAPs": "LDAPs (Secure LDAP)",
|
||||||
"LDAPs - Tooltip": "Servidores LDAP",
|
"LDAPs - Tooltip": "Servidores LDAP",
|
||||||
"Languages": "Idiomas",
|
"Languages": "Idiomas",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "DN base",
|
"Base DN": "DN base",
|
||||||
"Base DN - Tooltip": "Base DN durante la búsqueda LDAP",
|
"Base DN - Tooltip": "Base DN durante la búsqueda LDAP",
|
||||||
"CN": "CN (siglas en inglés) podría traducirse como \"Red de Comunicaciones\". Sin embargo, sin más contexto, no es posible saber cuál es el significado exacto de estas siglas",
|
"CN": "CN (siglas en inglés) podría traducirse como \"Red de Comunicaciones\". Sin embargo, sin más contexto, no es posible saber cuál es el significado exacto de estas siglas",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "Editar LDAP",
|
"Edit LDAP": "Editar LDAP",
|
||||||
"Enable SSL": "Habilitar SSL",
|
"Enable SSL": "Habilitar SSL",
|
||||||
"Enable SSL - Tooltip": "Si se habilita SSL",
|
"Enable SSL - Tooltip": "Si se habilita SSL",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Your phone is",
|
"Your phone is": "Your phone is",
|
||||||
"preferred": "preferred"
|
"preferred": "preferred"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Editar modelo",
|
"Edit Model": "Editar modelo",
|
||||||
"Model text": "Texto modelo",
|
"Model text": "Texto modelo",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Enlace",
|
"Link": "Enlace",
|
||||||
"Location": "Ubicación",
|
"Location": "Ubicación",
|
||||||
"Location - Tooltip": "Ciudad de residencia",
|
"Location - Tooltip": "Ciudad de residencia",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Cuentas gestionadas",
|
"Managed accounts": "Cuentas gestionadas",
|
||||||
"Modify password...": "Modificar contraseña...",
|
"Modify password...": "Modificar contraseña...",
|
||||||
"Multi-factor authentication": "Multi-factor authentication",
|
"Multi-factor authentication": "Multi-factor authentication",
|
||||||
|
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "Token fields - Tooltip",
|
"Token fields - Tooltip": "Token fields - Tooltip",
|
||||||
"Token format": "Token format",
|
"Token format": "Token format",
|
||||||
"Token format - Tooltip": "The format of access token",
|
"Token format - Tooltip": "The format of access token",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enabled successfully": "Enabled successfully",
|
"Enabled successfully": "Enabled successfully",
|
||||||
"Enforcers": "Enforcers",
|
"Enforcers": "Enforcers",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Is enabled",
|
"Is enabled": "Is enabled",
|
||||||
"Is enabled - Tooltip": "Set whether it can use",
|
"Is enabled - Tooltip": "Set whether it can use",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs",
|
"LDAPs": "LDAPs",
|
||||||
"LDAPs - Tooltip": "LDAP servers",
|
"LDAPs - Tooltip": "LDAP servers",
|
||||||
"Languages": "Languages",
|
"Languages": "Languages",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "Base DN",
|
"Base DN": "Base DN",
|
||||||
"Base DN - Tooltip": "Base DN during LDAP search",
|
"Base DN - Tooltip": "Base DN during LDAP search",
|
||||||
"CN": "CN",
|
"CN": "CN",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "Edit LDAP",
|
"Edit LDAP": "Edit LDAP",
|
||||||
"Enable SSL": "Enable SSL",
|
"Enable SSL": "Enable SSL",
|
||||||
"Enable SSL - Tooltip": "Whether to enable SSL",
|
"Enable SSL - Tooltip": "Whether to enable SSL",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Your phone is",
|
"Your phone is": "Your phone is",
|
||||||
"preferred": "preferred"
|
"preferred": "preferred"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Edit Model",
|
"Edit Model": "Edit Model",
|
||||||
"Model text": "Model text",
|
"Model text": "Model text",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Link",
|
"Link": "Link",
|
||||||
"Location": "Location",
|
"Location": "Location",
|
||||||
"Location - Tooltip": "City of residence",
|
"Location - Tooltip": "City of residence",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Managed accounts",
|
"Managed accounts": "Managed accounts",
|
||||||
"Modify password...": "Modify password...",
|
"Modify password...": "Modify password...",
|
||||||
"Multi-factor authentication": "Multi-factor authentication",
|
"Multi-factor authentication": "Multi-factor authentication",
|
||||||
|
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "Token fields - Tooltip",
|
"Token fields - Tooltip": "Token fields - Tooltip",
|
||||||
"Token format": "Token format",
|
"Token format": "Token format",
|
||||||
"Token format - Tooltip": "The format of access token",
|
"Token format - Tooltip": "The format of access token",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Enable",
|
"Enable": "Enable",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Enabled",
|
"Enabled": "Enabled",
|
||||||
"Enabled successfully": "Enabled successfully",
|
"Enabled successfully": "Enabled successfully",
|
||||||
"Enforcers": "Enforcers",
|
"Enforcers": "Enforcers",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Is enabled",
|
"Is enabled": "Is enabled",
|
||||||
"Is enabled - Tooltip": "Set whether it can use",
|
"Is enabled - Tooltip": "Set whether it can use",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs",
|
"LDAPs": "LDAPs",
|
||||||
"LDAPs - Tooltip": "LDAP servers",
|
"LDAPs - Tooltip": "LDAP servers",
|
||||||
"Languages": "Languages",
|
"Languages": "Languages",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "Base DN",
|
"Base DN": "Base DN",
|
||||||
"Base DN - Tooltip": "Base DN during LDAP search",
|
"Base DN - Tooltip": "Base DN during LDAP search",
|
||||||
"CN": "CN",
|
"CN": "CN",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "Edit LDAP",
|
"Edit LDAP": "Edit LDAP",
|
||||||
"Enable SSL": "Enable SSL",
|
"Enable SSL": "Enable SSL",
|
||||||
"Enable SSL - Tooltip": "Whether to enable SSL",
|
"Enable SSL - Tooltip": "Whether to enable SSL",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Your phone is",
|
"Your phone is": "Your phone is",
|
||||||
"preferred": "preferred"
|
"preferred": "preferred"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Edit Model",
|
"Edit Model": "Edit Model",
|
||||||
"Model text": "Model text",
|
"Model text": "Model text",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Link",
|
"Link": "Link",
|
||||||
"Location": "Location",
|
"Location": "Location",
|
||||||
"Location - Tooltip": "City of residence",
|
"Location - Tooltip": "City of residence",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Managed accounts",
|
"Managed accounts": "Managed accounts",
|
||||||
"Modify password...": "Modify password...",
|
"Modify password...": "Modify password...",
|
||||||
"Multi-factor authentication": "Multi-factor authentication",
|
"Multi-factor authentication": "Multi-factor authentication",
|
||||||
|
@ -121,6 +121,8 @@
|
|||||||
"Token fields - Tooltip": "Token fields - Tooltip",
|
"Token fields - Tooltip": "Token fields - Tooltip",
|
||||||
"Token format": "Format de jeton",
|
"Token format": "Format de jeton",
|
||||||
"Token format - Tooltip": "Le format du jeton d'accès",
|
"Token format - Tooltip": "Le format du jeton d'accès",
|
||||||
|
"Token signing method": "Token signing method",
|
||||||
|
"Token signing method - Tooltip": "Signing method of JWT token, needs to be the same algorithm as the certificate",
|
||||||
"You are unexpected to see this prompt page": "Il n'était pas prévu que vous voyez cette page de saisie"
|
"You are unexpected to see this prompt page": "Il n'était pas prévu que vous voyez cette page de saisie"
|
||||||
},
|
},
|
||||||
"cert": {
|
"cert": {
|
||||||
@ -234,6 +236,8 @@
|
|||||||
"Enable": "Activer",
|
"Enable": "Activer",
|
||||||
"Enable dark logo": "Enable dark logo",
|
"Enable dark logo": "Enable dark logo",
|
||||||
"Enable dark logo - Tooltip": "Enable dark logo",
|
"Enable dark logo - Tooltip": "Enable dark logo",
|
||||||
|
"Enable tour": "Enable tour",
|
||||||
|
"Enable tour - Tooltip": "Display tour for users",
|
||||||
"Enabled": "Activé",
|
"Enabled": "Activé",
|
||||||
"Enabled successfully": "Activé avec succès",
|
"Enabled successfully": "Activé avec succès",
|
||||||
"Enforcers": "Exécuteurs",
|
"Enforcers": "Exécuteurs",
|
||||||
@ -265,6 +269,8 @@
|
|||||||
"Invitations": "Invitations",
|
"Invitations": "Invitations",
|
||||||
"Is enabled": "Est activé",
|
"Is enabled": "Est activé",
|
||||||
"Is enabled - Tooltip": "Définir s'il peut être utilisé",
|
"Is enabled - Tooltip": "Définir s'il peut être utilisé",
|
||||||
|
"Is shared": "Is shared",
|
||||||
|
"Is shared - Tooltip": "Share this application with other organizations",
|
||||||
"LDAPs": "LDAPs",
|
"LDAPs": "LDAPs",
|
||||||
"LDAPs - Tooltip": "Serveurs LDAP",
|
"LDAPs - Tooltip": "Serveurs LDAP",
|
||||||
"Languages": "Langues",
|
"Languages": "Langues",
|
||||||
@ -441,6 +447,8 @@
|
|||||||
"Base DN": "DN racine",
|
"Base DN": "DN racine",
|
||||||
"Base DN - Tooltip": "Le DN racine (base DN) lors de la recherche LDAP",
|
"Base DN - Tooltip": "Le DN racine (base DN) lors de la recherche LDAP",
|
||||||
"CN": "CN",
|
"CN": "CN",
|
||||||
|
"Default group": "Default group",
|
||||||
|
"Default group - Tooltip": "Group to which users belong after synchronization",
|
||||||
"Edit LDAP": "Modifier le LDAP",
|
"Edit LDAP": "Modifier le LDAP",
|
||||||
"Enable SSL": "Activer SSL",
|
"Enable SSL": "Activer SSL",
|
||||||
"Enable SSL - Tooltip": "Activer SSL",
|
"Enable SSL - Tooltip": "Activer SSL",
|
||||||
@ -551,6 +559,11 @@
|
|||||||
"Your phone is": "Votre téléphone est",
|
"Your phone is": "Votre téléphone est",
|
||||||
"preferred": "préféré"
|
"preferred": "préféré"
|
||||||
},
|
},
|
||||||
|
"mfaAccount": {
|
||||||
|
"Account Name": "Account Name",
|
||||||
|
"Issuer": "Issuer",
|
||||||
|
"Secret Key": "Secret Key"
|
||||||
|
},
|
||||||
"model": {
|
"model": {
|
||||||
"Edit Model": "Modifier le modèle",
|
"Edit Model": "Modifier le modèle",
|
||||||
"Model text": "Définition du modèle",
|
"Model text": "Définition du modèle",
|
||||||
@ -1136,6 +1149,7 @@
|
|||||||
"Link": "Lier",
|
"Link": "Lier",
|
||||||
"Location": "Localisation",
|
"Location": "Localisation",
|
||||||
"Location - Tooltip": "Ville de résidence",
|
"Location - Tooltip": "Ville de résidence",
|
||||||
|
"MFA accounts": "MFA accounts",
|
||||||
"Managed accounts": "Comptes gérés",
|
"Managed accounts": "Comptes gérés",
|
||||||
"Modify password...": "Modifier le mot de passe...",
|
"Modify password...": "Modifier le mot de passe...",
|
||||||
"Multi-factor authentication": "Authentification multifacteur",
|
"Multi-factor authentication": "Authentification multifacteur",
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user