Compare commits

...

5 Commits

40 changed files with 627 additions and 41 deletions

View File

@ -140,6 +140,9 @@ func (c *ApiController) SendEmail() {
}
content = strings.Replace(content, "%{user.friendlyName}", userString, 1)
matchContent := object.ResetLinkReg.Find([]byte(content))
content = strings.Replace(content, string(matchContent), "", -1)
for _, receiver := range emailForm.Receivers {
err = object.SendEmail(provider, emailForm.Title, content, receiver, emailForm.Sender)
if err != nil {

View File

@ -258,7 +258,7 @@ func (c *ApiController) SendVerificationCode() {
return
}
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest)
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest, vform.Method, c.Ctx.Request.Host, application.Name)
case object.VerifyTypePhone:
if vform.Method == LoginVerification || vform.Method == ForgetVerification {
if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest {

View File

@ -27,16 +27,22 @@ import (
)
type LarkIdProvider struct {
Client *http.Client
Config *oauth2.Config
Client *http.Client
Config *oauth2.Config
LarkDomain string
}
func NewLarkIdProvider(clientId string, clientSecret string, redirectUrl string) *LarkIdProvider {
func NewLarkIdProvider(clientId string, clientSecret string, redirectUrl string, useGlobalEndpoint bool) *LarkIdProvider {
idp := &LarkIdProvider{}
if useGlobalEndpoint {
idp.LarkDomain = "https://open.larksuite.com"
} else {
idp.LarkDomain = "https://open.feishu.cn"
}
config := idp.getConfig(clientId, clientSecret, redirectUrl)
idp.Config = config
return idp
}
@ -47,7 +53,7 @@ func (idp *LarkIdProvider) SetHttpClient(client *http.Client) {
// getConfig return a point of Config, which describes a typical 3-legged OAuth2 flow
func (idp *LarkIdProvider) getConfig(clientId string, clientSecret string, redirectUrl string) *oauth2.Config {
endpoint := oauth2.Endpoint{
TokenURL: "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
TokenURL: idp.LarkDomain + "/open-apis/auth/v3/tenant_access_token/internal",
}
config := &oauth2.Config{
@ -162,6 +168,7 @@ type LarkUserInfo struct {
} `json:"data"`
}
// GetUserInfo use LarkAccessToken gotten before return LinkedInUserInf
// GetUserInfo use LarkAccessToken gotten before return LinkedInUserInfo
// get more detail via: https://docs.microsoft.com/en-us/linkedin/consumer/integrations/self-serve/sign-in-with-linkedin?context=linkedin/consumer/context
func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
@ -175,7 +182,7 @@ func (idp *LarkIdProvider) GetUserInfo(token *oauth2.Token) (*UserInfo, error) {
return nil, err
}
req, err := http.NewRequest("POST", "https://open.feishu.cn/open-apis/authen/v1/access_token", strings.NewReader(string(data)))
req, err := http.NewRequest("POST", idp.LarkDomain+"/open-apis/authen/v1/access_token", strings.NewReader(string(data)))
if err != nil {
return nil, err
}

View File

@ -87,7 +87,7 @@ func GetIdProvider(idpInfo *ProviderInfo, redirectUrl string) (IdProvider, error
return nil, fmt.Errorf("WeCom provider subType: %s is not supported", idpInfo.SubType)
}
case "Lark":
return NewLarkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
return NewLarkIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl, idpInfo.DisableSsl), nil
case "GitLab":
return NewGitlabIdProvider(idpInfo.ClientId, idpInfo.ClientSecret, redirectUrl), nil
case "ADFS":

View File

@ -19,6 +19,8 @@ import (
"fmt"
"math"
"math/rand"
"net/url"
"regexp"
"strings"
"time"
@ -33,6 +35,8 @@ type VerifyResult struct {
Msg string
}
var ResetLinkReg *regexp.Regexp
const (
VerificationSuccess = iota
wrongCodeError
@ -45,6 +49,10 @@ const (
VerifyTypeEmail = "email"
)
func init() {
ResetLinkReg = regexp.MustCompile("(?s)<reset-link>(.*?)</reset-link>")
}
type VerificationRecord struct {
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
Name string `xorm:"varchar(100) notnull pk" json:"name"`
@ -81,7 +89,7 @@ func IsAllowSend(user *User, remoteAddr, recordType string) error {
return nil
}
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string) error {
func SendVerificationCodeToEmail(organization *Organization, user *User, provider *Provider, remoteAddr string, dest string, method string, host string, applicationName string) error {
sender := organization.DisplayName
title := provider.Title
@ -93,6 +101,23 @@ func SendVerificationCodeToEmail(organization *Organization, user *User, provide
// "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes."
content := strings.Replace(provider.Content, "%s", code, 1)
if method == "forget" {
originFrontend, _ := getOriginFromHost(host)
query := url.Values{}
query.Add("code", code)
query.Add("username", user.Name)
query.Add("dest", util.GetMaskedEmail(dest))
forgetURL := originFrontend + "/forget/" + applicationName + "?" + query.Encode()
content = strings.Replace(content, "%link", forgetURL, -1)
content = strings.Replace(content, "<reset-link>", "", -1)
content = strings.Replace(content, "</reset-link>", "", -1)
} else {
matchContent := ResetLinkReg.Find([]byte(content))
content = strings.Replace(content, string(matchContent), "", -1)
}
userString := "Hi"
if user != nil {
userString = user.GetFriendlyName()

View File

@ -404,6 +404,7 @@ class App extends Component {
account={this.state.account}
theme={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}
requiredEnableMfa={this.state.requiredEnableMfa}
updateApplication={(application) => {
this.setState({
application: application,

View File

@ -241,6 +241,21 @@ class PlanEditPage extends React.Component {
{id: "HKD", name: "HKD"},
{id: "SGD", name: "SGD"},
{id: "BRL", name: "BRL"},
{id: "PLN", name: "PLN"},
{id: "KRW", name: "KRW"},
{id: "INR", name: "INR"},
{id: "RUB", name: "RUB"},
{id: "MXN", name: "MXN"},
{id: "ZAR", name: "ZAR"},
{id: "TRY", name: "TRY"},
{id: "SEK", name: "SEK"},
{id: "NOK", name: "NOK"},
{id: "DKK", name: "DKK"},
{id: "THB", name: "THB"},
{id: "MYR", name: "MYR"},
{id: "TWD", name: "TWD"},
{id: "CZK", name: "CZK"},
{id: "HUF", name: "HUF"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>

View File

@ -141,6 +141,36 @@ class ProductBuyPage extends React.Component {
return "S$";
} else if (product?.currency === "BRL") {
return "R$";
} else if (product?.currency === "PLN") {
return "zł";
} else if (product?.currency === "KRW") {
return "₩";
} else if (product?.currency === "INR") {
return "₹";
} else if (product?.currency === "RUB") {
return "₽";
} else if (product?.currency === "MXN") {
return "$";
} else if (product?.currency === "ZAR") {
return "R";
} else if (product?.currency === "TRY") {
return "₺";
} else if (product?.currency === "SEK") {
return "kr";
} else if (product?.currency === "NOK") {
return "kr";
} else if (product?.currency === "DKK") {
return "kr";
} else if (product?.currency === "THB") {
return "฿";
} else if (product?.currency === "MYR") {
return "RM";
} else if (product?.currency === "TWD") {
return "NT$";
} else if (product?.currency === "CZK") {
return "Kč";
} else if (product?.currency === "HUF") {
return "Ft";
} else {
return "(Unknown currency)";
}

View File

@ -218,6 +218,21 @@ class ProductEditPage extends React.Component {
{id: "HKD", name: "HKD"},
{id: "SGD", name: "SGD"},
{id: "BRL", name: "BRL"},
{id: "PLN", name: "PLN"},
{id: "KRW", name: "KRW"},
{id: "INR", name: "INR"},
{id: "RUB", name: "RUB"},
{id: "MXN", name: "MXN"},
{id: "ZAR", name: "ZAR"},
{id: "TRY", name: "TRY"},
{id: "SEK", name: "SEK"},
{id: "NOK", name: "NOK"},
{id: "DKK", name: "DKK"},
{id: "THB", name: "THB"},
{id: "MYR", name: "MYR"},
{id: "TWD", name: "TWD"},
{id: "CZK", name: "CZK"},
{id: "HUF", name: "HUF"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>

View File

@ -931,10 +931,12 @@ class ProviderEditPage extends React.Component {
)
}
{
this.state.provider.type !== "Google" ? null : (
this.state.provider.type !== "Google" && this.state.provider.type !== "Lark" ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Get phone number"), i18next.t("provider:Get phone number - Tooltip"))} :
{this.state.provider.type === "Google" ?
Setting.getLabel(i18next.t("provider:Get phone number"), i18next.t("provider:Get phone number - Tooltip"))
: Setting.getLabel(i18next.t("provider:Use global endpoint"), i18next.t("provider:Use global endpoint - Tooltip"))} :
</Col>
<Col span={1} >
<Switch disabled={!this.state.provider.clientId} checked={this.state.provider.disableSsl} onChange={checked => {
@ -1227,7 +1229,7 @@ class ProviderEditPage extends React.Component {
</Col>
<Col span={22} >
<Row style={{marginTop: "20px"}} >
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes.")} >
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateProviderField("content", "You have requested a verification code at Casdoor. Here is your code: %s, please enter in 5 minutes. <reset-link>Or click %link to reset</reset-link>")} >
{i18next.t("provider:Reset to Default Text")}
</Button>
<Button style={{marginLeft: "10px", marginBottom: "5px"}} type="primary" onClick={() => this.updateProviderField("content", Setting.getDefaultHtmlEmailContent())} >

View File

@ -1516,6 +1516,54 @@ export function getCurrencySymbol(currency) {
return "$";
} else if (currency === "CNY" || currency === "cny") {
return "¥";
} else if (currency === "EUR" || currency === "eur") {
return "€";
} else if (currency === "JPY" || currency === "jpy") {
return "¥";
} else if (currency === "GBP" || currency === "gbp") {
return "£";
} else if (currency === "AUD" || currency === "aud") {
return "A$";
} else if (currency === "CAD" || currency === "cad") {
return "C$";
} else if (currency === "CHF" || currency === "chf") {
return "CHF";
} else if (currency === "HKD" || currency === "hkd") {
return "HK$";
} else if (currency === "SGD" || currency === "sgd") {
return "S$";
} else if (currency === "BRL" || currency === "brl") {
return "R$";
} else if (currency === "PLN" || currency === "pln") {
return "zł";
} else if (currency === "KRW" || currency === "krw") {
return "₩";
} else if (currency === "INR" || currency === "inr") {
return "₹";
} else if (currency === "RUB" || currency === "rub") {
return "₽";
} else if (currency === "MXN" || currency === "mxn") {
return "$";
} else if (currency === "ZAR" || currency === "zar") {
return "R";
} else if (currency === "TRY" || currency === "try") {
return "₺";
} else if (currency === "SEK" || currency === "sek") {
return "kr";
} else if (currency === "NOK" || currency === "nok") {
return "kr";
} else if (currency === "DKK" || currency === "dkk") {
return "kr";
} else if (currency === "THB" || currency === "thb") {
return "฿";
} else if (currency === "MYR" || currency === "myr") {
return "RM";
} else if (currency === "TWD" || currency === "twd") {
return "NT$";
} else if (currency === "CZK" || currency === "czk") {
return "Kč";
} else if (currency === "HUF" || currency === "huf") {
return "Ft";
} else {
return currency;
}
@ -1580,6 +1628,11 @@ export function getDefaultHtmlEmailContent() {
<div class="code">
%s
</div>
<reset-link>
<div class="link">
Or click this <a href="%link">link</a> to reset
</div>
</reset-link>
<p>Thanks</p>
<p>Casbin Team</p>
<hr>
@ -1614,6 +1667,36 @@ export function getCurrencyText(product) {
return i18next.t("currency:SGD");
} else if (product?.currency === "BRL") {
return i18next.t("currency:BRL");
} else if (product?.currency === "PLN") {
return i18next.t("currency:PLN");
} else if (product?.currency === "KRW") {
return i18next.t("currency:KRW");
} else if (product?.currency === "INR") {
return i18next.t("currency:INR");
} else if (product?.currency === "RUB") {
return i18next.t("currency:RUB");
} else if (product?.currency === "MXN") {
return i18next.t("currency:MXN");
} else if (product?.currency === "ZAR") {
return i18next.t("currency:ZAR");
} else if (product?.currency === "TRY") {
return i18next.t("currency:TRY");
} else if (product?.currency === "SEK") {
return i18next.t("currency:SEK");
} else if (product?.currency === "NOK") {
return i18next.t("currency:NOK");
} else if (product?.currency === "DKK") {
return i18next.t("currency:DKK");
} else if (product?.currency === "THB") {
return i18next.t("currency:THB");
} else if (product?.currency === "MYR") {
return i18next.t("currency:MYR");
} else if (product?.currency === "TWD") {
return i18next.t("currency:TWD");
} else if (product?.currency === "CZK") {
return i18next.t("currency:CZK");
} else if (product?.currency === "HUF") {
return i18next.t("currency:HUF");
} else {
return "(Unknown currency)";
}

View File

@ -31,18 +31,21 @@ const {Option} = Select;
class ForgetPage extends React.Component {
constructor(props) {
super(props);
const queryParams = new URLSearchParams(location.search);
this.state = {
classes: props,
applicationName: props.applicationName ?? props.match.params?.applicationName,
msg: null,
name: props.account ? props.account.name : "",
name: props.account ? props.account.name : queryParams.get("username"),
username: props.account ? props.account.name : "",
phone: "",
email: "",
dest: "",
isVerifyTypeFixed: false,
verifyType: "", // "email", "phone"
current: 0,
current: queryParams.get("code") ? 2 : 0,
code: queryParams.get("code"),
queryParams: queryParams,
};
this.form = React.createRef();
}
@ -148,9 +151,26 @@ class ForgetPage extends React.Component {
}
}
onFinish(values) {
async onFinish(values) {
values.username = this.state.name;
values.userOwner = this.getApplicationObj()?.organizationObj.name;
if (this.state.queryParams.get("code")) {
const res = await UserBackend.verifyCode({
application: this.getApplicationObj().name,
organization: values.userOwner,
username: this.state.queryParams.get("dest"),
name: this.state.name,
code: this.state.code,
type: "login",
});
if (res.status !== "ok") {
Setting.showMessage("error", res.msg);
return;
}
}
UserBackend.setPassword(values.userOwner, values.username, "", values?.newPassword, this.state.code).then(res => {
if (res.status === "ok") {
const linkInStorage = sessionStorage.getItem("signinUrl");

View File

@ -1040,6 +1040,10 @@ class LoginPage extends React.Component {
return null;
}
if (this.props.requiredEnableMfa) {
return null;
}
if (this.state.userCode && this.state.userCodeStatus === "success") {
return null;
}

View File

@ -68,6 +68,7 @@ const authInfo = {
Lark: {
// scope: "email",
endpoint: "https://open.feishu.cn/open-apis/authen/v1/index",
endpoint2: "https://accounts.larksuite.com/open-apis/authen/v1/authorize",
},
GitLab: {
scope: "read_user+profile",
@ -406,6 +407,8 @@ export function getAuthUrl(application, provider, method, code) {
if (provider.domain) {
endpoint = `${provider.domain}/apps/oauth2/authorize`;
}
} else if (provider.type === "Lark" && provider.disableSsl) {
endpoint = authInfo[provider.type].endpoint2;
}
if (provider.type === "Google" || provider.type === "GitHub" || provider.type === "Facebook"
@ -460,6 +463,9 @@ export function getAuthUrl(application, provider, method, code) {
return `https://error:not-supported-provider-sub-type:${provider.subType}`;
}
} else if (provider.type === "Lark") {
if (provider.disableSsl) {
redirectUri = encodeURIComponent(redirectUri);
}
return `${endpoint}?app_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}`;
} else if (provider.type === "ADFS") {
return `${provider.domain}/adfs/oauth2/authorize?client_id=${provider.clientId}&redirect_uri=${redirectUri}&state=${state}&response_type=code&nonce=casdoor&scope=openid`;

View File

@ -82,7 +82,7 @@ export function renderPasswordPopover(options, password) {
}
export function checkPasswordComplexity(password, options) {
if (password.length === 0) {
if (!password?.length) {
return i18next.t("login:Please input your password!");
}

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Upravit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "ویرایش Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Éditer l'exécuteur",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Editar Executor",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Редактировать контролёра доступа",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Upraviť vynútiteľa",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Редагувати Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "CAD",
"CHF": "CHF",
"CNY": "CNY",
"CZK": "CZK",
"DKK": "DKK",
"EUR": "EUR",
"GBP": "GBP",
"HKD": "HKD",
"HUF": "HUF",
"INR": "INR",
"JPY": "JPY",
"KRW": "KRW",
"MXN": "MXN",
"MYR": "MYR",
"NOK": "NOK",
"PLN": "PLN",
"RUB": "RUB",
"SEK": "SEK",
"SGD": "SGD",
"USD": "USD"
"THB": "THB",
"TRY": "TRY",
"TWD": "TWD",
"USD": "USD",
"ZAR": "ZAR"
},
"enforcer": {
"Edit Enforcer": "Edit Enforcer",

View File

@ -177,12 +177,27 @@
"CAD": "加拿大元",
"CHF": "瑞士法郎",
"CNY": "人民币",
"CZK": "捷克克朗",
"DKK": "丹麦克朗",
"EUR": "欧元",
"GBP": "英镑",
"HKD": "港币",
"HUF": "匈牙利福林",
"INR": "印度卢比",
"JPY": "日元",
"KRW": "韩元",
"MXN": "墨西哥比索",
"MYR": "马来西亚林吉特",
"NOK": "挪威克朗",
"PLN": "波兰兹罗提",
"RUB": "俄罗斯卢布",
"SEK": "瑞典克朗",
"SGD": "新加坡元",
"USD": "美元"
"THB": "泰铢",
"TRY": "土耳其里拉",
"TWD": "新台币",
"USD": "美元",
"ZAR": "南非兰特"
},
"enforcer": {
"Edit Enforcer": "编辑Casbin执行器",