mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-09 01:13:41 +08:00
Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
fe448cbcf4 | |||
2ab25df950 | |||
b895926754 | |||
5bb7a4153f | |||
b7cd598ee8 | |||
b10fb97c92 |
@ -31,6 +31,8 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
|
||||
return NewAliyunCaptchaProvider()
|
||||
} else if captchaType == "GEETEST" {
|
||||
return NewGEETESTCaptchaProvider()
|
||||
} else if captchaType == "Cloudflare Turnstile" {
|
||||
return NewCloudflareTurnstileProvider()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
66
captcha/turnstile.go
Normal file
66
captcha/turnstile.go
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package captcha
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const CloudflareTurnstileVerifyUrl = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
|
||||
|
||||
type CloudflareTurnstileProvider struct{}
|
||||
|
||||
func NewCloudflareTurnstileProvider() *CloudflareTurnstileProvider {
|
||||
captcha := &CloudflareTurnstileProvider{}
|
||||
return captcha
|
||||
}
|
||||
|
||||
func (captcha *CloudflareTurnstileProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
|
||||
reqData := url.Values{
|
||||
"secret": {clientSecret},
|
||||
"response": {token},
|
||||
}
|
||||
resp, err := http.PostForm(CloudflareTurnstileVerifyUrl, reqData)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
type captchaResponse struct {
|
||||
Success bool `json:"success"`
|
||||
ErrorCodes []string `json:"error-codes"`
|
||||
}
|
||||
captchaResp := &captchaResponse{}
|
||||
err = json.Unmarshal(body, captchaResp)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if len(captchaResp.ErrorCodes) > 0 {
|
||||
return false, errors.New(strings.Join(captchaResp.ErrorCodes, ","))
|
||||
}
|
||||
|
||||
return captchaResp.Success, nil
|
||||
}
|
@ -18,6 +18,7 @@ import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/beego/beego/utils/pagination"
|
||||
xormadapter "github.com/casbin/xorm-adapter/v3"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@ -89,6 +90,69 @@ func (c *ApiController) SyncPolicies() {
|
||||
id := c.Input().Get("id")
|
||||
adapter := object.GetCasbinAdapter(id)
|
||||
|
||||
c.Data["json"] = object.SyncPolicies(adapter)
|
||||
policies, err := object.SyncPolicies(adapter)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = policies
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) UpdatePolicy() {
|
||||
id := c.Input().Get("id")
|
||||
adapter := object.GetCasbinAdapter(id)
|
||||
var policies []xormadapter.CasbinRule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policies)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
affected, err := object.UpdatePolicy(util.CasbinToSlice(policies[0]), util.CasbinToSlice(policies[1]), adapter)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) AddPolicy() {
|
||||
id := c.Input().Get("id")
|
||||
adapter := object.GetCasbinAdapter(id)
|
||||
var policy xormadapter.CasbinRule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
affected, err := object.AddPolicy(util.CasbinToSlice(policy), adapter)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
func (c *ApiController) RemovePolicy() {
|
||||
id := c.Input().Get("id")
|
||||
adapter := object.GetCasbinAdapter(id)
|
||||
var policy xormadapter.CasbinRule
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &policy)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
affected, err := object.RemovePolicy(util.CasbinToSlice(policy), adapter)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.Data["json"] = wrapActionResponse(affected)
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
@ -171,6 +171,20 @@ func (c *ApiController) UploadResource() {
|
||||
fileType, _ = util.GetOwnerAndNameFromId(mimeType)
|
||||
}
|
||||
|
||||
if tag != "avatar" && tag != "termsOfUse" {
|
||||
ext := filepath.Ext(filepath.Base(fullFilePath))
|
||||
index := len(fullFilePath) - len(ext)
|
||||
for i := 1; ; i++ {
|
||||
_, objectKey := object.GetUploadFileUrl(provider, fullFilePath, true)
|
||||
if object.GetResourceCount(owner, username, "name", objectKey) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// duplicated fullFilePath found, change it
|
||||
fullFilePath = fullFilePath[:index] + fmt.Sprintf("-%d", i) + ext
|
||||
}
|
||||
}
|
||||
|
||||
fileUrl, objectKey, err := object.UploadFileSafe(provider, fullFilePath, fileBuffer)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
|
@ -92,6 +92,10 @@ func (c *ApiController) SendVerificationCode() {
|
||||
user := c.getCurrentUser()
|
||||
application := object.GetApplication(applicationId)
|
||||
organization := object.GetOrganization(fmt.Sprintf("%s/%s", application.Owner, application.Organization))
|
||||
if organization == nil {
|
||||
c.ResponseError(c.T("OrgErr.DoNotExist"))
|
||||
return
|
||||
}
|
||||
|
||||
if checkUser == "true" && user == nil && object.GetUserByFields(organization.Name, dest) == nil {
|
||||
c.ResponseError(c.T("LoginErr.LoginFirst"))
|
||||
@ -114,6 +118,12 @@ func (c *ApiController) SendVerificationCode() {
|
||||
return
|
||||
}
|
||||
|
||||
userByEmail := object.GetUserByEmail(organization.Name, dest)
|
||||
if userByEmail == nil {
|
||||
c.ResponseError(c.T("UserErr.DoNotExistSignUp"))
|
||||
return
|
||||
}
|
||||
|
||||
provider := application.GetEmailProvider()
|
||||
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, dest)
|
||||
case "phone":
|
||||
@ -124,8 +134,10 @@ func (c *ApiController) SendVerificationCode() {
|
||||
c.ResponseError(c.T("PhoneErr.NumberInvalid"))
|
||||
return
|
||||
}
|
||||
if organization == nil {
|
||||
c.ResponseError(c.T("OrgErr.DoNotExist"))
|
||||
|
||||
userByPhone := object.GetUserByPhone(organization.Name, dest)
|
||||
if userByPhone == nil {
|
||||
c.ResponseError(c.T("UserErr.DoNotExistSignUp"))
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,7 +60,7 @@ func getPermanentAvatarUrl(organization string, username string, url string, upl
|
||||
}
|
||||
|
||||
fullFilePath := fmt.Sprintf("/avatar/%s/%s.png", organization, username)
|
||||
uploadedFileUrl, _ := getUploadFileUrl(defaultStorageProvider, fullFilePath, false)
|
||||
uploadedFileUrl, _ := GetUploadFileUrl(defaultStorageProvider, fullFilePath, false)
|
||||
|
||||
if upload {
|
||||
DownloadAndUpload(url, fullFilePath)
|
||||
|
@ -148,34 +148,7 @@ func (casbinAdapter *CasbinAdapter) getTable() string {
|
||||
}
|
||||
}
|
||||
|
||||
func safeReturn(policy []string, i int) string {
|
||||
if len(policy) > i {
|
||||
return policy[i]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func matrixToCasbinRules(pType string, policies [][]string) []*xormadapter.CasbinRule {
|
||||
res := []*xormadapter.CasbinRule{}
|
||||
|
||||
for _, policy := range policies {
|
||||
line := xormadapter.CasbinRule{
|
||||
Ptype: pType,
|
||||
V0: safeReturn(policy, 0),
|
||||
V1: safeReturn(policy, 1),
|
||||
V2: safeReturn(policy, 2),
|
||||
V3: safeReturn(policy, 3),
|
||||
V4: safeReturn(policy, 4),
|
||||
V5: safeReturn(policy, 5),
|
||||
}
|
||||
res = append(res, &line)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
|
||||
func initEnforcer(modelObj *Model, casbinAdapter *CasbinAdapter) (*casbin.Enforcer, error) {
|
||||
// init Adapter
|
||||
if casbinAdapter.Adapter == nil {
|
||||
var dataSourceName string
|
||||
@ -191,20 +164,60 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
|
||||
dataSourceName = strings.ReplaceAll(dataSourceName, "dbi.", "db.")
|
||||
}
|
||||
|
||||
casbinAdapter.Adapter, _ = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "")
|
||||
var err error
|
||||
casbinAdapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(casbinAdapter.DatabaseType, dataSourceName, casbinAdapter.Database).Engine, casbinAdapter.getTable(), "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// init Model
|
||||
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||
m, err := model.NewModelFromString(modelObj.ModelText)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// init Enforcer
|
||||
enforcer, err := casbin.NewEnforcer(m, casbinAdapter.Adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return enforcer, nil
|
||||
}
|
||||
|
||||
func safeReturn(policy []string, i int) string {
|
||||
if len(policy) > i {
|
||||
return policy[i]
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func matrixToCasbinRules(Ptype string, policies [][]string) []*xormadapter.CasbinRule {
|
||||
res := []*xormadapter.CasbinRule{}
|
||||
|
||||
for _, policy := range policies {
|
||||
line := xormadapter.CasbinRule{
|
||||
Ptype: Ptype,
|
||||
V0: safeReturn(policy, 0),
|
||||
V1: safeReturn(policy, 1),
|
||||
V2: safeReturn(policy, 2),
|
||||
V3: safeReturn(policy, 3),
|
||||
V4: safeReturn(policy, 4),
|
||||
V5: safeReturn(policy, 5),
|
||||
}
|
||||
res = append(res, &line)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func SyncPolicies(casbinAdapter *CasbinAdapter) ([]*xormadapter.CasbinRule, error) {
|
||||
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
policies := matrixToCasbinRules("p", enforcer.GetPolicy())
|
||||
@ -212,5 +225,48 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) []*xormadapter.CasbinRule {
|
||||
policies = append(policies, matrixToCasbinRules("g", enforcer.GetGroupingPolicy())...)
|
||||
}
|
||||
|
||||
return policies
|
||||
return policies, nil
|
||||
}
|
||||
|
||||
func UpdatePolicy(oldPolicy, newPolicy []string, casbinAdapter *CasbinAdapter) (bool, error) {
|
||||
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := enforcer.UpdatePolicy(oldPolicy, newPolicy)
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
func AddPolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) {
|
||||
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := enforcer.AddPolicy(policy)
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
func RemovePolicy(policy []string, casbinAdapter *CasbinAdapter) (bool, error) {
|
||||
modelObj := getModel(casbinAdapter.Owner, casbinAdapter.Model)
|
||||
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := enforcer.RemovePolicy(policy)
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func escapePath(path string) string {
|
||||
return res
|
||||
}
|
||||
|
||||
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||
func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
|
||||
escapedPath := util.UrlJoin(provider.PathPrefix, escapePath(fullFilePath))
|
||||
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
|
||||
|
||||
@ -93,7 +93,7 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
|
||||
UpdateProvider(provider.GetId(), provider)
|
||||
}
|
||||
|
||||
fileUrl, objectKey := getUploadFileUrl(provider, fullFilePath, true)
|
||||
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
|
||||
|
||||
_, err := storageProvider.Put(objectKey, fileBuffer)
|
||||
if err != nil {
|
||||
|
@ -105,6 +105,9 @@ func initAPI() {
|
||||
beego.Router("/api/add-adapter", &controllers.ApiController{}, "POST:AddCasbinAdapter")
|
||||
beego.Router("/api/delete-adapter", &controllers.ApiController{}, "POST:DeleteCasbinAdapter")
|
||||
beego.Router("/api/sync-policies", &controllers.ApiController{}, "GET:SyncPolicies")
|
||||
beego.Router("/api/update-policy", &controllers.ApiController{}, "POST:UpdatePolicy")
|
||||
beego.Router("/api/add-policy", &controllers.ApiController{}, "POST:AddPolicy")
|
||||
beego.Router("/api/remove-policy", &controllers.ApiController{}, "POST:RemovePolicy")
|
||||
|
||||
beego.Router("/api/set-password", &controllers.ApiController{}, "POST:SetPassword")
|
||||
beego.Router("/api/check-user-password", &controllers.ApiController{}, "POST:CheckUserPassword")
|
||||
|
29
util/struct.go
Normal file
29
util/struct.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import xormadapter "github.com/casbin/xorm-adapter/v3"
|
||||
|
||||
func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
|
||||
s := []string{
|
||||
casbinRule.V0,
|
||||
casbinRule.V1,
|
||||
casbinRule.V2,
|
||||
casbinRule.V3,
|
||||
casbinRule.V4,
|
||||
casbinRule.V5,
|
||||
}
|
||||
return s
|
||||
}
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch, Table, Tooltip} from "antd";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
|
||||
import * as AdapterBackend from "./backend/AdapterBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as Setting from "./Setting";
|
||||
@ -21,7 +21,7 @@ import i18next from "i18next";
|
||||
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import * as ModelBackend from "./backend/ModelBackend";
|
||||
import {EditOutlined, MinusOutlined} from "@ant-design/icons";
|
||||
import PolicyTable from "./common/PoliciyTable";
|
||||
require("codemirror/theme/material-darker.css");
|
||||
require("codemirror/mode/javascript/javascript");
|
||||
|
||||
@ -32,12 +32,11 @@ class AdapterEditPage extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
classes: props,
|
||||
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||
adapterName: props.match.params.adapterName,
|
||||
adapter: null,
|
||||
organizations: [],
|
||||
models: [],
|
||||
policyLists: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
};
|
||||
}
|
||||
@ -48,7 +47,7 @@ class AdapterEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getAdapter() {
|
||||
AdapterBackend.getAdapter(this.state.organizationName, this.state.adapterName)
|
||||
AdapterBackend.getAdapter(this.state.owner, this.state.adapterName)
|
||||
.then((adapter) => {
|
||||
this.setState({
|
||||
adapter: adapter,
|
||||
@ -93,93 +92,6 @@ class AdapterEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
synPolicies() {
|
||||
this.setState({loading: true});
|
||||
AdapterBackend.syncPolicies(this.state.adapter.owner, this.state.adapter.name)
|
||||
.then((res) => {
|
||||
this.setState({loading: false, policyLists: res});
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({loading: false});
|
||||
Setting.showMessage("error", `Adapter failed to get policies: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(table) {
|
||||
const columns = [
|
||||
{
|
||||
title: "Rule Type",
|
||||
dataIndex: "PType",
|
||||
key: "PType",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "V0",
|
||||
dataIndex: "V0",
|
||||
key: "V0",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "V1",
|
||||
dataIndex: "V1",
|
||||
key: "V1",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "V2",
|
||||
dataIndex: "V2",
|
||||
key: "V2",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "V3",
|
||||
dataIndex: "V3",
|
||||
key: "V3",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "V4",
|
||||
dataIndex: "V4",
|
||||
key: "V4",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "V5",
|
||||
dataIndex: "V5",
|
||||
key: "V5",
|
||||
width: "100px",
|
||||
},
|
||||
{
|
||||
title: "Option",
|
||||
key: "option",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
<Tooltip placement="topLeft" title="Edit">
|
||||
<Button style={{marginRight: "0.5rem"}} icon={<EditOutlined />} size="small" />
|
||||
</Tooltip>
|
||||
<Tooltip placement="topLeft" title="Delete">
|
||||
<Button icon={<MinusOutlined />} size="small" />
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Table
|
||||
pagination={{
|
||||
defaultPageSize: 10,
|
||||
}}
|
||||
columns={columns} dataSource={table} rowKey="name" size="middle" bordered
|
||||
loading={this.state.loading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderAdapter() {
|
||||
return (
|
||||
<Card size="small" title={
|
||||
@ -329,19 +241,8 @@ class AdapterEditPage extends React.Component {
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={2}>
|
||||
<Button type="primary" onClick={() => {this.synPolicies();}}>
|
||||
{i18next.t("adapter:Sync")}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{
|
||||
this.renderTable(this.state.policyLists)
|
||||
}
|
||||
<Col span={22}>
|
||||
<PolicyTable owner={this.state.owner} name={this.state.adapterName} mode={this.state.mode} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
@ -371,7 +272,7 @@ class AdapterEditPage extends React.Component {
|
||||
if (willExist) {
|
||||
this.props.history.push("/adapters");
|
||||
} else {
|
||||
this.props.history.push(`/adapters/${this.state.adapter.name}`);
|
||||
this.props.history.push(`/adapters/${this.state.owner}/${this.state.adapter.name}`);
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
|
@ -45,7 +45,7 @@ class AdapterListPage extends BaseListPage {
|
||||
const newAdapter = this.newAdapter();
|
||||
AdapterBackend.addAdapter(newAdapter)
|
||||
.then((res) => {
|
||||
this.props.history.push({pathname: `/adapters/${newAdapter.owner}/${newAdapter.name}`, mode: "add"});
|
||||
this.props.history.push({pathname: `/adapters/${newAdapter.organization}/${newAdapter.name}`, mode: "add"});
|
||||
}
|
||||
)
|
||||
.catch(error => {
|
||||
|
@ -145,6 +145,10 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_geetest.png`,
|
||||
url: "https://www.geetest.com",
|
||||
},
|
||||
"Cloudflare Turnstile": {
|
||||
logo: `${StaticBaseUrl}/img/social_cloudflare.png`,
|
||||
url: "https://www.cloudflare.com/products/turnstile/",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@ -416,9 +420,7 @@ export function goToLinkSoft(ths, link) {
|
||||
}
|
||||
|
||||
export function showMessage(type, text) {
|
||||
if (type === "") {
|
||||
return;
|
||||
} else if (type === "success") {
|
||||
if (type === "success") {
|
||||
message.success(text);
|
||||
} else if (type === "error") {
|
||||
message.error(text);
|
||||
@ -445,8 +447,8 @@ export function deepCopy(obj) {
|
||||
return Object.assign({}, obj);
|
||||
}
|
||||
|
||||
export function addRow(array, row) {
|
||||
return [...array, row];
|
||||
export function addRow(array, row, position = "end") {
|
||||
return position === "end" ? [...array, row] : [row, ...array];
|
||||
}
|
||||
|
||||
export function prependRow(array, row) {
|
||||
@ -697,6 +699,7 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "hCaptcha", name: "hCaptcha"},
|
||||
{id: "Aliyun Captcha", name: "Aliyun Captcha"},
|
||||
{id: "GEETEST", name: "GEETEST"},
|
||||
{id: "Cloudflare Turnstile", name: "Cloudflare Turnstile"},
|
||||
]);
|
||||
} else {
|
||||
return [];
|
||||
|
@ -49,6 +49,7 @@ class UserEditPage extends React.Component {
|
||||
applications: [],
|
||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||
loading: true,
|
||||
returnUrl: null,
|
||||
};
|
||||
}
|
||||
|
||||
@ -57,6 +58,7 @@ class UserEditPage extends React.Component {
|
||||
this.getOrganizations();
|
||||
this.getApplicationsByOrganization(this.state.organizationName);
|
||||
this.getUserApplication();
|
||||
this.setReturnUrl();
|
||||
}
|
||||
|
||||
getUser() {
|
||||
@ -100,9 +102,14 @@ class UserEditPage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
getReturnUrl() {
|
||||
setReturnUrl() {
|
||||
const searchParams = new URLSearchParams(this.props.location.search);
|
||||
return searchParams.get("returnUrl");
|
||||
const returnUrl = searchParams.get("returnUrl");
|
||||
if (returnUrl !== null) {
|
||||
this.setState({
|
||||
returnUrl: returnUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
parseUserField(key, value) {
|
||||
@ -619,9 +626,8 @@ class UserEditPage extends React.Component {
|
||||
}
|
||||
} else {
|
||||
if (willExist) {
|
||||
const returnUrl = this.getReturnUrl();
|
||||
if (returnUrl) {
|
||||
window.location.href = returnUrl;
|
||||
if (this.state.returnUrl) {
|
||||
window.location.href = this.state.returnUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -335,8 +335,8 @@ class LoginPage extends React.Component {
|
||||
return (
|
||||
<Result
|
||||
status="error"
|
||||
title="Sign Up Error"
|
||||
subTitle={"The application does not allow to sign up new account"}
|
||||
title={i18next.t("application:Sign Up Error")}
|
||||
subTitle={i18next.t("application:The application does not allow to sign up new account")}
|
||||
extra={[
|
||||
<Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
|
||||
{
|
||||
@ -368,7 +368,7 @@ class LoginPage extends React.Component {
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please input your application!",
|
||||
message: i18next.t("application:Please input your application!"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
@ -379,7 +379,7 @@ class LoginPage extends React.Component {
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: "Please input your organization!",
|
||||
message: i18next.t("application:Please input your organization!"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
@ -681,7 +681,7 @@ class LoginPage extends React.Component {
|
||||
const accessToken = res.data;
|
||||
Setting.goToLink(`${oAuthParams.redirectUri}#${responseType}=${accessToken}?state=${oAuthParams.state}&token_type=bearer`);
|
||||
} else {
|
||||
Setting.showMessage("success", "Successfully logged in with webauthn credentials");
|
||||
Setting.showMessage("success", i18next.t("login:Successfully logged in with webauthn credentials"));
|
||||
Setting.goToLink("/");
|
||||
}
|
||||
} else {
|
||||
@ -689,7 +689,7 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Failed to connect to server: ${error}`);
|
||||
Setting.showMessage("error", `${i18next.t("application:Failed to connect to server: ")}${error}`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -232,8 +232,8 @@ class PromptPage extends React.Component {
|
||||
return (
|
||||
<Result
|
||||
status="error"
|
||||
title="Sign Up Error"
|
||||
subTitle={"You are unexpected to see this prompt page"}
|
||||
title={i18next.t("application:Sign Up Error")}
|
||||
subTitle={i18next.t("application:You are unexpected to see this prompt page")}
|
||||
extra={[
|
||||
<Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
|
||||
{
|
||||
|
@ -539,8 +539,8 @@ class SignupPage extends React.Component {
|
||||
return (
|
||||
<Result
|
||||
status="error"
|
||||
title="Sign Up Error"
|
||||
subTitle={"The application does not allow to sign up new account"}
|
||||
title={i18next.t("application:Sign Up Error")}
|
||||
subTitle={i18next.t("application:The application does not allow to sign up new account")}
|
||||
extra={[
|
||||
<Button type="primary" key="signin" onClick={() => Setting.redirectToLoginPage(application, this.props.history)}>
|
||||
{
|
||||
|
@ -70,6 +70,41 @@ export function deleteAdapter(Adapter) {
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function UpdatePolicy(owner, name, policy) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(policy);
|
||||
return fetch(`${Setting.ServerUrl}/api/update-policy?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(policy),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function AddPolicy(owner, name, policy) {
|
||||
return fetch(`${Setting.ServerUrl}/api/add-policy?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(policy),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function RemovePolicy(owner, name, policy) {
|
||||
return fetch(`${Setting.ServerUrl}/api/remove-policy?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
body: JSON.stringify(policy),
|
||||
headers: {
|
||||
"Accept-Language": Setting.getAcceptLanguage(),
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function syncPolicies(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/sync-policies?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "GET",
|
||||
|
@ -105,6 +105,21 @@ export const CaptchaWidget = ({captchaType, subType, siteKey, clientSecret, onCh
|
||||
}, 500);
|
||||
break;
|
||||
}
|
||||
case "Cloudflare Turnstile": {
|
||||
const tTimer = setInterval(() => {
|
||||
if (!window.turnstile) {
|
||||
loadScript("https://challenges.cloudflare.com/turnstile/v0/api.js");
|
||||
}
|
||||
if (window.turnstile && window.turnstile.render) {
|
||||
window.turnstile.render("#captcha", {
|
||||
sitekey: siteKey,
|
||||
callback: onChange,
|
||||
});
|
||||
clearInterval(tTimer);
|
||||
}
|
||||
}, 300);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
308
web/src/common/PoliciyTable.js
Normal file
308
web/src/common/PoliciyTable.js
Normal file
@ -0,0 +1,308 @@
|
||||
// Copyright 2022 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {DeleteOutlined, EditOutlined} from "@ant-design/icons";
|
||||
import {Button, Input, Popconfirm, Table, Tooltip} from "antd";
|
||||
import * as Setting from "../Setting";
|
||||
import * as AdapterBackend from "../backend/AdapterBackend";
|
||||
import i18next from "i18next";
|
||||
|
||||
class PolicyTable extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
policyLists: [],
|
||||
loading: false,
|
||||
editingIndex: "",
|
||||
oldPolicy: "",
|
||||
add: false,
|
||||
};
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
if (this.props.mode === "edit") {
|
||||
this.synPolicies();
|
||||
}
|
||||
}
|
||||
|
||||
isEditing = (index) => {
|
||||
return index === this.state.editingIndex;
|
||||
};
|
||||
|
||||
edit = (record, index) => {
|
||||
this.setState({editingIndex: index, oldPolicy: Setting.deepCopy(record)});
|
||||
};
|
||||
|
||||
cancel = (table, index) => {
|
||||
Object.keys(table[index]).forEach((key) => {
|
||||
table[index][key] = this.state.oldPolicy[key];
|
||||
});
|
||||
this.updateTable(table);
|
||||
this.setState({editingIndex: "", oldPolicy: ""});
|
||||
if (this.state.add) {
|
||||
this.deleteRow(this.state.policyLists, index);
|
||||
this.setState({add: false});
|
||||
}
|
||||
};
|
||||
|
||||
updateTable(table) {
|
||||
this.setState({policyLists: table});
|
||||
}
|
||||
|
||||
updateField(table, index, key, value) {
|
||||
table[index][key] = value;
|
||||
this.updateTable(table);
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {Ptype: "p"};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
table = Setting.addRow(table, row, "top");
|
||||
this.updateTable(table);
|
||||
this.edit(row, 0);
|
||||
this.setState({add: true});
|
||||
}
|
||||
|
||||
deleteRow(table, i) {
|
||||
table = Setting.deleteRow(table, i);
|
||||
this.updateTable(table);
|
||||
}
|
||||
|
||||
save(table, i) {
|
||||
this.state.add ? this.addPolicy(table, i) : this.updatePolicy(table, i);
|
||||
}
|
||||
|
||||
synPolicies() {
|
||||
this.setState({loading: true});
|
||||
AdapterBackend.syncPolicies(this.props.owner, this.props.name)
|
||||
.then((res) => {
|
||||
if (res.status !== "error") {
|
||||
this.setState({loading: false, policyLists: res});
|
||||
} else {
|
||||
this.setState({loading: false});
|
||||
Setting.showMessage("error", `Adapter failed to get policies, ${res.msg}`);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
this.setState({loading: false});
|
||||
Setting.showMessage("error", `Adapter failed to get policies, ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
updatePolicy(table, i) {
|
||||
AdapterBackend.UpdatePolicy(this.props.owner, this.props.name, [this.state.oldPolicy, table[i]]).then(res => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({editingIndex: "", oldPolicy: ""});
|
||||
Setting.showMessage("success", i18next.t("adapter:Update policy successfully"));
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t(`adapter:Update policy failed, ${res.msg}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addPolicy(table, i) {
|
||||
AdapterBackend.AddPolicy(this.props.owner, this.props.name, table[i]).then(res => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({editingIndex: "", oldPolicy: "", add: false});
|
||||
if (res.data !== "Affected") {
|
||||
Setting.showMessage("info", i18next.t("adapter:Repeated policy"));
|
||||
} else {
|
||||
Setting.showMessage("success", i18next.t("adapter:Add policy successfully"));
|
||||
}
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t(`adapter:Add policy failed, ${res.msg}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deletePolicy(table, i) {
|
||||
AdapterBackend.RemovePolicy(this.props.owner, this.props.name, table[i]).then(res => {
|
||||
if (res.status === "ok") {
|
||||
table = Setting.deleteRow(table, i);
|
||||
this.updateTable(table);
|
||||
Setting.showMessage("success", i18next.t("adapter:Delete policy successfully"));
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t(`adapter:Delete policy failed, ${res.msg}`));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(table) {
|
||||
const columns = [
|
||||
{
|
||||
title: "Rule Type",
|
||||
dataIndex: "Ptype",
|
||||
width: "100px",
|
||||
// render: (text, record, index) => {
|
||||
// const editing = this.isEditing(index);
|
||||
// return (
|
||||
// editing ?
|
||||
// <Input value={text} onChange={e => {
|
||||
// this.updateField(table, index, "Ptype", e.target.value);
|
||||
// }} />
|
||||
// : text
|
||||
// );
|
||||
// },
|
||||
},
|
||||
{
|
||||
title: "V0",
|
||||
dataIndex: "V0",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editing = this.isEditing(index);
|
||||
return (
|
||||
editing ?
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "V0", e.target.value);
|
||||
}} />
|
||||
: text
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "V1",
|
||||
dataIndex: "V1",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editing = this.isEditing(index);
|
||||
return (
|
||||
editing ?
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "V1", e.target.value);
|
||||
}} />
|
||||
: text
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "V2",
|
||||
dataIndex: "V2",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editing = this.isEditing(index);
|
||||
return (
|
||||
editing ?
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "V2", e.target.value);
|
||||
}} />
|
||||
: text
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "V3",
|
||||
dataIndex: "V3",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editing = this.isEditing(index);
|
||||
return (
|
||||
editing ?
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "V3", e.target.value);
|
||||
}} />
|
||||
: text
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "V4",
|
||||
dataIndex: "V4",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editing = this.isEditing(index);
|
||||
return (
|
||||
editing ?
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "V4", e.target.value);
|
||||
}} />
|
||||
: text
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "V5",
|
||||
dataIndex: "V5",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editing = this.isEditing(index);
|
||||
return (
|
||||
editing ?
|
||||
<Input value={text} onChange={e => {
|
||||
this.updateField(table, index, "V5", e.target.value);
|
||||
}} />
|
||||
: text
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "Option",
|
||||
key: "option",
|
||||
width: "100px",
|
||||
render: (text, record, index) => {
|
||||
const editable = this.isEditing(index);
|
||||
return editable ? (
|
||||
<span>
|
||||
<Button style={{marginRight: 8}} onClick={() => this.save(table, index)}>
|
||||
Save
|
||||
</Button>
|
||||
<Popconfirm title="Sure to cancel?" onConfirm={() => this.cancel(table, index)}>
|
||||
<a>Cancel</a>
|
||||
</Popconfirm>
|
||||
</span>
|
||||
) : (
|
||||
<div>
|
||||
<Tooltip placement="topLeft" title="Edit">
|
||||
<Button disabled={this.state.editingIndex !== ""} style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => this.edit(record, index)} />
|
||||
</Tooltip>
|
||||
<Tooltip placement="topLeft" title="Delete">
|
||||
<Button disabled={this.state.editingIndex !== ""} style={{marginRight: "5px"}} icon={<DeleteOutlined />} size="small" onClick={() => this.deletePolicy(table, index)} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
}];
|
||||
|
||||
return (
|
||||
<Table
|
||||
pagination={{
|
||||
defaultPageSize: 10,
|
||||
}}
|
||||
columns={columns} dataSource={table} rowKey="index" size="middle" bordered
|
||||
loading={this.state.loading}
|
||||
title={() => (
|
||||
<div>
|
||||
<Button disabled={this.state.editingIndex !== ""} style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (<>
|
||||
<Button type="primary" onClick={() => {this.synPolicies();}}>
|
||||
{i18next.t("adapter:Sync")}
|
||||
</Button>
|
||||
{
|
||||
this.renderTable(this.state.policyLists)
|
||||
}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default PolicyTable;
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "Registrieren"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "Add policy successfully",
|
||||
"Delete policy successfully": "Delete policy successfully",
|
||||
"Edit Adapter": "Edit Adapter",
|
||||
"New Adapter": "New Adapter",
|
||||
"Policies": "Policies",
|
||||
"Policies - Tooltip": "Policies - Tooltip",
|
||||
"Sync": "Sync"
|
||||
"Repeated policy": "Repeated policy",
|
||||
"Sync": "Sync",
|
||||
"Update policy successfully": "Update policy successfully"
|
||||
},
|
||||
"application": {
|
||||
"Always": "Always",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "Aktiviere Anmeldesession - Tooltip",
|
||||
"Enable signup": "Anmeldung aktivieren",
|
||||
"Enable signup - Tooltip": "Whether to allow users to sign up",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"File uploaded successfully": "Datei erfolgreich hochgeladen",
|
||||
"Form CSS": "Form CSS",
|
||||
"Form CSS - Edit": "Form CSS - Edit",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "None",
|
||||
"Password ON": "Passwort AN",
|
||||
"Password ON - Tooltip": "Whether to allow password login",
|
||||
"Please input your application!": "Please input your application!",
|
||||
"Please input your organization!": "Please input your organization!",
|
||||
"Please select a HTML file": "Bitte wählen Sie eine HTML-Datei",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "Weiterleitungs-URL",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "Side panel HTML",
|
||||
"Side panel HTML - Edit": "Side panel HTML - Edit",
|
||||
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Signin session": "Anmeldesitzung",
|
||||
"Signup items": "Artikel registrieren",
|
||||
"Signup items - Tooltip": "Signup items that need to be filled in when users register",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
"Token expire": "Token läuft ab",
|
||||
"Token expire - Tooltip": "Token läuft ab - Tooltip",
|
||||
"Token format": "Token-Format",
|
||||
"Token format - Tooltip": "Token-Format - Tooltip"
|
||||
"Token format - Tooltip": "Token-Format - Tooltip",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bitgröße",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "Sign in with WebAuthn",
|
||||
"Sign in with {type}": "Mit {type} anmelden",
|
||||
"Signing in...": "Anmelden...",
|
||||
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
|
||||
"The input is not valid Email or Phone!": "Die Eingabe ist keine gültige E-Mail oder Telefon!",
|
||||
"To access": "Zu Zugriff",
|
||||
"Verification Code": "Verification Code",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"Visible": "Visible",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "alarmtyp"
|
||||
},
|
||||
"record": {
|
||||
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "Sign Up"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "Add policy successfully",
|
||||
"Delete policy successfully": "Delete policy successfully",
|
||||
"Edit Adapter": "Edit Adapter",
|
||||
"New Adapter": "New Adapter",
|
||||
"Policies": "Policies",
|
||||
"Policies - Tooltip": "Policies - Tooltip",
|
||||
"Sync": "Sync"
|
||||
"Repeated policy": "Repeated policy",
|
||||
"Sync": "Sync",
|
||||
"Update policy successfully": "Update policy successfully"
|
||||
},
|
||||
"application": {
|
||||
"Always": "Always",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
|
||||
"Enable signup": "Enable signup",
|
||||
"Enable signup - Tooltip": "Enable signup - Tooltip",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"File uploaded successfully": "File uploaded successfully",
|
||||
"Form CSS": "Form CSS",
|
||||
"Form CSS - Edit": "Form CSS - Edit",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "None",
|
||||
"Password ON": "Password ON",
|
||||
"Password ON - Tooltip": "Password ON - Tooltip",
|
||||
"Please input your application!": "Please input your application!",
|
||||
"Please input your organization!": "Please input your organization!",
|
||||
"Please select a HTML file": "Please select a HTML file",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "Redirect URL",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "Side panel HTML",
|
||||
"Side panel HTML - Edit": "Side panel HTML - Edit",
|
||||
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Signin session": "Signin session",
|
||||
"Signup items": "Signup items",
|
||||
"Signup items - Tooltip": "Signup items - Tooltip",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
"Token expire": "Token expire",
|
||||
"Token expire - Tooltip": "Token expire - Tooltip",
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
"Token format - Tooltip": "Token format - Tooltip",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "Sign in with WebAuthn",
|
||||
"Sign in with {type}": "Sign in with {type}",
|
||||
"Signing in...": "Signing in...",
|
||||
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
|
||||
"The input is not valid Email or Phone!": "The input is not valid Email or Phone!",
|
||||
"To access": "To access",
|
||||
"Verification Code": "Verification Code",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"Visible": "Visible",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "alertType"
|
||||
},
|
||||
"record": {
|
||||
|
@ -38,6 +38,7 @@
|
||||
"SAML metadata URL copied to clipboard successfully": "SAML metadata URL copiado al portapapeles con éxito",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Signin session": "Signin session",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signup items": "Signup items",
|
||||
"Signup items - Tooltip": "Signup items - Tooltip",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
@ -45,6 +46,7 @@
|
||||
"Token expire - Tooltip": "Expiración del Token - Tooltip",
|
||||
"Token format": "Formato del Token",
|
||||
"Token format - Tooltip": "Formato del Token - Tooltip",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page",
|
||||
"Rule": "Rule",
|
||||
"None": "None",
|
||||
"Always": "Always"
|
||||
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "S'inscrire"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "Add policy successfully",
|
||||
"Delete policy successfully": "Delete policy successfully",
|
||||
"Edit Adapter": "Edit Adapter",
|
||||
"New Adapter": "New Adapter",
|
||||
"Policies": "Policies",
|
||||
"Policies - Tooltip": "Policies - Tooltip",
|
||||
"Sync": "Sync"
|
||||
"Repeated policy": "Repeated policy",
|
||||
"Sync": "Sync",
|
||||
"Update policy successfully": "Update policy successfully"
|
||||
},
|
||||
"application": {
|
||||
"Always": "Always",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "Activer la session de connexion - infobulle",
|
||||
"Enable signup": "Activer l'inscription",
|
||||
"Enable signup - Tooltip": "Whether to allow users to sign up",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"File uploaded successfully": "Fichier téléchargé avec succès",
|
||||
"Form CSS": "Form CSS",
|
||||
"Form CSS - Edit": "Form CSS - Edit",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "None",
|
||||
"Password ON": "Mot de passe activé",
|
||||
"Password ON - Tooltip": "Whether to allow password login",
|
||||
"Please input your application!": "Please input your application!",
|
||||
"Please input your organization!": "Please input your organization!",
|
||||
"Please select a HTML file": "Veuillez sélectionner un fichier HTML",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "URL de redirection",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "Side panel HTML",
|
||||
"Side panel HTML - Edit": "Side panel HTML - Edit",
|
||||
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Signin session": "Connexion à la session",
|
||||
"Signup items": "Inscrire des éléments",
|
||||
"Signup items - Tooltip": "Signup items that need to be filled in when users register",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
"Token expire": "Expiration du jeton",
|
||||
"Token expire - Tooltip": "Expiration du jeton - Info-bulle",
|
||||
"Token format": "Format du jeton",
|
||||
"Token format - Tooltip": "Format du jeton - infobulle"
|
||||
"Token format - Tooltip": "Format du jeton - infobulle",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Taille du bit",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "Sign in with WebAuthn",
|
||||
"Sign in with {type}": "Se connecter avec {type}",
|
||||
"Signing in...": "Connexion en cours...",
|
||||
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
|
||||
"The input is not valid Email or Phone!": "L'entrée n'est pas valide Email ou Téléphone !",
|
||||
"To access": "Pour accéder à",
|
||||
"Verification Code": "Verification Code",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"Visible": "Visible",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "Type d'alerte"
|
||||
},
|
||||
"record": {
|
||||
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "新規登録"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "Add policy successfully",
|
||||
"Delete policy successfully": "Delete policy successfully",
|
||||
"Edit Adapter": "Edit Adapter",
|
||||
"New Adapter": "New Adapter",
|
||||
"Policies": "Policies",
|
||||
"Policies - Tooltip": "Policies - Tooltip",
|
||||
"Sync": "Sync"
|
||||
"Repeated policy": "Repeated policy",
|
||||
"Sync": "Sync",
|
||||
"Update policy successfully": "Update policy successfully"
|
||||
},
|
||||
"application": {
|
||||
"Always": "Always",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
|
||||
"Enable signup": "サインアップを有効にする",
|
||||
"Enable signup - Tooltip": "Whether to allow users to sign up",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"File uploaded successfully": "ファイルが正常にアップロードされました",
|
||||
"Form CSS": "Form CSS",
|
||||
"Form CSS - Edit": "Form CSS - Edit",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "None",
|
||||
"Password ON": "パスワードON",
|
||||
"Password ON - Tooltip": "Whether to allow password login",
|
||||
"Please input your application!": "Please input your application!",
|
||||
"Please input your organization!": "Please input your organization!",
|
||||
"Please select a HTML file": "HTMLファイルを選択してください",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "リダイレクトURL",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "Side panel HTML",
|
||||
"Side panel HTML - Edit": "Side panel HTML - Edit",
|
||||
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Signin session": "サインインセッション",
|
||||
"Signup items": "アイテムの登録",
|
||||
"Signup items - Tooltip": "Signup items that need to be filled in when users register",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
"Token expire": "トークンの有効期限",
|
||||
"Token expire - Tooltip": "トークンの有効期限 - ツールチップ",
|
||||
"Token format": "トークンのフォーマット",
|
||||
"Token format - Tooltip": "トークンフォーマット - ツールチップ"
|
||||
"Token format - Tooltip": "トークンフォーマット - ツールチップ",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "ビットサイズ",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "Sign in with WebAuthn",
|
||||
"Sign in with {type}": "{type} でサインイン",
|
||||
"Signing in...": "サインイン中...",
|
||||
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
|
||||
"The input is not valid Email or Phone!": "入力されたメールアドレスまたは電話番号が正しくありません。",
|
||||
"To access": "アクセスするには",
|
||||
"Verification Code": "Verification Code",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"Visible": "Visible",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "alertType"
|
||||
},
|
||||
"record": {
|
||||
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "Sign Up"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "Add policy successfully",
|
||||
"Delete policy successfully": "Delete policy successfully",
|
||||
"Edit Adapter": "Edit Adapter",
|
||||
"New Adapter": "New Adapter",
|
||||
"Policies": "Policies",
|
||||
"Policies - Tooltip": "Policies - Tooltip",
|
||||
"Sync": "Sync"
|
||||
"Repeated policy": "Repeated policy",
|
||||
"Sync": "Sync",
|
||||
"Update policy successfully": "Update policy successfully"
|
||||
},
|
||||
"application": {
|
||||
"Always": "Always",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "Enable signin session - Tooltip",
|
||||
"Enable signup": "Enable signup",
|
||||
"Enable signup - Tooltip": "Whether to allow users to sign up",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"File uploaded successfully": "File uploaded successfully",
|
||||
"Form CSS": "Form CSS",
|
||||
"Form CSS - Edit": "Form CSS - Edit",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "None",
|
||||
"Password ON": "Password ON",
|
||||
"Password ON - Tooltip": "Whether to allow password login",
|
||||
"Please input your application!": "Please input your application!",
|
||||
"Please input your organization!": "Please input your organization!",
|
||||
"Please select a HTML file": "Please select a HTML file",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Redirect URL": "Redirect URL",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "Side panel HTML",
|
||||
"Side panel HTML - Edit": "Side panel HTML - Edit",
|
||||
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"Signin session": "Signin session",
|
||||
"Signup items": "Signup items",
|
||||
"Signup items - Tooltip": "Signup items that need to be filled in when users register",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
"Token expire": "Token expire",
|
||||
"Token expire - Tooltip": "Token expire - Tooltip",
|
||||
"Token format": "Token format",
|
||||
"Token format - Tooltip": "Token format - Tooltip"
|
||||
"Token format - Tooltip": "Token format - Tooltip",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Bit size",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "Sign in with WebAuthn",
|
||||
"Sign in with {type}": "Sign in with {type}",
|
||||
"Signing in...": "Signing in...",
|
||||
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
|
||||
"The input is not valid Email or Phone!": "The input is not valid Email or Phone!",
|
||||
"To access": "To access",
|
||||
"Verification Code": "Verification Code",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"Visible": "Visible",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "alertType"
|
||||
},
|
||||
"record": {
|
||||
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "Регистрация"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "Add policy successfully",
|
||||
"Delete policy successfully": "Delete policy successfully",
|
||||
"Edit Adapter": "Edit Adapter",
|
||||
"New Adapter": "New Adapter",
|
||||
"Policies": "Policies",
|
||||
"Policies - Tooltip": "Policies - Tooltip",
|
||||
"Sync": "Sync"
|
||||
"Repeated policy": "Repeated policy",
|
||||
"Sync": "Sync",
|
||||
"Update policy successfully": "Update policy successfully"
|
||||
},
|
||||
"application": {
|
||||
"Always": "Always",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "Включить сеанс входа - Подсказка",
|
||||
"Enable signup": "Включить регистрацию",
|
||||
"Enable signup - Tooltip": "Whether to allow users to sign up",
|
||||
"Failed to connect to server": "Failed to connect to server",
|
||||
"File uploaded successfully": "Файл успешно загружен",
|
||||
"Form CSS": "Form CSS",
|
||||
"Form CSS - Edit": "Form CSS - Edit",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "None",
|
||||
"Password ON": "Пароль ВКЛ",
|
||||
"Password ON - Tooltip": "Whether to allow password login",
|
||||
"Please input your application!": "Please input your application!",
|
||||
"Please input your organization!": "Please input your organization!",
|
||||
"Please select a HTML file": "Пожалуйста, выберите HTML-файл",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "Ссылка на страницу успешно скопирована в буфер обмена, пожалуйста, вставьте ее в окно инкогнито или другой браузер",
|
||||
"Redirect URL": "URL перенаправления",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "Side panel HTML",
|
||||
"Side panel HTML - Edit": "Side panel HTML - Edit",
|
||||
"Side panel HTML - Tooltip": "Side panel HTML - Tooltip",
|
||||
"Sign Up Error": "Sign Up Error",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL страницы входа успешно скопирован в буфер обмена, пожалуйста, вставьте его в окно инкогнито или другой браузер",
|
||||
"Signin session": "Сессия входа",
|
||||
"Signup items": "Элементы регистрации",
|
||||
"Signup items - Tooltip": "Signup items that need to be filled in when users register",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "URL страницы регистрации успешно скопирован в буфер обмена, пожалуйста, вставьте его в окно инкогнито или другой браузер",
|
||||
"The application does not allow to sign up new account": "The application does not allow to sign up new account",
|
||||
"Token expire": "Токен истекает",
|
||||
"Token expire - Tooltip": "Истек токен - Подсказка",
|
||||
"Token format": "Формат токена",
|
||||
"Token format - Tooltip": "Формат токена - Подсказка"
|
||||
"Token format - Tooltip": "Формат токена - Подсказка",
|
||||
"You are unexpected to see this prompt page": "You are unexpected to see this prompt page"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "Размер бита",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "Sign in with WebAuthn",
|
||||
"Sign in with {type}": "Войти с помощью {type}",
|
||||
"Signing in...": "Вход...",
|
||||
"Successfully logged in with webauthn credentials": "Successfully logged in with webauthn credentials",
|
||||
"The input is not valid Email or Phone!": "Введен неверный адрес электронной почты или телефон!",
|
||||
"To access": "На доступ",
|
||||
"Verification Code": "Verification Code",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - Tooltip",
|
||||
"Visible": "Visible",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "тип оповещения"
|
||||
},
|
||||
"record": {
|
||||
|
@ -6,11 +6,15 @@
|
||||
"Sign Up": "注册"
|
||||
},
|
||||
"adapter": {
|
||||
"Add policy successfully": "添加策略成功",
|
||||
"Delete policy successfully": "删除策略成功",
|
||||
"Edit Adapter": "编辑适配器",
|
||||
"New Adapter": "添加适配器",
|
||||
"Policies": "策略",
|
||||
"Policies - Tooltip": "策略",
|
||||
"Sync": "同步"
|
||||
"Repeated policy": "策略重复",
|
||||
"Sync": "同步",
|
||||
"Update policy successfully": "更新策略成功"
|
||||
},
|
||||
"application": {
|
||||
"Always": "始终开启",
|
||||
@ -34,6 +38,7 @@
|
||||
"Enable signin session - Tooltip": "从应用登录Casdoor后,Casdoor是否保持会话",
|
||||
"Enable signup": "启用注册",
|
||||
"Enable signup - Tooltip": "是否允许用户注册",
|
||||
"Failed to connect to server": "无法连接服务器",
|
||||
"File uploaded successfully": "文件上传成功",
|
||||
"Form CSS": "表单CSS",
|
||||
"Form CSS - Edit": "编辑表单CSS",
|
||||
@ -47,6 +52,8 @@
|
||||
"None": "关闭",
|
||||
"Password ON": "开启密码",
|
||||
"Password ON - Tooltip": "是否允许密码登录",
|
||||
"Please input your application!": "请输入你的应用",
|
||||
"Please input your organization!": "请输入你的组织",
|
||||
"Please select a HTML file": "请选择一个HTML文件",
|
||||
"Prompt page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "提醒页面URL已成功复制到剪贴板,请粘贴到当前浏览器的隐身模式窗口或另一个浏览器访问",
|
||||
"Redirect URL": "重定向 URL",
|
||||
@ -62,15 +69,18 @@
|
||||
"Side panel HTML": "侧面板 HTML",
|
||||
"Side panel HTML - Edit": "侧面板 HTML - 编辑",
|
||||
"Side panel HTML - Tooltip": "侧面板 HTML - Tooltip",
|
||||
"Sign Up Error": "注册错误",
|
||||
"Signin page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "登录页面URL已成功复制到剪贴板,请粘贴到当前浏览器的隐身模式窗口或另一个浏览器访问",
|
||||
"Signin session": "保持登录会话",
|
||||
"Signup items": "注册项",
|
||||
"Signup items - Tooltip": "注册用户注册时需要填写的项目",
|
||||
"Signup page URL copied to clipboard successfully, please paste it into the incognito window or another browser": "注册页面URL已成功复制到剪贴板,请粘贴到当前浏览器的隐身模式窗口或另一个浏览器访问",
|
||||
"The application does not allow to sign up new account": "该应用不允许注册新账户",
|
||||
"Token expire": "Access Token过期",
|
||||
"Token expire - Tooltip": "Access Token过期时间",
|
||||
"Token format": "Access Token格式",
|
||||
"Token format - Tooltip": "Access Token格式"
|
||||
"Token format - Tooltip": "Access Token格式",
|
||||
"You are unexpected to see this prompt page": "错误跳转至提醒页面"
|
||||
},
|
||||
"cert": {
|
||||
"Bit size": "位大小",
|
||||
@ -292,6 +302,7 @@
|
||||
"Sign in with WebAuthn": "WebAuthn登录",
|
||||
"Sign in with {type}": "{type}登录",
|
||||
"Signing in...": "正在登录...",
|
||||
"Successfully logged in with webauthn credentials": "成功使用WebAuthn证书登录",
|
||||
"The input is not valid Email or Phone!": "您输入的电子邮箱格式或手机号有误!",
|
||||
"To access": "访问",
|
||||
"Verification Code": "验证码",
|
||||
@ -555,6 +566,7 @@
|
||||
"UserInfo URL": "UserInfo URL",
|
||||
"UserInfo URL - Tooltip": "UserInfo URL - 工具提示",
|
||||
"Visible": "是否可见",
|
||||
"admin (share)": "admin (share)",
|
||||
"alertType": "警报类型"
|
||||
},
|
||||
"record": {
|
||||
|
Reference in New Issue
Block a user