Compare commits

..

16 Commits

Author SHA1 Message Date
DacongDA
7e2f265420 feat: improve organization select UI (#2798) 2024-03-12 19:39:53 +08:00
Yang Luo
53ef179e9b Set Webhook.Url length to 200 2024-03-11 18:18:01 +08:00
Yang Luo
376ef0ed14 feat: support custom Email content in /send-email API 2024-03-11 11:48:00 +08:00
Yang Luo
ca183be336 Improve ManagedAccountTable UI 2024-03-11 00:13:34 +08:00
Yang Luo
e5da57a005 feat: fix cert's ES options 2024-03-10 19:30:05 +08:00
Yang Luo
e4e225db32 Use "ES512" value 2024-03-10 19:25:41 +08:00
Yang Luo
a1add992ee Support legacy "RSA" value 2024-03-10 19:23:54 +08:00
Yang Luo
2aac265ed4 Improve populateContent() 2024-03-10 18:58:53 +08:00
xiao-kong-long
2dc755f529 fix: add more cert algorithms like ES256 and PS256 (#2793) 2024-03-10 18:39:41 +08:00
Yang Luo
0dd474d5fc feat: fix public profile page shows blank page bug 2024-03-10 14:12:24 +08:00
Yang Luo
6998451e97 fix: support roles and permissions in /userinfo API 2024-03-10 12:34:56 +08:00
Yang Luo
9175e5b664 Fix bug in GetMaskedEmail() 2024-03-10 11:49:55 +08:00
DacongDA
dbc6b0dc45 feat: fix issue that forget password page fails to redirect back to signin page (#2792) 2024-03-10 09:55:44 +08:00
Yang Luo
31b7000f6a fix: enable the only language for login page 2024-03-09 11:28:23 +08:00
DacongDA
d25eaa65cd feat: support custom page footer (#2790) 2024-03-08 23:11:03 +08:00
Yang Luo
f5bcd00652 Add language to records page 2024-03-08 23:03:30 +08:00
45 changed files with 519 additions and 99 deletions

View File

@@ -459,7 +459,12 @@ func (c *ApiController) GetUserinfo() {
scope, aud := c.GetSessionOidc()
host := c.Ctx.Request.Host
userInfo := object.GetUserInfo(user, scope, aud, host)
userInfo, err := object.GetUserInfo(user, scope, aud, host)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = userInfo
c.ServeJSON()

View File

@@ -60,7 +60,6 @@ func (c *ApiController) SendEmail() {
}
var emailForm EmailForm
err := json.Unmarshal(c.Ctx.Input.RequestBody, &emailForm)
if err != nil {
c.ResponseError(err.Error())
@@ -87,7 +86,7 @@ func (c *ApiController) SendEmail() {
// when receiver is the reserved keyword: "TestSmtpServer", it means to test the SMTP server instead of sending a real Email
if len(emailForm.Receivers) == 1 && emailForm.Receivers[0] == "TestSmtpServer" {
err := object.DailSmtpServer(provider)
err = object.DailSmtpServer(provider)
if err != nil {
c.ResponseError(err.Error())
return
@@ -112,20 +111,23 @@ func (c *ApiController) SendEmail() {
return
}
code := "123456"
content := emailForm.Content
if content == "" {
code := "123456"
// "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 !strings.HasPrefix(userId, "app/") {
var user *object.User
user, err = object.GetUser(userId)
if err != nil {
c.ResponseError(err.Error())
return
}
// "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 !strings.HasPrefix(userId, "app/") {
var user *object.User
user, err = object.GetUser(userId)
if err != nil {
c.ResponseError(err.Error())
return
}
if user != nil {
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
if user != nil {
content = strings.Replace(content, "%{user.friendlyName}", user.GetFriendlyName(), 1)
}
}
}

2
go.mod
View File

@@ -14,7 +14,7 @@ require (
github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.6.0
github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.0.3
github.com/casvisor/casvisor-go-sdk v1.1.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
github.com/denisenkom/go-mssqldb v0.9.0
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect

4
go.sum
View File

@@ -1093,8 +1093,8 @@ github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.0.3 h1:TKJQWKnhtznEBhzLPEdNsp7nJK2GgdD8JsB0lFPMW7U=
github.com/casvisor/casvisor-go-sdk v1.0.3/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
github.com/casvisor/casvisor-go-sdk v1.1.0 h1:XRDrlDuMjXfPsNa1INLHASPHdV/vgwh1gPZ7+v9fNHk=
github.com/casvisor/casvisor-go-sdk v1.1.0/go.mod h1:frnNtH5GA0wxzAQLyZxxfL0RSsSub9GQPi2Ybe86ocE=
github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.2/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=

View File

@@ -105,6 +105,7 @@ type Application struct {
SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
ThemeData *ThemeData `xorm:"json" json:"themeData"`
FooterHtml string `xorm:"mediumtext" json:"footerHtml"`
FormCss string `xorm:"text" json:"formCss"`
FormCssMobile string `xorm:"text" json:"formCssMobile"`
FormOffset int `json:"formOffset"`
@@ -528,11 +529,12 @@ func GetMaskedApplication(application *Application, userId string) *Application
application.OrganizationObj.PasswordSalt = "***"
application.OrganizationObj.InitScore = -1
application.OrganizationObj.EnableSoftDeletion = false
application.OrganizationObj.IsProfilePublic = false
if !isOrgUser {
application.OrganizationObj.MfaItems = nil
application.OrganizationObj.AccountItems = nil
if !application.OrganizationObj.IsProfilePublic {
application.OrganizationObj.AccountItems = nil
}
}
}

View File

@@ -205,16 +205,41 @@ func (p *Cert) GetId() string {
}
func (p *Cert) populateContent() error {
if p.Certificate == "" || p.PrivateKey == "" {
certificate, privateKey, err := generateRsaKeys(p.BitSize, p.ExpireInYears, p.Name, p.Owner)
if err != nil {
return err
}
p.Certificate = certificate
p.PrivateKey = privateKey
if p.Certificate != "" && p.PrivateKey != "" {
return nil
}
if len(p.CryptoAlgorithm) < 3 {
err := fmt.Errorf("populateContent() error, unsupported crypto algorithm: %s", p.CryptoAlgorithm)
return err
}
if p.CryptoAlgorithm == "RSA" {
p.CryptoAlgorithm = "RS256"
}
sigAlgorithm := p.CryptoAlgorithm[:2]
shaSize, err := util.ParseIntWithError(p.CryptoAlgorithm[2:])
if err != nil {
return err
}
var certificate, privateKey string
if sigAlgorithm == "RS" {
certificate, privateKey, err = generateRsaKeys(p.BitSize, shaSize, p.ExpireInYears, p.Name, p.Owner)
} else if sigAlgorithm == "ES" {
certificate, privateKey, err = generateEsKeys(shaSize, p.ExpireInYears, p.Name, p.Owner)
} else if sigAlgorithm == "PS" {
certificate, privateKey, err = generateRsaPssKeys(p.BitSize, shaSize, p.ExpireInYears, p.Name, p.Owner)
} else {
err = fmt.Errorf("populateContent() error, unsupported signature algorithm: %s", sigAlgorithm)
}
if err != nil {
return err
}
p.Certificate = certificate
p.PrivateKey = privateKey
return nil
}

View File

@@ -47,6 +47,12 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
object = string(ctx.Input.RequestBody)
}
language := ctx.Request.Header.Get("Accept-Language")
if len(language) > 2 {
language = language[0:2]
}
languageCode := conf.GetLanguage(language)
record := casvisorsdk.Record{
Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(),
@@ -55,6 +61,7 @@ func NewRecord(ctx *context.Context) *casvisorsdk.Record {
Method: ctx.Request.Method,
RequestUri: requestUri,
Action: action,
Language: languageCode,
Object: object,
IsTriggered: false,
}

View File

@@ -15,16 +15,19 @@
package object
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"time"
)
func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string, error) {
func generateRsaKeys(bitSize int, shaSize int, expireInYears int, commonName string, organization string) (string, string, error) {
// https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa
// https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key
@@ -55,6 +58,132 @@ func generateRsaKeys(bitSize int, expireInYears int, commonName string, organiza
BasicConstraintsValid: true,
}
switch shaSize {
case 256:
tml.SignatureAlgorithm = x509.SHA256WithRSA
case 384:
tml.SignatureAlgorithm = x509.SHA384WithRSA
case 512:
tml.SignatureAlgorithm = x509.SHA512WithRSA
default:
return "", "", fmt.Errorf("generateRsaKeys() error, unsupported SHA size: %d", shaSize)
}
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return "", "", err
}
// Generate a pem block with the certificate
certPem := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: cert,
})
return string(certPem), string(privateKeyPem), nil
}
func generateEsKeys(shaSize int, expireInYears int, commonName string, organization string) (string, string, error) {
var curve elliptic.Curve
switch shaSize {
case 256:
curve = elliptic.P256()
case 384:
curve = elliptic.P384()
case 512:
curve = elliptic.P521() // ES512(P521,SHA512)
default:
return "", "", fmt.Errorf("generateEsKeys() error, unsupported SHA size: %d", shaSize)
}
// Generate ECDSA key pair.
privateKey, err := ecdsa.GenerateKey(curve, rand.Reader)
if err != nil {
return "", "", err
}
// Encode private key to PEM format.
privateKeyBytes, err := x509.MarshalECPrivateKey(privateKey)
if err != nil {
return "", "", err
}
privateKeyPem := pem.EncodeToMemory(&pem.Block{
Type: "EC PRIVATE KEY",
Bytes: privateKeyBytes,
})
// Generate certificate template.
template := x509.Certificate{
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(expireInYears, 0, 0),
SerialNumber: big.NewInt(time.Now().Unix()),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{organization},
},
BasicConstraintsValid: true,
}
// Generate certificate.
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &privateKey.PublicKey, privateKey)
if err != nil {
return "", "", err
}
// Encode certificate to PEM format.
certPem := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
})
return string(certPem), string(privateKeyPem), nil
}
func generateRsaPssKeys(bitSize int, shaSize int, expireInYears int, commonName string, organization string) (string, string, error) {
// Generate RSA key.
key, err := rsa.GenerateKey(rand.Reader, bitSize)
if err != nil {
return "", "", err
}
// Encode private key to PKCS#8 ASN.1 PEM.
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(key)
if err != nil {
return "", "", err
}
privateKeyPem := pem.EncodeToMemory(
&pem.Block{
Type: "RSA PSS PRIVATE KEY",
Bytes: privateKeyBytes,
},
)
tml := x509.Certificate{
// you can add any attr that you need
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(expireInYears, 0, 0),
// you have to generate a different serial number each execution
SerialNumber: big.NewInt(123456),
Subject: pkix.Name{
CommonName: commonName,
Organization: []string{organization},
},
BasicConstraintsValid: true,
}
// Set the signature algorithm based on the hash function
switch shaSize {
case 256:
tml.SignatureAlgorithm = x509.SHA256WithRSAPSS
case 384:
tml.SignatureAlgorithm = x509.SHA384WithRSAPSS
case 512:
tml.SignatureAlgorithm = x509.SHA512WithRSAPSS
default:
return "", "", fmt.Errorf("generateRsaPssKeys() error, unsupported SHA size: %d", shaSize)
}
cert, err := x509.CreateCertificate(rand.Reader, &tml, &tml, &key.PublicKey, key)
if err != nil {
return "", "", err

View File

@@ -23,7 +23,35 @@ import (
func TestGenerateRsaKeys(t *testing.T) {
fileId := "token_jwt_key"
certificate, privateKey, err := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
certificate, privateKey, err := generateRsaKeys(4096, 512, 20, "Casdoor Cert", "Casdoor Organization")
if err != nil {
panic(err)
}
// Write certificate (aka certificate) to file.
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
// Write private key to file.
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))
}
func TestGenerateEsKeys(t *testing.T) {
fileId := "token_jwt_key"
certificate, privateKey, err := generateEsKeys(256, 20, "Casdoor Cert", "Casdoor Organization")
if err != nil {
panic(err)
}
// Write certificate (aka certificate) to file.
util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
// Write private key to file.
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))
}
func TestGenerateRsaPssKeys(t *testing.T) {
fileId := "token_jwt_key"
certificate, privateKey, err := generateRsaPssKeys(4096, 256, 20, "Casdoor Cert", "Casdoor Organization")
if err != nil {
panic(err)
}

View File

@@ -216,6 +216,8 @@ type Userinfo struct {
Address string `json:"address,omitempty"`
Phone string `json:"phone,omitempty"`
Groups []string `json:"groups,omitempty"`
Roles []string `json:"roles,omitempty"`
Permissions []string `json:"permissions,omitempty"`
}
type ManagedAccount struct {
@@ -914,7 +916,7 @@ func DeleteUser(user *User) (bool, error) {
return affected != 0, nil
}
func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) {
_, originBackend := getOriginFromHost(host)
resp := Userinfo{
@@ -922,24 +924,44 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
Iss: originBackend,
Aud: aud,
}
if strings.Contains(scope, "profile") {
resp.Name = user.Name
resp.DisplayName = user.DisplayName
resp.Avatar = user.Avatar
resp.Groups = user.Groups
err := ExtendUserWithRolesAndPermissions(user)
if err != nil {
return nil, err
}
resp.Roles = []string{}
for _, role := range user.Roles {
resp.Roles = append(resp.Roles, role.Name)
}
resp.Permissions = []string{}
for _, permission := range user.Permissions {
resp.Permissions = append(resp.Permissions, permission.Name)
}
}
if strings.Contains(scope, "email") {
resp.Email = user.Email
// resp.EmailVerified = user.EmailVerified
resp.EmailVerified = true
}
if strings.Contains(scope, "address") {
resp.Address = user.Location
}
if strings.Contains(scope, "phone") {
resp.Phone = user.Phone
}
return &resp
return &resp, nil
}
func LinkUserAccount(user *User, field string, value string) (bool, error) {

View File

@@ -33,7 +33,7 @@ type Webhook struct {
Organization string `xorm:"varchar(100) index" json:"organization"`
Url string `xorm:"varchar(100)" json:"url"`
Url string `xorm:"varchar(200)" json:"url"`
Method string `xorm:"varchar(100)" json:"method"`
ContentType string `xorm:"varchar(100)" json:"contentType"`
Headers []*Header `xorm:"mediumtext" json:"headers"`

View File

@@ -264,6 +264,10 @@ func GetMaskedEmail(email string) string {
return ""
}
if !strings.Contains(email, "@") {
return maskString(email)
}
tokens := strings.Split(email, "@")
username := maskString(tokens[0])
domain := tokens[1]

View File

@@ -34,6 +34,7 @@ const ManagementPage = lazy(() => import("./ManagementPage"));
const {Footer, Content} = Layout;
import {setTwoToneColor} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend";
setTwoToneColor("rgb(87,52,211)");
@@ -56,6 +57,7 @@ class App extends Component {
logo: this.getLogo(storageThemeAlgorithm),
requiredEnableMfa: false,
isAiAssistantOpen: false,
application: undefined,
};
Setting.initServerUrl();
Auth.initAuthWithConfig({
@@ -67,6 +69,7 @@ class App extends Component {
UNSAFE_componentWillMount() {
this.updateMenuKey();
this.getAccount();
this.getApplication();
}
componentDidUpdate(prevProps, prevState, snapshot) {
@@ -190,6 +193,24 @@ class App extends Component {
}
};
getApplication() {
const applicationName = localStorage.getItem("applicationName");
if (!applicationName) {
return;
}
ApplicationBackend.getApplication("admin", applicationName)
.then((res) => {
if (res.status === "error") {
Setting.showMessage("error", res.msg);
return;
}
this.setState({
application: res.data,
});
});
}
getAccount() {
const params = new URLSearchParams(this.props.location.search);
@@ -245,11 +266,17 @@ class App extends Component {
}
}>
{
Conf.CustomFooter !== null ? Conf.CustomFooter : (
this.state.application?.footerHtml && this.state.application.footerHtml !== "" ?
<React.Fragment>
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={this.state.logo} /></a>
<div dangerouslySetInnerHTML={{__html: this.state.application.footerHtml}} />
</React.Fragment>
)
: (
Conf.CustomFooter !== null ? Conf.CustomFooter : (
<React.Fragment>
Powered by <a target="_blank" href="https://casdoor.org" rel="noreferrer"><img style={{paddingBottom: "3px"}} height={"20px"} alt={"Casdoor"} src={this.state.logo} /></a>
</React.Fragment>
)
)
}
</Footer>
</React.Fragment>
@@ -330,6 +357,11 @@ class App extends Component {
<EntryPage
account={this.state.account}
theme={this.state.themeData}
updateApplication={(application) => {
this.setState({
application: application,
});
}}
onLoginSuccess={(redirectUrl) => {
if (redirectUrl) {
localStorage.setItem("mfaRedirectUrl", redirectUrl);

View File

@@ -887,6 +887,38 @@ class ApplicationEditPage extends React.Component {
</Popover>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Footer HTML"), i18next.t("application:Footer HTML - Tooltip"))} :
</Col>
<Col span={22} >
<Popover placement="right" content={
<div style={{width: "900px", height: "300px"}} >
<CodeMirror
value={this.state.application.footerHtml}
options={{mode: "htmlmixed", theme: "material-darker"}}
onBeforeChange={(editor, data, value) => {
this.updateApplicationField("footerHtml", value);
}}
/>
</div>
} title={i18next.t("application:Footer HTML - Edit")} trigger="click">
<Input value={this.state.application.footerHtml} style={{marginBottom: "10px"}} onChange={e => {
this.updateApplicationField("footerHtml", e.target.value);
}} />
</Popover>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
</Col>
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateApplicationField("footerHtml", Setting.getDefaultFooterContent())} >
{i18next.t("provider:Reset to Default HTML")}
</Button>
<Button style={{marginLeft: "10px", marginBottom: "5px"}} onClick={() => this.updateApplicationField("footerHtml", Setting.getEmptyFooterContent())} >
{i18next.t("application:Reset to Empty")}
</Button>
</Row>
{
<React.Fragment>
<Row style={{marginTop: "20px"}} >

View File

@@ -171,48 +171,54 @@ class CertEditPage extends React.Component {
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.cryptoAlgorithm} onChange={(value => {
this.updateCertField("cryptoAlgorithm", value);
if (value === "RS256") {
this.updateCertField("bitSize", 2048);
} else if (value === "HS256" || value === "ES256") {
this.updateCertField("bitSize", 256);
} else if (value === "ES384") {
this.updateCertField("bitSize", 384);
} else if (value === "ES521") {
this.updateCertField("bitSize", 521);
} else {
if (value.startsWith("ES")) {
this.updateCertField("bitSize", 0);
} else {
if (this.state.cert.bitSize !== 1024 && this.state.cert.bitSize !== 2048 && this.state.cert.bitSize !== 4096) {
this.updateCertField("bitSize", 2048);
}
}
this.updateCertField("certificate", "");
this.updateCertField("privateKey", "");
})}>
{
[
{id: "RS256", name: "RS256 (RSA + SHA256)"},
{id: "HS256", name: "HS256 (HMAC + SHA256)"},
{id: "RS384", name: "RS384 (RSA + SHA384)"},
{id: "RS512", name: "RS512 (RSA + SHA512)"},
{id: "ES256", name: "ES256 (ECDSA using P-256 + SHA256)"},
{id: "ES384", name: "ES384 (ECDSA using P-384 + SHA256)"},
{id: "ES521", name: "ES521 (ECDSA using P-521 + SHA256)"},
{id: "ES384", name: "ES384 (ECDSA using P-384 + SHA384)"},
{id: "ES512", name: "ES512 (ECDSA using P-521 + SHA512)"},
{id: "PS256", name: "PS256 (RSASSA-PSS using SHA256 and MGF1 with SHA256)"},
{id: "PS384", name: "PS384 (RSASSA-PSS using SHA384 and MGF1 with SHA384)"},
{id: "PS512", name: "PS512 (RSASSA-PSS using SHA512 and MGF1 with SHA512)"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("cert:Bit size"), i18next.t("cert:Bit size - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.bitSize} onChange={(value => {
this.updateCertField("bitSize", value);
this.updateCertField("certificate", "");
this.updateCertField("privateKey", "");
})}>
{
Setting.getCryptoAlgorithmOptions(this.state.cert.cryptoAlgorithm).map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
</Col>
</Row>
{
this.state.cert.cryptoAlgorithm.startsWith("ES") ? null : (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("cert:Bit size"), i18next.t("cert:Bit size - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.bitSize} onChange={(value => {
this.updateCertField("bitSize", value);
this.updateCertField("certificate", "");
this.updateCertField("privateKey", "");
})}>
{
Setting.getCryptoAlgorithmOptions(this.state.cert.cryptoAlgorithm).map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
}
</Select>
</Col>
</Row>
)
}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("cert:Expire in years"), i18next.t("cert:Expire in years - Tooltip"))} :

View File

@@ -69,6 +69,9 @@ class EntryPage extends React.Component {
});
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
this.props.updataThemeData(themeData);
this.props.updateApplication(application);
localStorage.setItem("applicationName", application.name);
};
const onUpdatePricing = (pricing) => {

View File

@@ -139,10 +139,18 @@ class RecordListPage extends BaseListPage {
title: i18next.t("general:Request URI"),
dataIndex: "requestUri",
key: "requestUri",
// width: '300px',
// width: "300px",
sorter: true,
...this.getColumnSearchProps("requestUri"),
},
{
title: i18next.t("user:Language"),
dataIndex: "language",
key: "language",
width: "90px",
sorter: true,
...this.getColumnSearchProps("language"),
},
{
title: i18next.t("general:Action"),
dataIndex: "action",

View File

@@ -1101,7 +1101,9 @@ export function getProviderTypeOptions(category) {
}
export function getCryptoAlgorithmOptions(cryptoAlgorithm) {
if (cryptoAlgorithm === "RS256") {
if (cryptoAlgorithm.startsWith("ES")) {
return [];
} else {
return (
[
{id: 1024, name: "1024"},
@@ -1109,26 +1111,6 @@ export function getCryptoAlgorithmOptions(cryptoAlgorithm) {
{id: 4096, name: "4096"},
]
);
} else if (cryptoAlgorithm === "HS256" || cryptoAlgorithm === "ES256") {
return (
[
{id: 256, name: "256"},
]
);
} else if (cryptoAlgorithm === "ES384") {
return (
[
{id: 384, name: "384"},
]
);
} else if (cryptoAlgorithm === "ES521") {
return (
[
{id: 521, name: "521"},
]
);
} else {
return [];
}
}
@@ -1467,6 +1449,19 @@ export function getUserCommonFields() {
"PreferredMfaType", "TotpSecret", "SignupApplication"];
}
export function getDefaultFooterContent() {
return "Powered by <a target=\"_blank\" href=\"https://casdoor.org\" rel=\"noreferrer\"><img style=\"padding-bottom: 3px\" height=\"20\" alt=\"Casdoor\" src=\"https://cdn.casbin.org/img/casdoor-logo_1185x256.png\"/></a>";
}
export function getEmptyFooterContent() {
return `<style>
#footer {
display: none;
}
<style>
`;
}
export function getDefaultHtmlEmailContent() {
return `<!DOCTYPE html>
<html lang="en">

View File

@@ -201,6 +201,10 @@ class UserEditPage extends React.Component {
}
updateUserField(key, value) {
if (this.props.account === null) {
return;
}
value = this.parseUserField(key, value);
const user = this.state.user;
@@ -989,7 +993,11 @@ class UserEditPage extends React.Component {
<div style={{verticalAlign: "middle", marginBottom: 10}}>{`(${i18next.t("general:empty")})`}</div>
</Col>
}
<CropperDivModal disabled={disabled} tag={tag} setTitle={set} buttonText={`${title}...`} title={title} user={this.state.user} organization={this.getUserOrganization()} />
{
(this.props.account === null) ? null : (
<CropperDivModal disabled={disabled} tag={tag} setTitle={set} buttonText={`${title}...`} title={title} user={this.state.user} organization={this.getUserOrganization()} />
)
}
</Col>
);
}

View File

@@ -156,7 +156,7 @@ class ForgetPage extends React.Component {
if (res.status === "ok") {
const linkInStorage = sessionStorage.getItem("signinUrl");
if (linkInStorage !== null && linkInStorage !== "") {
Setting.goToLinkSoft(linkInStorage);
Setting.goToLinkSoft(this, linkInStorage);
} else {
Setting.redirectToLoginPage(this.getApplicationObj(), this.props.history);
}

View File

@@ -521,6 +521,15 @@ class LoginPage extends React.Component {
</div>
);
} else if (signinItem.name === "Languages") {
const languages = application.organizationObj.languages;
if (languages.length <= 1) {
const language = (languages.length === 1) ? languages[0] : "en";
if (Setting.getLanguage() !== language) {
Setting.setLanguage(language);
}
return null;
}
return (
<div className="login-languages">
<div dangerouslySetInnerHTML={{__html: signinItem.label}} />
@@ -1108,7 +1117,7 @@ class LoginPage extends React.Component {
};
return (
<div style={{height: 300, width: 300}}>
<div style={{height: 300}}>
{renderChoiceBox()}
</div>
);
@@ -1179,11 +1188,9 @@ class LoginPage extends React.Component {
</div>
<div className="login-form">
<div>
<div>
{
this.renderLoginPanel(application)
}
</div>
{
this.renderLoginPanel(application)
}
</div>
</div>
</div>

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Datei erfolgreich hochgeladen",
"First, last": "First, last",
"Follow organization theme": "Folge dem Theme der Organisation",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Formposition",
"Form position - Tooltip": "Position der Anmelde-, Registrierungs- und Passwort-vergessen-Formulare",
"Grant types": "Grant-Typen",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Liste erlaubter Umleitungs-URLs mit Unterstützung von regulärer Ausdrucksprüfung; URLs, die nicht in der Liste enthalten sind, können nicht umgeleitet werden",
"Refresh token expire": "Gültigkeitsdauer des Refresh-Tokens",
"Refresh token expire - Tooltip": "Angabe der Gültigkeitsdauer des Refresh Tokens",
"Reset to Empty": "Reset to Empty",
"Right": "Rechts",
"Rule": "Regel",
"SAML metadata": "SAML-Metadaten",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Archivo subido exitosamente",
"First, last": "First, last",
"Follow organization theme": "Seguir el tema de la organización",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Posición de la Forma",
"Form position - Tooltip": "Ubicación de los formularios de registro, inicio de sesión y olvido de contraseña",
"Grant types": "Tipos de subvenciones",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Lista de URL de redireccionamiento permitidos, con soporte para coincidencias de expresiones regulares; las URL que no estén en la lista no se redirigirán",
"Refresh token expire": "Token de actualización expirado",
"Refresh token expire - Tooltip": "Tiempo de caducidad del token de actualización",
"Reset to Empty": "Reset to Empty",
"Right": "Correcto",
"Rule": "Regla",
"SAML metadata": "Metadatos de SAML",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Fichier téléchargé avec succès",
"First, last": "Prénom, nom",
"Follow organization theme": "Suivre le thème de l'organisation",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Position du formulaire",
"Form position - Tooltip": "Emplacement des formulaires d'inscription, de connexion et de récupération de mot de passe",
"Grant types": "Types d'autorisation",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Liste des URL de redirection autorisées, les expressions régulières sont supportées ; les URL n'étant pas dans la liste ne seront pas redirigées",
"Refresh token expire": "Expiration du jeton de rafraîchissement",
"Refresh token expire - Tooltip": "Durée avant expiration du jeton de rafraîchissement",
"Reset to Empty": "Reset to Empty",
"Right": "Droit",
"Rule": "Règle",
"SAML metadata": "Métadonnées SAML",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Berkas telah diunggah dengan sukses",
"First, last": "First, last",
"Follow organization theme": "Ikuti tema organisasi",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Posisi formulir",
"Form position - Tooltip": "Tempat pendaftaran, masuk, dan lupa kata sandi",
"Grant types": "Jenis-jenis hibah",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Daftar URL redirect yang diizinkan, mendukung pencocokan ekspresi reguler; URL yang tidak ada dalam daftar akan gagal dialihkan",
"Refresh token expire": "Token segar kedaluwarsa",
"Refresh token expire - Tooltip": "Waktu kedaluwarsa token penyegaran",
"Reset to Empty": "Reset to Empty",
"Right": "Benar",
"Rule": "Aturan",
"SAML metadata": "Metadata SAML",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "ファイルが正常にアップロードされました",
"First, last": "First, last",
"Follow organization theme": "組織のテーマに従ってください",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "フォームのポジション",
"Form position - Tooltip": "登録、ログイン、パスワード忘れフォームの位置",
"Grant types": "グラント種類",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "許可されたリダイレクトURLリストは、正規表現マッチングをサポートしています。リストに含まれていないURLはリダイレクトできません",
"Refresh token expire": "リフレッシュトークンの有効期限が切れました",
"Refresh token expire - Tooltip": "リフレッシュトークンの有効期限時間",
"Reset to Empty": "Reset to Empty",
"Right": "右",
"Rule": "ルール",
"SAML metadata": "SAMLメタデータ",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "파일이 성공적으로 업로드되었습니다",
"First, last": "First, last",
"Follow organization theme": "조직의 주제를 따르세요",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "양식 위치",
"Form position - Tooltip": "가입, 로그인 및 비밀번호 재설정 양식의 위치",
"Grant types": "Grant types: 부여 유형",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "허용된 리디렉션 URL 목록은 정규 표현식 일치를 지원합니다. 목록에 없는 URL은 리디렉션에 실패합니다",
"Refresh token expire": "리프레시 토큰 만료",
"Refresh token expire - Tooltip": "리프레시 토큰 만료 시간",
"Reset to Empty": "Reset to Empty",
"Right": "옳은",
"Rule": "규칙",
"SAML metadata": "SAML 메타데이터",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Arquivo enviado com sucesso",
"First, last": "Primeiro, último",
"Follow organization theme": "Seguir tema da organização",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Posição do formulário",
"Form position - Tooltip": "Localização dos formulários de registro, login e recuperação de senha",
"Grant types": "Tipos de concessão",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Lista de URLs de redirecionamento permitidos, com suporte à correspondência por expressões regulares; URLs que não estão na lista falharão ao redirecionar",
"Refresh token expire": "Expiração do token de atualização",
"Refresh token expire - Tooltip": "Tempo de expiração do token de atualização",
"Reset to Empty": "Reset to Empty",
"Right": "Direita",
"Rule": "Regra",
"SAML metadata": "Metadados do SAML",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Файл успешно загружен",
"First, last": "Имя, Фамилия",
"Follow organization theme": "Cледуйте теме организации",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Позиция формы",
"Form position - Tooltip": "Местоположение форм регистрации, входа и восстановления пароля",
"Grant types": "Типы грантов",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Разрешенный список URL-адресов для перенаправления с поддержкой сопоставления регулярных выражений; URL-адреса, которые не находятся в списке, не будут перенаправляться",
"Refresh token expire": "Срок действия токена обновления истек",
"Refresh token expire - Tooltip": "Время истечения токена обновления",
"Reset to Empty": "Reset to Empty",
"Right": "Правильно",
"Rule": "Правило",
"SAML metadata": "Метаданные SAML",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "Adı, Soyadı",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Kabul edilen yönlendirme URL listesi, düzenli ifadeleri (regexp) kullanabilirsiniz. Eğer url bu lşistede yoksa hata sayfasına yönlendirilirsiniz",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Sağ",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "File uploaded successfully",
"First, last": "First, last",
"Follow organization theme": "Follow organization theme",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Form position",
"Form position - Tooltip": "Location of the signup, signin and forget password forms",
"Grant types": "Grant types",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Allowed redirect URL list, supporting regular expression matching; URLs not in the list will fail to redirect",
"Refresh token expire": "Refresh token expire",
"Refresh token expire - Tooltip": "Refresh token expiration time",
"Reset to Empty": "Reset to Empty",
"Right": "Right",
"Rule": "Rule",
"SAML metadata": "SAML metadata",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "Tệp được tải lên thành công",
"First, last": "Tên, Họ",
"Follow organization theme": "Theo giao diện tổ chức",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "Custom the footer of your application",
"Form position": "Vị trí của hình thức",
"Form position - Tooltip": "Vị trí của các biểu mẫu đăng ký, đăng nhập và quên mật khẩu",
"Grant types": "Loại hỗ trợ",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "Danh sách URL chuyển hướng được phép, hỗ trợ khớp biểu thức chính quy; các URL không có trong danh sách sẽ không được chuyển hướng",
"Refresh token expire": "Làm mới mã thông báo hết hạn",
"Refresh token expire - Tooltip": "Thời gian hết hạn của mã thông báo làm mới",
"Reset to Empty": "Reset to Empty",
"Right": "Đúng",
"Rule": "Quy tắc",
"SAML metadata": "SAML metadata: Siêu dữ liệu SAML",

View File

@@ -58,6 +58,9 @@
"File uploaded successfully": "文件上传成功",
"First, last": "名字, 姓氏",
"Follow organization theme": "使用组织主题",
"Footer HTML": "Footer HTML",
"Footer HTML - Edit": "Footer HTML - Edit",
"Footer HTML - Tooltip": "自定义应用的footer",
"Form position": "表单位置",
"Form position - Tooltip": "注册、登录、忘记密码等表单的位置",
"Grant types": "OAuth授权类型",
@@ -88,6 +91,7 @@
"Redirect URLs - Tooltip": "允许的重定向URL列表支持正则匹配不在列表中的URL将会跳转失败",
"Refresh token expire": "Refresh Token过期",
"Refresh token expire - Tooltip": "Refresh Token过期时间",
"Reset to Empty": "重置为空",
"Right": "居右",
"Rule": "规则",
"SAML metadata": "SAML元数据",

View File

@@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {DeleteOutlined, DownOutlined, LinkOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Select, Table, Tooltip} from "antd";
import * as Setting from "../Setting";
import i18next from "i18next";
@@ -98,14 +98,27 @@ class ManagedAccountTable extends React.Component {
);
},
},
{
title: i18next.t("general:Signin URL"),
dataIndex: "signinUrl",
key: "signinUrl",
// width: "420px",
render: (text, record, index) => {
return (
<Input prefix={<LinkOutlined />} value={text} onChange={e => {
this.updateField(table, index, "signinUrl", e.target.value);
}} />
);
},
},
{
title: i18next.t("signup:Username"),
dataIndex: "username",
key: "username",
width: "420px",
width: "200px",
render: (text, record, index) => {
return (
<Input defaultValue={text} onChange={e => {
<Input value={text} onChange={e => {
this.updateField(table, index, "username", e.target.value);
}} />
);
@@ -115,10 +128,10 @@ class ManagedAccountTable extends React.Component {
title: i18next.t("general:Password"),
dataIndex: "password",
key: "password",
width: "420px",
width: "200px",
render: (text, record, index) => {
return (
<Input defaultValue={text} onChange={e => {
<Input.Password value={text} onChange={e => {
this.updateField(table, index, "password", e.target.value);
}} />
);