Compare commits

...

11 Commits

Author SHA1 Message Date
3875896c1e feat: support custom header logo (#2801)
* feat: support custom header logo

* feat: add i18n

* feat: preview default logo when field is empty

* feat: improve logo setting and display logic

* feat: change logoLight to logo
2024-03-13 23:33:43 +08:00
7e2f265420 feat: improve organization select UI (#2798) 2024-03-12 19:39:53 +08:00
53ef179e9b Set Webhook.Url length to 200 2024-03-11 18:18:01 +08:00
376ef0ed14 feat: support custom Email content in /send-email API 2024-03-11 11:48:00 +08:00
ca183be336 Improve ManagedAccountTable UI 2024-03-11 00:13:34 +08:00
e5da57a005 feat: fix cert's ES options 2024-03-10 19:30:05 +08:00
e4e225db32 Use "ES512" value 2024-03-10 19:25:41 +08:00
a1add992ee Support legacy "RSA" value 2024-03-10 19:23:54 +08:00
2aac265ed4 Improve populateContent() 2024-03-10 18:58:53 +08:00
2dc755f529 fix: add more cert algorithms like ES256 and PS256 (#2793) 2024-03-10 18:39:41 +08:00
0dd474d5fc feat: fix public profile page shows blank page bug 2024-03-10 14:12:24 +08:00
39 changed files with 478 additions and 103 deletions

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

View File

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

@ -54,6 +54,8 @@ type Organization struct {
DisplayName string `xorm:"varchar(100)" json:"displayName"`
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
Logo string `xorm:"varchar(200)" json:"logo"`
LogoDark string `xorm:"varchar(200)" json:"logoDark"`
Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`

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

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

@ -153,11 +153,7 @@ class App extends Component {
}
getLogo(themes) {
if (themes.includes("dark")) {
return `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256_dark.png`;
} else {
return `${Setting.StaticBaseUrl}/img/casdoor-logo_1185x256.png`;
}
return Setting.getLogo(themes);
}
setLanguage(account) {

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

@ -220,9 +220,29 @@ function ManagementPage(props) {
return [];
}
const textColor = props.themeAlgorithm.includes("dark") ? "white" : "black";
let textColor = "black";
const twoToneColor = props.themeData.colorPrimary;
let logo = props.account.organization.logo ? props.account.organization.logo : Setting.getLogo(props.themeAlgorithm);
if (props.themeAlgorithm.includes("dark")) {
if (props.account.organization.logoDark) {
logo = props.account.organization.logoDark;
}
textColor = "white";
}
!Setting.isMobile() ? res.push({
label:
<Link to="/">
<img className="logo" src={logo ?? props.logo} alt="logo" />
</Link>,
disabled: true,
style: {
padding: 0,
height: "auto",
},
}) : null;
res.push(Setting.getItem(<Link style={{color: textColor}} to="/">{i18next.t("general:Home")}</Link>, "/home", <HomeTwoTone twoToneColor={twoToneColor} />, [
Setting.getItem(<Link to="/">{i18next.t("general:Dashboard")}</Link>, "/"),
Setting.getItem(<Link to="/shortcuts">{i18next.t("general:Shortcuts")}</Link>, "/shortcuts"),
@ -400,11 +420,6 @@ function ManagementPage(props) {
<React.Fragment>
<EnableMfaNotification account={props.account} />
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}} >
{Setting.isMobile() ? null : (
<Link to={"/"}>
<div className="logo" style={{background: `url(${props.logo})`}} />
</Link>
)}
{props.requiredEnableMfa || (Setting.isMobile() ?
<React.Fragment>
<Drawer title={i18next.t("general:Close")} placement="left" visible={menuVisible} onClose={onClose}>
@ -426,7 +441,7 @@ function ManagementPage(props) {
items={getMenuItems()}
mode={"horizontal"}
selectedKeys={[props.selectedMenuKey]}
style={{position: "absolute", left: "145px", right: menuStyleRight, backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}}
style={{position: "absolute", left: 0, right: menuStyleRight, backgroundColor: props.themeAlgorithm.includes("dark") ? "black" : "white"}}
/>
)}
{

View File

@ -56,6 +56,7 @@ class OrganizationEditPage extends React.Component {
this.props.history.push("/404");
return;
}
organization["enableDarkLogo"] = !!organization["logoDark"];
this.setState({
organization: organization,
@ -141,6 +142,78 @@ class OrganizationEditPage extends React.Component {
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Enable dark logo"), i18next.t("general:Enable dark logo - Tooltip"))} :
</Col>
<Col span={22} >
<Switch checked={this.state.organization.enableDarkLogo} onChange={e => {
this.updateOrganizationField("enableDarkLogo", e);
if (!e) {
this.updateOrganizationField("logoDark", "");
}
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} :
</Col>
<Col span={22} >
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
{Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
</Col>
<Col span={23} >
<Input prefix={<LinkOutlined />} value={this.state.organization.logo} onChange={e => {
this.updateOrganizationField("logo", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
{i18next.t("general:Preview")}:
</Col>
<Col span={23}>
<a target="_blank" rel="noreferrer" href={this.state.organization.logo}>
<img src={this.state.organization.logo ? this.state.organization.logo : Setting.getLogo([""])} alt={this.state.organization.logo} height={90} style={{background: "white", marginBottom: "20px"}} />
</a>
</Col>
</Row>
</Col>
</Row>
{
!this.state.organization.enableDarkLogo ? null : (<Row style={{marginTop: "20px"}}>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Logo dark"), i18next.t("general:Logo dark - Tooltip"))} :
</Col>
<Col span={22}>
<Row style={{marginTop: "20px"}}>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
{Setting.getLabel(i18next.t("general:URL"), i18next.t("general:URL - Tooltip"))} :
</Col>
<Col span={23}>
<Input prefix={<LinkOutlined />} value={this.state.organization.logoDark} onChange={e => {
this.updateOrganizationField("logoDark", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}}>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 1}>
{i18next.t("general:Preview")}:
</Col>
<Col span={23}>
<a target="_blank" rel="noreferrer" href={this.state.organization.logoDark}>
<img
src={this.state.organization.logoDark ? this.state.organization.logoDark : Setting.getLogo(["dark"])}
alt={this.state.organization.logoDark} height={90}
style={{background: "#141414", marginBottom: "20px"}} />
</a>
</Col>
</Row>
</Col>
</Row>)
}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Favicon"), i18next.t("general:Favicon - Tooltip"))} :

View File

@ -89,6 +89,14 @@ export function getAlgorithmNames(themeData) {
return algorithms;
}
export function getLogo(themes) {
if (themes.includes("dark")) {
return `${StaticBaseUrl}/img/casdoor-logo_1185x256_dark.png`;
} else {
return `${StaticBaseUrl}/img/casdoor-logo_1185x256.png`;
}
}
export const OtherProviderInfo = {
SMS: {
"Aliyun SMS": {
@ -1101,7 +1109,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 +1119,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 [];
}
}

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

@ -1117,7 +1117,7 @@ class LoginPage extends React.Component {
};
return (
<div style={{height: 300, width: 300}}>
<div style={{height: 300}}>
{renderChoiceBox()}
</div>
);
@ -1188,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

@ -27,9 +27,6 @@ code {
}
.logo {
background: url("https://cdn.casbin.org/img/casdoor-logo_1185x256.png");
background-size: 130px, 27px !important;
width: 130px;
height: 27px;
margin: 17px 0 16px 15px;
float: left;

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Gültige E-Mail-Adresse",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Symbole, die die Anwendung der Außenwelt präsentiert",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Hauptpasswort",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Dirección de correo electrónico válida",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logotipo",
"Logo - Tooltip": "Iconos que la aplicación presenta al mundo exterior",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Contraseña maestra",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Adresse e-mail valide",
"Email only": "Email only",
"Enable": "Activer",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Activé",
"Enabled successfully": "Activé avec succès",
"Enforcers": "Exécuteurs",
@ -268,6 +270,8 @@
"Logging & Auditing": "Journalisation et Audit",
"Logo": "Logo",
"Logo - Tooltip": "Icônes que l'application présente au monde extérieur",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "Type d'authentification multifacteur",
"MFA items - Tooltip": "Types d'authentification multifacteur - Infobulle",
"Master password": "Mot de passe passe-partout",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Alamat email yang valid",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Ikon-ikon yang disajikan aplikasi ke dunia luar",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Kata sandi utama",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "有効な電子メールアドレス",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "ロゴ",
"Logo - Tooltip": "アプリケーションが外部世界に示すアイコン",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "マスターパスワード",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "유효한 이메일 주소",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "로고",
"Logo - Tooltip": "애플리케이션이 외부 세계에 제시하는 아이콘들",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "마스터 비밀번호",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Endereço de e-mail válido",
"Email only": "Email only",
"Enable": "Habilitar",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Habilitado",
"Enabled successfully": "Habilitado com sucesso",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Ícones que o aplicativo apresenta para o mundo externo",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Senha mestra",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Действительный адрес электронной почты",
"Email only": "Email only",
"Enable": "Включить",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Включено",
"Enabled successfully": "Успешно включено",
"Enforcers": "Контролёры доступа",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Логотип",
"Logo - Tooltip": "Иконки, которые приложение представляет во внешний мир",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Главный пароль",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Geçerli e-posta adresi",
"Email only": "Sadece eposta",
"Enable": "Etkinleştir",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Etkin",
"Enabled successfully": "Başarıyla etkinleştirildi",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Valid email address",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Logo",
"Logo - Tooltip": "Icons that the application presents to the outside world",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Master password",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "Địa chỉ email hợp lệ",
"Email only": "Email only",
"Enable": "Enable",
"Enable dark logo": "Enable dark logo",
"Enable dark logo - Tooltip": "Enable dark logo",
"Enabled": "Enabled",
"Enabled successfully": "Enabled successfully",
"Enforcers": "Enforcers",
@ -268,6 +270,8 @@
"Logging & Auditing": "Logging & Auditing",
"Logo": "Biểu tượng",
"Logo - Tooltip": "Biểu tượng mà ứng dụng hiển thị ra ngoài thế giới",
"Logo dark": "Logo dark",
"Logo dark - Tooltip": "The logo used in dark theme",
"MFA items": "MFA items",
"MFA items - Tooltip": "MFA items - Tooltip",
"Master password": "Mật khẩu chính",

View File

@ -229,6 +229,8 @@
"Email - Tooltip": "合法的电子邮件地址",
"Email only": "仅支持邮件",
"Enable": "启用",
"Enable dark logo": "开启暗黑Logo",
"Enable dark logo - Tooltip": "开启暗黑Logo",
"Enabled": "已开启",
"Enabled successfully": "启用成功",
"Enforcers": "Casbin执行器",
@ -268,6 +270,8 @@
"Logging & Auditing": "日志 & 审计",
"Logo": "Logo",
"Logo - Tooltip": "应用程序向外展示的图标",
"Logo dark": "暗黑logo",
"Logo dark - Tooltip": "暗黑主题下使用的logo",
"MFA items": "MFA 项",
"MFA items - Tooltip": "MFA 项 - Tooltip",
"Master password": "万能密码",

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