Compare commits

..

12 Commits

Author SHA1 Message Date
Resulte Lee
155660b0d7 feat: get user api return roles and permissions (#929) 2022-07-30 17:31:56 +08:00
imp2002
1c72f5300c feat: fix 'Enable code sign' is not displayed in the login page (#925) 2022-07-28 23:11:33 +08:00
q1anx1
3dd56195d9 fix: fix the problem of link error (#923) 2022-07-28 21:52:10 +08:00
Resulte Lee
8865244262 fix: add oauth login auto close page (#915) 2022-07-26 23:03:55 +08:00
Yixiang Zhao
3400fa1e9c feat: support local login for non-built-in users (#911)
Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>
2022-07-26 19:27:24 +08:00
Resulte Lee
bdc5c92ef0 fix: send code missing parameter & show more detail responseError (#910) 2022-07-25 23:46:38 +08:00
SLingyu
4e3eedf246 feat: fix bug that the default permission prevents admin to login in (#907)
* fix:The certs page is displayed incorrectly

* Translations for each language are added

* Replace the variables certificat with Certificat with certificate and Certificate

* Replace the variables certificat with Certificat with certificate and Certificate

* Variable names are more accurate

* Variable names are more accurate

* Modify the variable name

* fix: Default action prevents admin to login in
2022-07-24 23:36:55 +08:00
SLingyu
8e98fc5a9f feat: rename all publicKey occurrences to certificate (#894)
* fix:The certs page is displayed incorrectly

* Translations for each language are added

* Replace the variables certificat with Certificat with certificate and Certificate

* Replace the variables certificat with Certificat with certificate and Certificate

* Variable names are more accurate

* Variable names are more accurate

* Modify the variable name
2022-07-23 09:40:51 +08:00
leoshine
6f6159be07 feat: add GET method of logout API (#903) 2022-07-22 21:13:49 +08:00
Gucheng Wang
3e4dbc2dcb fix: URL bug in getUploadFileUrl function 2022-07-20 17:49:11 +08:00
Yixiang Zhao
48b5b27982 fix: invalid redirect url after sign up (#896)
* fix: invalid redirect url after sign up

Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com>

* Update App.js

* Update Setting.js

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2022-07-19 23:31:17 +08:00
q1anx1
1839252c30 chore(web): sort import members (#895) 2022-07-18 20:57:38 +08:00
57 changed files with 559 additions and 159 deletions

View File

@@ -78,6 +78,7 @@ p, *, *, POST, /api/get-email-and-phone, *, *
p, *, *, POST, /api/login, *, * p, *, *, POST, /api/login, *, *
p, *, *, GET, /api/get-app-login, *, * p, *, *, GET, /api/get-app-login, *, *
p, *, *, POST, /api/logout, *, * p, *, *, POST, /api/logout, *, *
p, *, *, GET, /api/logout, *, *
p, *, *, GET, /api/get-account, *, * p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, * p, *, *, GET, /api/userinfo, *, *
p, *, *, *, /api/login/oauth, *, * p, *, *, *, /api/login/oauth, *, *

View File

@@ -228,7 +228,7 @@ func (c *ApiController) Signup() {
// @Tag Login API // @Tag Login API
// @Description logout the current user // @Description logout the current user
// @Success 200 {object} controllers.Response The Response object // @Success 200 {object} controllers.Response The Response object
// @router /logout [post] // @router /logout [get,post]
func (c *ApiController) Logout() { func (c *ApiController) Logout() {
user := c.GetSessionUsername() user := c.GetSessionUsername()
util.LogInfo(c.Ctx, "API: [%s] logged out", user) util.LogInfo(c.Ctx, "API: [%s] logged out", user)

View File

@@ -112,6 +112,11 @@ func (c *ApiController) GetUser() {
user = object.GetUser(id) user = object.GetUser(id)
} }
roles := object.GetRolesByUser(fmt.Sprintf("%s/%s", user.Owner, user.Name))
user.Roles = roles
permissions := object.GetPermissionsByUser(fmt.Sprintf("%s/%s", user.Owner, user.Name))
user.Permissions = permissions
c.Data["json"] = object.GetMaskedUser(user) c.Data["json"] = object.GetMaskedUser(user)
c.ServeJSON() c.ServeJSON()
} }

View File

@@ -49,8 +49,24 @@ func (c *ApiController) SendVerificationCode() {
applicationId := c.Ctx.Request.Form.Get("applicationId") applicationId := c.Ctx.Request.Form.Get("applicationId")
remoteAddr := util.GetIPFromRequest(c.Ctx.Request) remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
if destType == "" || dest == "" || applicationId == "" || !strings.Contains(applicationId, "/") || checkType == "" { if destType == "" {
c.ResponseError("Missing parameter.") c.ResponseError("Missing parameter: type.")
return
}
if dest == "" {
c.ResponseError("Missing parameter: dest.")
return
}
if applicationId == "" {
c.ResponseError("Missing parameter: applicationId.")
return
}
if !strings.Contains(applicationId, "/") {
c.ResponseError("Wrong parameter: applicationId.")
return
}
if checkType == "" {
c.ResponseError("Missing parameter: checkType.")
return return
} }

View File

@@ -139,7 +139,7 @@
"cryptoAlgorithm": "RS256", "cryptoAlgorithm": "RS256",
"bitSize": 4096, "bitSize": 4096,
"expireInYears": 20, "expireInYears": 20,
"publicKey": "", "certificate": "",
"privateKey": "" "privateKey": ""
} }
], ],

View File

@@ -33,7 +33,7 @@ type Cert struct {
BitSize int `json:"bitSize"` BitSize int `json:"bitSize"`
ExpireInYears int `json:"expireInYears"` ExpireInYears int `json:"expireInYears"`
PublicKey string `xorm:"mediumtext" json:"publicKey"` Certificate string `xorm:"mediumtext" json:"certificate"`
PrivateKey string `xorm:"mediumtext" json:"privateKey"` PrivateKey string `xorm:"mediumtext" json:"privateKey"`
AuthorityPublicKey string `xorm:"mediumtext" json:"authorityPublicKey"` AuthorityPublicKey string `xorm:"mediumtext" json:"authorityPublicKey"`
AuthorityRootPublicKey string `xorm:"mediumtext" json:"authorityRootPublicKey"` AuthorityRootPublicKey string `xorm:"mediumtext" json:"authorityRootPublicKey"`
@@ -123,9 +123,9 @@ func UpdateCert(id string, cert *Cert) bool {
} }
func AddCert(cert *Cert) bool { func AddCert(cert *Cert) bool {
if cert.PublicKey == "" || cert.PrivateKey == "" { if cert.Certificate == "" || cert.PrivateKey == "" {
publicKey, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner) certificate, privateKey := generateRsaKeys(cert.BitSize, cert.ExpireInYears, cert.Name, cert.Owner)
cert.PublicKey = publicKey cert.Certificate = certificate
cert.PrivateKey = privateKey cert.PrivateKey = privateKey
} }

View File

@@ -236,7 +236,7 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
allowed := true allowed := true
var err error var err error
for _, permission := range permissions { for _, permission := range permissions {
if !permission.IsEnabled { if !permission.IsEnabled || len(permission.Users) == 0 {
continue continue
} }

View File

@@ -25,6 +25,7 @@ import (
func InitDb() { func InitDb() {
existed := initBuiltInOrganization() existed := initBuiltInOrganization()
if !existed { if !existed {
initBuiltInPermission()
initBuiltInProvider() initBuiltInProvider()
initBuiltInUser() initBuiltInUser()
initBuiltInApplication() initBuiltInApplication()
@@ -70,6 +71,8 @@ func initBuiltInOrganization() bool {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"}, {Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, {Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
@@ -167,7 +170,7 @@ func readTokenFromFile() (string, string) {
} }
func initBuiltInCert() { func initBuiltInCert() {
tokenJwtPublicKey, tokenJwtPrivateKey := readTokenFromFile() tokenJwtCertificate, tokenJwtPrivateKey := readTokenFromFile()
cert := getCert("admin", "cert-built-in") cert := getCert("admin", "cert-built-in")
if cert != nil { if cert != nil {
return return
@@ -183,7 +186,7 @@ func initBuiltInCert() {
CryptoAlgorithm: "RS256", CryptoAlgorithm: "RS256",
BitSize: 4096, BitSize: 4096,
ExpireInYears: 20, ExpireInYears: 20,
PublicKey: tokenJwtPublicKey, Certificate: tokenJwtCertificate,
PrivateKey: tokenJwtPrivateKey, PrivateKey: tokenJwtPrivateKey,
} }
AddCert(cert) AddCert(cert)
@@ -230,3 +233,25 @@ func initBuiltInProvider() {
func initWebAuthn() { func initWebAuthn() {
gob.Register(webauthn.SessionData{}) gob.Register(webauthn.SessionData{})
} }
func initBuiltInPermission() {
permission := GetPermission("built-in/permission-built-in")
if permission != nil {
return
}
permission = &Permission{
Owner: "built-in",
Name: "permission-built-in",
CreatedTime: util.GetCurrentTime(),
DisplayName: "Built-in Permission",
Users: []string{"built-in/admin"},
Roles: []string{},
ResourceType: "Application",
Resources: []string{"app-built-in"},
Actions: []string{"Read", "Write", "Admin"},
Effect: "Allow",
IsEnabled: true,
}
AddPermission(permission)
}

View File

@@ -89,6 +89,8 @@ func initDefinedOrganization(organization *Organization) {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"}, {Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Tag", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"}, {Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"}, {Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"}, {Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},

View File

@@ -97,7 +97,7 @@ func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
//link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html //link here: https://self-issued.info/docs/draft-ietf-jose-json-web-key.html
//or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key //or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
for _, cert := range certs { for _, cert := range certs {
certPemBlock := []byte(cert.PublicKey) certPemBlock := []byte(cert.Certificate)
certDerBlock, _ := pem.Decode(certPemBlock) certDerBlock, _ := pem.Decode(certPemBlock)
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes) x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)

View File

@@ -229,3 +229,13 @@ func removePolicies(permission *Permission) {
panic(err) panic(err)
} }
} }
func GetPermissionsByUser(userId string) []*Permission {
permissions := []*Permission{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&permissions)
if err != nil {
panic(err)
}
return permissions
}

View File

@@ -30,7 +30,7 @@ func TestProduct(t *testing.T) {
product := GetProduct("admin/product_123") product := GetProduct("admin/product_123")
provider := getProvider(product.Owner, "provider_pay_alipay") provider := getProvider(product.Owner, "provider_pay_alipay")
cert := getCert(product.Owner, "cert-pay-alipay") cert := getCert(product.Owner, "cert-pay-alipay")
pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey) pProvider := pp.GetPaymentProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
paymentName := util.GenerateTimeId() paymentName := util.GenerateTimeId()
returnUrl := "" returnUrl := ""

View File

@@ -214,7 +214,7 @@ func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
} }
} }
pProvider := pp.GetPaymentProvider(p.Type, p.ClientId, p.ClientSecret, p.Host, cert.PublicKey, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey) pProvider := pp.GetPaymentProvider(p.Type, p.ClientId, p.ClientSecret, p.Host, cert.Certificate, cert.PrivateKey, cert.AuthorityPublicKey, cert.AuthorityRootPublicKey)
if pProvider == nil { if pProvider == nil {
return nil, cert, fmt.Errorf("the payment provider type: %s is not supported", p.Type) return nil, cert, fmt.Errorf("the payment provider type: %s is not supported", p.Type)
} }

View File

@@ -121,3 +121,13 @@ func DeleteRole(role *Role) bool {
func (role *Role) GetId() string { func (role *Role) GetId() string {
return fmt.Sprintf("%s/%s", role.Owner, role.Name) return fmt.Sprintf("%s/%s", role.Owner, role.Name)
} }
func GetRolesByUser(userId string) []*Role {
roles := []*Role{}
err := adapter.Engine.Where("users like ?", "%"+userId+"%").Find(&roles)
if err != nil {
panic(err)
}
return roles
}

View File

@@ -36,7 +36,7 @@ import (
) )
//returns a saml2 response //returns a saml2 response
func NewSamlResponse(user *User, host string, publicKey string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) { func NewSamlResponse(user *User, host string, certificate string, destination string, iss string, requestId string, redirectUri []string) (*etree.Element, error) {
samlResponse := &etree.Element{ samlResponse := &etree.Element{
Space: "samlp", Space: "samlp",
Tag: "Response", Tag: "Response",
@@ -177,8 +177,8 @@ type Attribute struct {
func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) { func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) {
//_, originBackend := getOriginFromHost(host) //_, originBackend := getOriginFromHost(host)
cert := getCertByApplication(application) cert := getCertByApplication(application)
block, _ := pem.Decode([]byte(cert.PublicKey)) block, _ := pem.Decode([]byte(cert.Certificate))
publicKey := base64.StdEncoding.EncodeToString(block.Bytes) certificate := base64.StdEncoding.EncodeToString(block.Bytes)
origin := beego.AppConfig.String("origin") origin := beego.AppConfig.String("origin")
originFrontend, originBackend := getOriginFromHost(host) originFrontend, originBackend := getOriginFromHost(host)
@@ -199,7 +199,7 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
KeyInfo: KeyInfo{ KeyInfo: KeyInfo{
X509Data: X509Data{ X509Data: X509Data{
X509Certificate: X509Certificate{ X509Certificate: X509Certificate{
Cert: publicKey, Cert: certificate,
}, },
}, },
}, },
@@ -248,18 +248,18 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
return "", "", fmt.Errorf("err: invalid issuer url") return "", "", fmt.Errorf("err: invalid issuer url")
} }
// get public key string // get certificate string
cert := getCertByApplication(application) cert := getCertByApplication(application)
block, _ := pem.Decode([]byte(cert.PublicKey)) block, _ := pem.Decode([]byte(cert.Certificate))
publicKey := base64.StdEncoding.EncodeToString(block.Bytes) certificate := base64.StdEncoding.EncodeToString(block.Bytes)
_, originBackend := getOriginFromHost(host) _, originBackend := getOriginFromHost(host)
// build signedResponse // build signedResponse
samlResponse, _ := NewSamlResponse(user, originBackend, publicKey, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris) samlResponse, _ := NewSamlResponse(user, originBackend, certificate, authnRequest.AssertionConsumerServiceURL, authnRequest.Issuer.Url, authnRequest.ID, application.RedirectUris)
randomKeyStore := &X509Key{ randomKeyStore := &X509Key{
PrivateKey: cert.PrivateKey, PrivateKey: cert.PrivateKey,
X509Certificate: publicKey, X509Certificate: certificate,
} }
ctx := dsig.NewDefaultSigningContext(randomKeyStore) ctx := dsig.NewDefaultSigningContext(randomKeyStore)
ctx.Hash = crypto.SHA1 ctx.Hash = crypto.SHA1

View File

@@ -55,7 +55,6 @@ func escapePath(path string) string {
func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) { func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool) (string, string) {
escapedPath := escapePath(fullFilePath) escapedPath := escapePath(fullFilePath)
escapedPath = escapePath(escapedPath)
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath) objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
host := "" host := ""
@@ -73,9 +72,9 @@ func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
host = fmt.Sprintf("%s/%s", host, provider.Bucket) host = fmt.Sprintf("%s/%s", host, provider.Bucket)
} }
fileUrl := util.UrlJoin(host, objectKey) fileUrl := util.UrlJoin(host, escapePath(objectKey))
if hasTimestamp { if hasTimestamp {
fileUrl = fmt.Sprintf("%s?t=%s", util.UrlJoin(host, objectKey), util.GetCurrentUnixTime()) fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
} }
return fileUrl, objectKey return fileUrl, objectKey

View File

@@ -241,11 +241,11 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
samlResponse := NewSamlResponse11(user, request.RequestID, host) samlResponse := NewSamlResponse11(user, request.RequestID, host)
cert := getCertByApplication(application) cert := getCertByApplication(application)
block, _ := pem.Decode([]byte(cert.PublicKey)) block, _ := pem.Decode([]byte(cert.Certificate))
publicKey := base64.StdEncoding.EncodeToString(block.Bytes) certificate := base64.StdEncoding.EncodeToString(block.Bytes)
randomKeyStore := &X509Key{ randomKeyStore := &X509Key{
PrivateKey: cert.PrivateKey, PrivateKey: cert.PrivateKey,
X509Certificate: publicKey, X509Certificate: certificate,
} }
ctx := dsig.NewDefaultSigningContext(randomKeyStore) ctx := dsig.NewDefaultSigningContext(randomKeyStore)

View File

@@ -129,13 +129,13 @@ func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
} }
// RSA public key // RSA certificate
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.PublicKey)) certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
if err != nil { if err != nil {
return nil, err return nil, err
} }
return publicKey, nil return certificate, nil
}) })
if t != nil { if t != nil {

View File

@@ -23,10 +23,10 @@ import (
func TestGenerateRsaKeys(t *testing.T) { func TestGenerateRsaKeys(t *testing.T) {
fileId := "token_jwt_key" fileId := "token_jwt_key"
publicKey, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization") certificate, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization")
// Write certificate (aka public key) to file. // Write certificate (aka certificate) to file.
util.WriteStringToPath(publicKey, fmt.Sprintf("%s.pem", fileId)) util.WriteStringToPath(certificate, fmt.Sprintf("%s.pem", fileId))
// Write private key to file. // Write private key to file.
util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId)) util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId))

View File

@@ -104,6 +104,9 @@ type User struct {
Ldap string `xorm:"ldap varchar(100)" json:"ldap"` Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
Properties map[string]string `json:"properties"` Properties map[string]string `json:"properties"`
Roles []*Role `json:"roles"`
Permissions []*Permission `json:"permissions"`
} }
type Userinfo struct { type Userinfo struct {

View File

@@ -106,6 +106,10 @@ func setUserProperty(user *User, field string, value string) {
if value == "" { if value == "" {
delete(user.Properties, field) delete(user.Properties, field)
} else { } else {
if user.Properties == nil {
user.Properties = make(map[string]string)
}
user.Properties[field] = value user.Properties[field] = value
} }
} }

View File

@@ -28,7 +28,7 @@ type AlipayPaymentProvider struct {
Client *alipay.Client Client *alipay.Client
} }
func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) *AlipayPaymentProvider { func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) *AlipayPaymentProvider {
pp := &AlipayPaymentProvider{} pp := &AlipayPaymentProvider{}
client, err := alipay.NewClient(appId, appPrivateKey, true) client, err := alipay.NewClient(appId, appPrivateKey, true)
@@ -36,7 +36,7 @@ func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey s
panic(err) panic(err)
} }
err = client.SetCertSnByContent([]byte(appPublicKey), []byte(authorityRootPublicKey), []byte(authorityPublicKey)) err = client.SetCertSnByContent([]byte(appCertificate), []byte(authorityRootPublicKey), []byte(authorityPublicKey))
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@@ -22,9 +22,9 @@ type PaymentProvider interface {
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
} }
func GetPaymentProvider(typ string, appId string, clientSecret string, host string, appPublicKey string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider { func GetPaymentProvider(typ string, appId string, clientSecret string, host string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string) PaymentProvider {
if typ == "Alipay" { if typ == "Alipay" {
return NewAlipayPaymentProvider(appId, appPublicKey, appPrivateKey, authorityPublicKey, authorityRootPublicKey) return NewAlipayPaymentProvider(appId, appCertificate, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
} else if typ == "GC" { } else if typ == "GC" {
return NewGcPaymentProvider(appId, clientSecret, host) return NewGcPaymentProvider(appId, clientSecret, host)
} }

View File

@@ -48,7 +48,7 @@ func initAPI() {
beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup") beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup")
beego.Router("/api/login", &controllers.ApiController{}, "POST:Login") beego.Router("/api/login", &controllers.ApiController{}, "POST:Login")
beego.Router("/api/get-app-login", &controllers.ApiController{}, "GET:GetApplicationLogin") beego.Router("/api/get-app-login", &controllers.ApiController{}, "GET:GetApplicationLogin")
beego.Router("/api/logout", &controllers.ApiController{}, "POST:Logout") beego.Router("/api/logout", &controllers.ApiController{}, "GET,POST:Logout")
beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount") beego.Router("/api/get-account", &controllers.ApiController{}, "GET:GetAccount")
beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo") beego.Router("/api/userinfo", &controllers.ApiController{}, "GET:GetUserinfo")
beego.Router("/api/unlink", &controllers.ApiController{}, "POST:Unlink") beego.Router("/api/unlink", &controllers.ApiController{}, "POST:Unlink")

View File

@@ -2418,6 +2418,21 @@
} }
}, },
"/api/logout": { "/api/logout": {
"get": {
"tags": [
"Login API"
],
"description": "logout the current user",
"operationId": "ApiController.Logout",
"responses": {
"200": {
"description": "The Response object",
"schema": {
"$ref": "#/definitions/controllers.Response"
}
}
}
},
"post": { "post": {
"tags": [ "tags": [
"Login API" "Login API"
@@ -3096,14 +3111,120 @@
], ],
"operationId": "ApiController.VerifyCaptcha" "operationId": "ApiController.VerifyCaptcha"
} }
},
"/api/webauthn/signin/begin": {
"get": {
"tags": [
"Login API"
],
"description": "WebAuthn Login Flow 1st stage",
"operationId": "ApiController.WebAuthnSigninBegin",
"parameters": [
{
"in": "query",
"name": "owner",
"description": "owner",
"required": true,
"type": "string"
},
{
"in": "query",
"name": "name",
"description": "name",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "The CredentialAssertion object",
"schema": {
"$ref": "#/definitions/protocol.CredentialAssertion"
}
}
}
}
},
"/api/webauthn/signin/finish": {
"post": {
"tags": [
"Login API"
],
"description": "WebAuthn Login Flow 2nd stage",
"operationId": "ApiController.WebAuthnSigninBegin",
"parameters": [
{
"in": "body",
"name": "body",
"description": "authenticator assertion Response",
"required": true,
"schema": {
"$ref": "#/definitions/protocol.CredentialAssertionResponse"
}
}
],
"responses": {
"200": {
"description": "\"The Response object\"",
"schema": {
"$ref": "#/definitions/Response"
}
}
}
}
},
"/api/webauthn/signup/begin": {
"get": {
"tags": [
"User API"
],
"description": "WebAuthn Registration Flow 1st stage",
"operationId": "ApiController.WebAuthnSignupBegin",
"responses": {
"200": {
"description": "The CredentialCreationOptions object",
"schema": {
"$ref": "#/definitions/protocol.CredentialCreation"
}
}
}
}
},
"/api/webauthn/signup/finish": {
"post": {
"tags": [
"User API"
],
"description": "WebAuthn Registration Flow 2nd stage",
"operationId": "ApiController.WebAuthnSignupFinish",
"parameters": [
{
"in": "body",
"name": "body",
"description": "authenticator attestation Response",
"required": true,
"schema": {
"$ref": "#/definitions/protocol.CredentialCreationResponse"
}
}
],
"responses": {
"200": {
"description": "\"The Response object\"",
"schema": {
"$ref": "#/definitions/Response"
}
}
}
}
} }
}, },
"definitions": { "definitions": {
"2127.0xc000398090.false": { "2127.0xc000427560.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
"2161.0xc0003980c0.false": { "2161.0xc000427590.false": {
"title": "false", "title": "false",
"type": "object" "type": "object"
}, },
@@ -3221,10 +3342,10 @@
"type": "object", "type": "object",
"properties": { "properties": {
"data": { "data": {
"$ref": "#/definitions/2127.0xc000398090.false" "$ref": "#/definitions/2127.0xc000427560.false"
}, },
"data2": { "data2": {
"$ref": "#/definitions/2161.0xc0003980c0.false" "$ref": "#/definitions/2161.0xc000427590.false"
}, },
"msg": { "msg": {
"type": "string" "type": "string"
@@ -3329,12 +3450,18 @@
"enablePassword": { "enablePassword": {
"type": "boolean" "type": "boolean"
}, },
"enableSamlCompress": {
"type": "boolean"
},
"enableSignUp": { "enableSignUp": {
"type": "boolean" "type": "boolean"
}, },
"enableSigninSession": { "enableSigninSession": {
"type": "boolean" "type": "boolean"
}, },
"enableWebAuthn": {
"type": "boolean"
},
"expireInHours": { "expireInHours": {
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
@@ -3444,7 +3571,7 @@
"privateKey": { "privateKey": {
"type": "string" "type": "string"
}, },
"publicKey": { "certificate": {
"type": "string" "type": "string"
}, },
"scope": { "scope": {
@@ -4507,6 +4634,12 @@
"updatedTime": { "updatedTime": {
"type": "string" "type": "string"
}, },
"webauthnCredentials": {
"type": "array",
"items": {
"$ref": "#/definitions/webauthn.Credential"
}
},
"wechat": { "wechat": {
"type": "string" "type": "string"
}, },
@@ -4596,6 +4729,26 @@
} }
} }
}, },
"protocol.CredentialAssertion": {
"title": "CredentialAssertion",
"type": "object"
},
"protocol.CredentialAssertionResponse": {
"title": "CredentialAssertionResponse",
"type": "object"
},
"protocol.CredentialCreation": {
"title": "CredentialCreation",
"type": "object"
},
"protocol.CredentialCreationResponse": {
"title": "CredentialCreationResponse",
"type": "object"
},
"webauthn.Credential": {
"title": "Credential",
"type": "object"
},
"xorm.Engine": { "xorm.Engine": {
"title": "Engine", "title": "Engine",
"type": "object" "type": "object"

View File

@@ -1584,6 +1584,16 @@ paths:
schema: schema:
$ref: '#/definitions/object.TokenError' $ref: '#/definitions/object.TokenError'
/api/logout: /api/logout:
get:
tags:
- Login API
description: logout the current user
operationId: ApiController.Logout
responses:
"200":
description: The Response object
schema:
$ref: '#/definitions/controllers.Response'
post: post:
tags: tags:
- Login API - Login API
@@ -2028,11 +2038,80 @@ paths:
tags: tags:
- Verification API - Verification API
operationId: ApiController.VerifyCaptcha operationId: ApiController.VerifyCaptcha
/api/webauthn/signin/begin:
get:
tags:
- Login API
description: WebAuthn Login Flow 1st stage
operationId: ApiController.WebAuthnSigninBegin
parameters:
- in: query
name: owner
description: owner
required: true
type: string
- in: query
name: name
description: name
required: true
type: string
responses:
"200":
description: The CredentialAssertion object
schema:
$ref: '#/definitions/protocol.CredentialAssertion'
/api/webauthn/signin/finish:
post:
tags:
- Login API
description: WebAuthn Login Flow 2nd stage
operationId: ApiController.WebAuthnSigninBegin
parameters:
- in: body
name: body
description: authenticator assertion Response
required: true
schema:
$ref: '#/definitions/protocol.CredentialAssertionResponse'
responses:
"200":
description: '"The Response object"'
schema:
$ref: '#/definitions/Response'
/api/webauthn/signup/begin:
get:
tags:
- User API
description: WebAuthn Registration Flow 1st stage
operationId: ApiController.WebAuthnSignupBegin
responses:
"200":
description: The CredentialCreationOptions object
schema:
$ref: '#/definitions/protocol.CredentialCreation'
/api/webauthn/signup/finish:
post:
tags:
- User API
description: WebAuthn Registration Flow 2nd stage
operationId: ApiController.WebAuthnSignupFinish
parameters:
- in: body
name: body
description: authenticator attestation Response
required: true
schema:
$ref: '#/definitions/protocol.CredentialCreationResponse'
responses:
"200":
description: '"The Response object"'
schema:
$ref: '#/definitions/Response'
definitions: definitions:
2127.0xc000398090.false: 2127.0xc000427560.false:
title: "false" title: "false"
type: object type: object
2161.0xc0003980c0.false: 2161.0xc000427590.false:
title: "false" title: "false"
type: object type: object
Response: Response:
@@ -2113,9 +2192,9 @@ definitions:
type: object type: object
properties: properties:
data: data:
$ref: '#/definitions/2127.0xc000398090.false' $ref: '#/definitions/2127.0xc000427560.false'
data2: data2:
$ref: '#/definitions/2161.0xc0003980c0.false' $ref: '#/definitions/2161.0xc000427590.false'
msg: msg:
type: string type: string
name: name:
@@ -2185,10 +2264,14 @@ definitions:
type: boolean type: boolean
enablePassword: enablePassword:
type: boolean type: boolean
enableSamlCompress:
type: boolean
enableSignUp: enableSignUp:
type: boolean type: boolean
enableSigninSession: enableSigninSession:
type: boolean type: boolean
enableWebAuthn:
type: boolean
expireInHours: expireInHours:
type: integer type: integer
format: int64 format: int64
@@ -2263,7 +2346,7 @@ definitions:
type: string type: string
privateKey: privateKey:
type: string type: string
publicKey: certificate:
type: string type: string
scope: scope:
type: string type: string
@@ -2977,6 +3060,10 @@ definitions:
type: string type: string
updatedTime: updatedTime:
type: string type: string
webauthnCredentials:
type: array
items:
$ref: '#/definitions/webauthn.Credential'
wechat: wechat:
type: string type: string
wecom: wecom:
@@ -3035,6 +3122,21 @@ definitions:
type: string type: string
url: url:
type: string type: string
protocol.CredentialAssertion:
title: CredentialAssertion
type: object
protocol.CredentialAssertionResponse:
title: CredentialAssertionResponse
type: object
protocol.CredentialCreation:
title: CredentialCreation
type: object
protocol.CredentialCreationResponse:
title: CredentialCreationResponse
type: object
webauthn.Credential:
title: Credential
type: object
xorm.Engine: xorm.Engine:
title: Engine title: Engine
type: object type: object

View File

@@ -45,6 +45,10 @@
"curly": ["error", "all"], "curly": ["error", "all"],
"brace-style": ["error", "1tbs", { "allowSingleLine": true }], "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
"no-mixed-spaces-and-tabs": "error", "no-mixed-spaces-and-tabs": "error",
"sort-imports": ["error", {
"ignoreDeclarationSort": true
}],
"react/prop-types": "off", "react/prop-types": "off",
"react/display-name": "off", "react/display-name": "off",

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {DownOutlined, DeleteOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Row, Select, Switch, Table, Tooltip} from "antd"; import {Button, Col, Row, Select, Switch, Table, Tooltip} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
@@ -86,6 +86,8 @@ class AccountTable extends React.Component {
{name: "Bio", displayName: i18next.t("user:Bio")}, {name: "Bio", displayName: i18next.t("user:Bio")},
{name: "Tag", displayName: i18next.t("user:Tag")}, {name: "Tag", displayName: i18next.t("user:Tag")},
{name: "Signup application", displayName: i18next.t("general:Signup application")}, {name: "Signup application", displayName: i18next.t("general:Signup application")},
{name: "Roles", displayName: i18next.t("general:Roles")},
{name: "Permissions", displayName: i18next.t("general:Permissions")},
{name: "3rd-party logins", displayName: i18next.t("user:3rd-party logins")}, {name: "3rd-party logins", displayName: i18next.t("user:3rd-party logins")},
{name: "Properties", displayName: i18next.t("user:Properties")}, {name: "Properties", displayName: i18next.t("user:Properties")},
{name: "Is admin", displayName: i18next.t("user:Is admin")}, {name: "Is admin", displayName: i18next.t("user:Is admin")},

View File

@@ -17,7 +17,7 @@ import "./App.less";
import {Helmet} from "react-helmet"; import {Helmet} from "react-helmet";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons"; import {DownOutlined, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
import {Avatar, BackTop, Dropdown, Layout, Menu, Card, Result, Button} from "antd"; import {Avatar, BackTop, Button, Card, Dropdown, Layout, Menu, Result} from "antd";
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom"; import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
import OrganizationListPage from "./OrganizationListPage"; import OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage"; import OrganizationEditPage from "./OrganizationEditPage";
@@ -235,6 +235,8 @@ class App extends Component {
AuthBackend.logout() AuthBackend.logout()
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
const owner = this.state.account.owner;
this.setState({ this.setState({
account: null account: null
}); });
@@ -243,7 +245,9 @@ class App extends Component {
let redirectUri = res.data2; let redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") { if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri); Setting.goToLink(redirectUri);
}else{ } else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft(this, "/"); Setting.goToLinkSoft(this, "/");
} }
} else { } else {
@@ -669,7 +673,9 @@ class App extends Component {
<Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} />)} /> <Route exact path="/signup" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} />)} />
<Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />)} /> <Route exact path="/signup/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />)} />
<Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} /> <Route exact path="/login" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
<Route exact path="/signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} /> <Route exact path="/login/:owner" render={(props) => this.renderHomeIfLoggedIn(<SelfLoginPage account={this.state.account} {...props} />)} />
<Route exact path="/auto-signup/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signup"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
<Route exact path="/signup/oauth/authorize" render={(props) => <SignupPage account={this.state.account} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
<Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} /> <Route exact path="/login/oauth/authorize" render={(props) => <LoginPage account={this.state.account} type={"code"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
<Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage account={this.state.account} type={"saml"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} /> <Route exact path="/login/saml/authorize/:owner/:applicationName" render={(props) => <LoginPage account={this.state.account} type={"saml"} mode={"signin"} {...props} onUpdateAccount={(account) => {this.onUpdateAccount(account);}} />} />
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout clearAccount={() => this.setState({account: null})} {...props} />)} /> <Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout clearAccount={() => this.setState({account: null})} {...props} />)} />

View File

@@ -164,25 +164,25 @@ class CertEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("cert:Public key"), i18next.t("cert:Public key - Tooltip"))} : {Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
</Col> </Col>
<Col span={9} > <Col span={9} >
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => { <Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
copy(this.state.cert.publicKey); copy(this.state.cert.certificate);
Setting.showMessage("success", i18next.t("cert:Public key copied to clipboard successfully")); Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
}} }}
> >
{i18next.t("cert:Copy public key")} {i18next.t("cert:Copy certificate")}
</Button> </Button>
<Button type="primary" onClick={() => { <Button type="primary" onClick={() => {
const blob = new Blob([this.state.cert.publicKey], {type: "text/plain;charset=utf-8"}); const blob = new Blob([this.state.cert.certificate], {type: "text/plain;charset=utf-8"});
FileSaver.saveAs(blob, "token_jwt_key.pem"); FileSaver.saveAs(blob, "token_jwt_key.pem");
}} }}
> >
{i18next.t("cert:Download public key")} {i18next.t("cert:Download certificate")}
</Button> </Button>
<TextArea autoSize={{minRows: 30, maxRows: 30}} value={this.state.cert.publicKey} onChange={e => { <TextArea autoSize={{minRows: 30, maxRows: 30}} value={this.state.cert.certificate} onChange={e => {
this.updateCertField("publicKey", e.target.value); this.updateCertField("certificate", e.target.value);
}} /> }} />
</Col> </Col>
<Col span={1} /> <Col span={1} />

View File

@@ -34,7 +34,7 @@ class CertListPage extends BaseListPage {
cryptoAlgorithm: "RS256", cryptoAlgorithm: "RS256",
bitSize: 4096, bitSize: 4096,
expireInYears: 20, expireInYears: 20,
publicKey: "", certificate: "",
privateKey: "", privateKey: "",
}; };
} }

View File

@@ -16,7 +16,7 @@ import React, {useState} from "react";
import Cropper from "react-cropper"; import Cropper from "react-cropper";
import "cropperjs/dist/cropper.css"; import "cropperjs/dist/cropper.css";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {Button, Row, Col, Modal} from "antd"; import {Button, Col, Modal, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import * as ResourceBackend from "./backend/ResourceBackend"; import * as ResourceBackend from "./backend/ResourceBackend";

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Col, Row, Table, Popconfirm} from "antd"; import {Button, Col, Popconfirm, Row, Table} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as LdapBackend from "./backend/LdapBackend"; import * as LdapBackend from "./backend/LdapBackend";
import i18next from "i18next"; import i18next from "i18next";

View File

@@ -57,6 +57,8 @@ class OrganizationListPage extends BaseListPage {
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"}, {name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
{name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"}, {name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"}, {name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
{name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"}, {name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"},
{name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"}, {name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"},
{name: "Is admin", visible: true, viewRule: "Admin", modifyRule: "Admin"}, {name: "Is admin", visible: true, viewRule: "Admin", modifyRule: "Admin"},

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import {Button, Col, Modal, Row, Input} from "antd"; import {Button, Col, Input, Modal, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import React from "react"; import React from "react";
import * as UserBackend from "./backend/UserBackend"; import * as UserBackend from "./backend/UserBackend";

View File

@@ -622,7 +622,7 @@ class ProviderEditPage extends React.Component {
</Row> </Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:IdP"), i18next.t("provider:IdP public key"))} : {Setting.getLabel(i18next.t("provider:IdP"), i18next.t("provider:IdP certificate"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.provider.idP} onChange={e => { <Input value={this.state.provider.idP} onChange={e => {

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {DownOutlined, DeleteOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Row, Select, Switch, Table, Tooltip} from "antd"; import {Button, Col, Row, Select, Switch, Table, Tooltip} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import {Button, Col, Modal, Row, Input} from "antd"; import {Button, Col, Input, Modal, Row} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "./Setting";

View File

@@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import {Menu, Dropdown} from "antd"; import {Dropdown, Menu} from "antd";
import {createFromIconfontCN} from "@ant-design/icons"; import {createFromIconfontCN} from "@ant-design/icons";
import "./App.less"; import "./App.less";

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
import {message, Tag, Tooltip} from "antd"; import {Tag, Tooltip, message} from "antd";
import {QuestionCircleTwoTone} from "@ant-design/icons"; import {QuestionCircleTwoTone} from "@ant-design/icons";
import React from "react"; import React from "react";
import {isMobile as isMobileDevice} from "react-device-detect"; import {isMobile as isMobileDevice} from "react-device-detect";
@@ -662,8 +662,8 @@ export function goToLogin(ths, application) {
return; return;
} }
if (!application.enablePassword && window.location.pathname.includes("/signup/oauth/authorize")) { if (!application.enablePassword && window.location.pathname.includes("/auto-signup/oauth/authorize")) {
const link = window.location.href.replace("/signup/oauth/authorize", "/login/oauth/authorize"); const link = window.location.href.replace("/auto-signup/oauth/authorize", "/login/oauth/authorize");
goToLink(link); goToLink(link);
return; return;
} }
@@ -685,7 +685,7 @@ export function goToSignup(ths, application) {
} }
if (!application.enablePassword && window.location.pathname.includes("/login/oauth/authorize")) { if (!application.enablePassword && window.location.pathname.includes("/login/oauth/authorize")) {
const link = window.location.href.replace("/login/oauth/authorize", "/signup/oauth/authorize"); const link = window.location.href.replace("/login/oauth/authorize", "/auto-signup/oauth/authorize");
goToLink(link); goToLink(link);
return; return;
} }

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {DownOutlined, DeleteOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Row, Select, Switch, Table, Tooltip} from "antd"; import {Button, Col, Row, Select, Switch, Table, Tooltip} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {DownOutlined, DeleteOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd"; import {Button, Col, Input, Row, Select, Switch, Table, Tooltip} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {DownOutlined, DeleteOutlined, UpOutlined, LinkOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, LinkOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Table, Tooltip} from "antd"; import {Button, Col, Input, Row, Table, Tooltip} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";

View File

@@ -427,6 +427,32 @@ class UserEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
); );
} else if (accountItem.name === "Roles") {
return (
<Row style={{marginTop: "20px", alignItems: "center"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Roles"), i18next.t("general:Roles - Tooltip"))} :
</Col>
<Col span={22} >
{
Setting.getTags(this.state.user.roles.map(role => role.name))
}
</Col>
</Row>
);
} else if (accountItem.name === "Permissions") {
return (
<Row style={{marginTop: "20px", alignItems: "center"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Permissions"), i18next.t("general:Permissions - Tooltip"))} :
</Col>
<Col span={22} >
{
Setting.getTags(this.state.user.permissions.map(permission => permission.name))
}
</Col>
</Row>
);
} else if (accountItem.name === "3rd-party logins") { } else if (accountItem.name === "3rd-party logins") {
return ( return (
!this.isSelfOrAdmin() ? null : ( !this.isSelfOrAdmin() ? null : (

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {DownOutlined, DeleteOutlined, UpOutlined} from "@ant-design/icons"; import {DeleteOutlined, DownOutlined, UpOutlined} from "@ant-design/icons";
import {Button, Col, Input, Row, Table, Tooltip} from "antd"; import {Button, Col, Input, Row, Table, Tooltip} from "antd";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";

View File

@@ -191,6 +191,10 @@ class LoginPage extends React.Component {
values["type"] = "saml"; values["type"] = "saml";
} }
if (this.state.owner != null) {
values["organization"] = this.state.owner;
}
AuthBackend.login(values, oAuthParams) AuthBackend.login(values, oAuthParams)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
@@ -203,6 +207,7 @@ class LoginPage extends React.Component {
} else if (responseType === "code") { } else if (responseType === "code") {
const code = res.data; const code = res.data;
const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?"; const concatChar = oAuthParams?.redirectUri?.includes("?") ? "&" : "?";
const noRedirect = oAuthParams.noRedirect;
if (Setting.hasPromptPage(application)) { if (Setting.hasPromptPage(application)) {
AuthBackend.getAccount("") AuthBackend.getAccount("")
@@ -224,7 +229,19 @@ class LoginPage extends React.Component {
} }
}); });
} else { } else {
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`); if (noRedirect === "true") {
window.close();
const newWindow = window.open(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
if (newWindow) {
setInterval(() => {
if (!newWindow.closed) {
newWindow.close();
}
}, 1000);
}
} else {
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
}
} }
// Util.showMessage("success", `Authorization code: ${res.data}`); // Util.showMessage("success", `Authorization code: ${res.data}`);
@@ -488,7 +505,7 @@ class LoginPage extends React.Component {
) )
} }
{ {
!application.enableSignUp ? null : this.renderFooter(application) this.renderFooter(application)
} }
</Form.Item> </Form.Item>
<Form.Item> <Form.Item>
@@ -518,16 +535,12 @@ class LoginPage extends React.Component {
return this.renderProviderLogo(providerItem.provider, application, 40, 10, "big"); return this.renderProviderLogo(providerItem.provider, application, 40, 10, "big");
}) })
} }
{ <div>
!application.enableSignUp ? null : ( <br />
<div> {
<br /> this.renderFooter(application)
{ }
this.renderFooter(application) </div>
}
</div>
)
}
</div> </div>
); );
} }
@@ -562,13 +575,19 @@ class LoginPage extends React.Component {
} }
</span> </span>
<span style={{float: "right"}}> <span style={{float: "right"}}>
{i18next.t("login:No account?")}&nbsp; {
<a onClick={() => { !application.enableSignUp ? null : (
sessionStorage.setItem("signinUrl", window.location.href); <>
Setting.goToSignup(this, application); {i18next.t("login:No account?")}&nbsp;
}}> <a onClick={() => {
{i18next.t("login:sign up now")} sessionStorage.setItem("signinUrl", window.location.href);
</a> Setting.goToSignup(this, application);
}}>
{i18next.t("login:sign up now")}
</a>
</>
)
}
</span> </span>
</React.Fragment> </React.Fragment>
); );
@@ -638,13 +657,13 @@ class LoginPage extends React.Component {
throw credentialRequestOptions.status.msg; throw credentialRequestOptions.status.msg;
} }
credentialRequestOptions.publicKey.challenge = UserWebauthnBackend.webAuthnBufferDecode(credentialRequestOptions.publicKey.challenge); credentialRequestOptions.certificate.challenge = UserWebauthnBackend.webAuthnBufferDecode(credentialRequestOptions.certificate.challenge);
credentialRequestOptions.publicKey.allowCredentials.forEach(function(listItem) { credentialRequestOptions.certificate.allowCredentials.forEach(function(listItem) {
listItem.id = UserWebauthnBackend.webAuthnBufferDecode(listItem.id); listItem.id = UserWebauthnBackend.webAuthnBufferDecode(listItem.id);
}); });
return navigator.credentials.get({ return navigator.credentials.get({
publicKey: credentialRequestOptions.publicKey certificate: credentialRequestOptions.certificate
}); });
}) })
.then((assertion) => { .then((assertion) => {
@@ -692,7 +711,7 @@ class LoginPage extends React.Component {
> >
<CountDownInput <CountDownInput
disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone} disabled={this.state.username?.length === 0 || !this.state.validEmailOrPhone}
onButtonClickArgs={[this.state.username, "", Setting.getApplicationOrgName(application), true]} onButtonClickArgs={[this.state.username, this.state.validEmail ? "email" : "phone", Setting.getApplicationName(application)]}
/> />
</Form.Item> </Form.Item>
) : ( ) : (

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Result, Button} from "antd"; import {Button, Result} from "antd";
import i18next from "i18next"; import i18next from "i18next";
import {authConfig} from "./Auth"; import {authConfig} from "./Auth";
import * as Util from "./Util"; import * as Util from "./Util";

View File

@@ -14,7 +14,7 @@
import React from "react"; import React from "react";
import {Link} from "react-router-dom"; import {Link} from "react-router-dom";
import {Form, Input, Checkbox, Button, Row, Col, Result, Modal} from "antd"; import {Button, Checkbox, Col, Form, Input, Modal, Result, Row} from "antd";
import * as Setting from "../Setting"; import * as Setting from "../Setting";
import * as AuthBackend from "./AuthBackend"; import * as AuthBackend from "./AuthBackend";
import i18next from "i18next"; import i18next from "i18next";
@@ -79,19 +79,28 @@ class SignupPage extends React.Component {
} }
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
if (this.state.applicationName !== undefined) { let applicationName = this.state.applicationName;
this.getApplication(); const oAuthParams = Util.getOAuthGetParameters();
if (oAuthParams !== null) {
applicationName = oAuthParams.state;
this.setState({applicationName: oAuthParams.state});
const signinUrl = window.location.href.replace("/signup/oauth/authorize", "/login/oauth/authorize");
sessionStorage.setItem("signinUrl", signinUrl);
}
if (applicationName !== undefined) {
this.getApplication(applicationName);
} else { } else {
Util.showMessage("error", `Unknown application name: ${this.state.applicationName}`); Util.showMessage("error", `Unknown application name: ${applicationName}`);
} }
} }
getApplication() { getApplication(applicationName) {
if (this.state.applicationName === undefined) { if (applicationName === undefined) {
return; return;
} }
ApplicationBackend.getApplication("admin", this.state.applicationName) ApplicationBackend.getApplication("admin", applicationName)
.then((application) => { .then((application) => {
this.setState({ this.setState({
application: application, application: application,

View File

@@ -13,7 +13,7 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Alert, Button, message, Result} from "antd"; import {Alert, Button, Result, message} from "antd";
export function showMessage(type, text) { export function showMessage(type, text) {
if (type === "success") { if (type === "success") {
@@ -103,6 +103,7 @@ export function getOAuthGetParameters(params) {
const codeChallenge = getRefinedValue(queries.get("code_challenge")); const codeChallenge = getRefinedValue(queries.get("code_challenge"));
const samlRequest = getRefinedValue(queries.get("SAMLRequest")); const samlRequest = getRefinedValue(queries.get("SAMLRequest"));
const relayState = getRefinedValue(queries.get("RelayState")); const relayState = getRefinedValue(queries.get("RelayState"));
const noRedirect = getRefinedValue(queries.get("noRedirect"));
if ((clientId === undefined || clientId === null || clientId === "") && (samlRequest === "" || samlRequest === undefined)) { if ((clientId === undefined || clientId === null || clientId === "") && (samlRequest === "" || samlRequest === undefined)) {
// login // login
@@ -120,6 +121,7 @@ export function getOAuthGetParameters(params) {
codeChallenge: codeChallenge, codeChallenge: codeChallenge,
samlRequest: samlRequest, samlRequest: samlRequest,
relayState: relayState, relayState: relayState,
noRedirect: noRedirect,
}; };
} }
} }

View File

@@ -21,15 +21,15 @@ export function registerWebauthnCredential() {
}) })
.then(res => res.json()) .then(res => res.json())
.then((credentialCreationOptions) => { .then((credentialCreationOptions) => {
credentialCreationOptions.publicKey.challenge = webAuthnBufferDecode(credentialCreationOptions.publicKey.challenge); credentialCreationOptions.certificate.challenge = webAuthnBufferDecode(credentialCreationOptions.certificate.challenge);
credentialCreationOptions.publicKey.user.id = webAuthnBufferDecode(credentialCreationOptions.publicKey.user.id); credentialCreationOptions.certificate.user.id = webAuthnBufferDecode(credentialCreationOptions.certificate.user.id);
if (credentialCreationOptions.publicKey.excludeCredentials) { if (credentialCreationOptions.certificate.excludeCredentials) {
for (var i = 0; i < credentialCreationOptions.publicKey.excludeCredentials.length; i++) { for (var i = 0; i < credentialCreationOptions.certificate.excludeCredentials.length; i++) {
credentialCreationOptions.publicKey.excludeCredentials[i].id = webAuthnBufferDecode(credentialCreationOptions.publicKey.excludeCredentials[i].id); credentialCreationOptions.certificate.excludeCredentials[i].id = webAuthnBufferDecode(credentialCreationOptions.certificate.excludeCredentials[i].id);
} }
} }
return navigator.credentials.create({ return navigator.credentials.create({
publicKey: credentialCreationOptions.publicKey certificate: credentialCreationOptions.certificate
}); });
}) })
.then((credential) => { .then((credential) => {

View File

@@ -51,11 +51,11 @@
"Bit size": "Bitgröße", "Bit size": "Bitgröße",
"Bit size - Tooltip": "Bit Größe - Tooltip", "Bit size - Tooltip": "Bit Größe - Tooltip",
"Copy private key": "Privaten Schlüssel kopieren", "Copy private key": "Privaten Schlüssel kopieren",
"Copy public key": "Öffentlichen Schlüssel kopieren", "Copy certificate": "Kopieren des Zertifikats",
"Crypto algorithm": "Crypto-Algorithmus", "Crypto algorithm": "Crypto-Algorithmus",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "Privaten Schlüssel herunterladen", "Download private key": "Privaten Schlüssel herunterladen",
"Download public key": "Öffentlichen Schlüssel herunterladen", "Download certificate": "Zertifikat herunterladen",
"Edit Cert": "Zitat bearbeiten", "Edit Cert": "Zitat bearbeiten",
"Expire in years": "Gültig in Jahren", "Expire in years": "Gültig in Jahren",
"Expire in years - Tooltip": "Verfällt in Jahren - Tooltip", "Expire in years - Tooltip": "Verfällt in Jahren - Tooltip",
@@ -63,9 +63,9 @@
"Private key": "Privater Schlüssel", "Private key": "Privater Schlüssel",
"Private key - Tooltip": "Privater Schlüssel - Tooltip", "Private key - Tooltip": "Privater Schlüssel - Tooltip",
"Private key copied to clipboard successfully": "Privater Schlüssel erfolgreich in die Zwischenablage kopiert", "Private key copied to clipboard successfully": "Privater Schlüssel erfolgreich in die Zwischenablage kopiert",
"Public key": "Öffentlicher Schlüssel", "Certificate": "Zertifikat",
"Public key - Tooltip": "Öffentlicher Schlüssel - Tooltip", "Certificate - Tooltip": "Zertifikat - Tooltip",
"Public key copied to clipboard successfully": "Öffentlicher Schlüssel erfolgreich in die Zwischenablage kopiert", "Certificate copied to clipboard successfully": "Das Zertifikat wurde erfolgreich in die Zwischenablage kopiert",
"Scope": "Bereich", "Scope": "Bereich",
"Scope - Tooltip": "Bereich - Tooltip", "Scope - Tooltip": "Bereich - Tooltip",
"Type": "Typ", "Type": "Typ",
@@ -426,7 +426,7 @@
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Unique string-style identifier", "Host - Tooltip": "Unique string-style identifier",
"IdP": "IdP", "IdP": "IdP",
"IdP public key": "IdP-öffentlicher Schlüssel", "IdP certificate": "IdP-öffentlicher Schlüssel",
"Issuer URL": "Ausgabe-URL", "Issuer URL": "Ausgabe-URL",
"Issuer URL - Tooltip": "Ausgabe-URL - Tooltip", "Issuer URL - Tooltip": "Ausgabe-URL - Tooltip",
"Link copied to clipboard successfully": "Link erfolgreich in die Zwischenablage kopiert", "Link copied to clipboard successfully": "Link erfolgreich in die Zwischenablage kopiert",

View File

@@ -51,11 +51,11 @@
"Bit size": "Bit size", "Bit size": "Bit size",
"Bit size - Tooltip": "Bit size - Tooltip", "Bit size - Tooltip": "Bit size - Tooltip",
"Copy private key": "Copy private key", "Copy private key": "Copy private key",
"Copy public key": "Copy public key", "Copy certificate": "Copy certificate",
"Crypto algorithm": "Crypto algorithm", "Crypto algorithm": "Crypto algorithm",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "Download private key", "Download private key": "Download private key",
"Download public key": "Download public key", "Download certificate": "Download certificate",
"Edit Cert": "Edit Cert", "Edit Cert": "Edit Cert",
"Expire in years": "Expire in years", "Expire in years": "Expire in years",
"Expire in years - Tooltip": "Expire in years - Tooltip", "Expire in years - Tooltip": "Expire in years - Tooltip",
@@ -63,9 +63,9 @@
"Private key": "Private key", "Private key": "Private key",
"Private key - Tooltip": "Private key - Tooltip", "Private key - Tooltip": "Private key - Tooltip",
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully", "Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
"Public key": "Public key", "Certificate": "Certificate",
"Public key - Tooltip": "Public key - Tooltip", "Certificate - Tooltip": "Certificate - Tooltip",
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully", "Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
"Scope": "Scope", "Scope": "Scope",
"Scope - Tooltip": "Scope - Tooltip", "Scope - Tooltip": "Scope - Tooltip",
"Type": "Type", "Type": "Type",
@@ -426,7 +426,7 @@
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Host - Tooltip", "Host - Tooltip": "Host - Tooltip",
"IdP": "IdP", "IdP": "IdP",
"IdP public key": "IdP public key", "IdP certificate": "IdP certificate",
"Issuer URL": "Issuer URL", "Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL - Tooltip", "Issuer URL - Tooltip": "Issuer URL - Tooltip",
"Link copied to clipboard successfully": "Link copied to clipboard successfully", "Link copied to clipboard successfully": "Link copied to clipboard successfully",

View File

@@ -51,21 +51,21 @@
"Bit size": "Taille du bit", "Bit size": "Taille du bit",
"Bit size - Tooltip": "Taille du bit - Infobulle", "Bit size - Tooltip": "Taille du bit - Infobulle",
"Copy private key": "Copier la clé privée", "Copy private key": "Copier la clé privée",
"Copy public key": "Copier la clé publique", "Copy certificate": "Copier le certificate",
"Crypto algorithm": "Algorithme de cryptomonnaie", "Crypto algorithm": "Algorithme de cryptomonnaie",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "Télécharger la clé privée", "Download private key": "Télécharger la clé privée",
"Download public key": "Télécharger la clé publique", "Download certificate": "Télécharger le certificate",
"Edit Cert": "Modifier le certificat", "Edit Cert": "Modifier le certificate",
"Expire in years": "Expire dans les années", "Expire in years": "Expire dans les années",
"Expire in years - Tooltip": "Expire dans les années - infobulle", "Expire in years - Tooltip": "Expire dans les années - infobulle",
"New Cert": "New Cert", "New Cert": "New Cert",
"Private key": "Clé privée", "Private key": "Clé privée",
"Private key - Tooltip": "Clé privée - Infobulle", "Private key - Tooltip": "Clé privée - Infobulle",
"Private key copied to clipboard successfully": "Clé privée copiée dans le presse-papiers avec succès", "Private key copied to clipboard successfully": "Clé privée copiée dans le presse-papiers avec succès",
"Public key": "Clé publique", "Certificate": "certificate",
"Public key - Tooltip": "Clé publique - Infobulle", "Certificate - Tooltip": "certificate - Infobulle",
"Public key copied to clipboard successfully": "Clé publique copiée dans le presse-papiers avec succès", "Certificate copied to clipboard successfully": "Le certificate a été copié avec succès dans le presse-papiers",
"Scope": "Périmètre d'application", "Scope": "Périmètre d'application",
"Scope - Tooltip": "Scope - Infobulle", "Scope - Tooltip": "Scope - Infobulle",
"Type": "Type de texte", "Type": "Type de texte",
@@ -426,7 +426,7 @@
"Host": "Hôte", "Host": "Hôte",
"Host - Tooltip": "Unique string-style identifier", "Host - Tooltip": "Unique string-style identifier",
"IdP": "IDP", "IdP": "IDP",
"IdP public key": "Clé publique IdP", "IdP certificate": "Clé publique IdP",
"Issuer URL": "URL de l'émetteur", "Issuer URL": "URL de l'émetteur",
"Issuer URL - Tooltip": "URL de l'émetteur - infobulle", "Issuer URL - Tooltip": "URL de l'émetteur - infobulle",
"Link copied to clipboard successfully": "Lien copié dans le presse-papiers avec succès", "Link copied to clipboard successfully": "Lien copié dans le presse-papiers avec succès",

View File

@@ -51,11 +51,11 @@
"Bit size": "ビットサイズ", "Bit size": "ビットサイズ",
"Bit size - Tooltip": "ビットサイズ - ツールチップ", "Bit size - Tooltip": "ビットサイズ - ツールチップ",
"Copy private key": "秘密鍵をコピー", "Copy private key": "秘密鍵をコピー",
"Copy public key": "公開鍵をコピー", "Copy certificate": "証明書をコピーします",
"Crypto algorithm": "暗号化アルゴリズム", "Crypto algorithm": "暗号化アルゴリズム",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "秘密鍵をダウンロード", "Download private key": "秘密鍵をダウンロード",
"Download public key": "公開鍵をダウンロード", "Download certificate": "証明書をダウンロードします",
"Edit Cert": "Certを編集", "Edit Cert": "Certを編集",
"Expire in years": "有効期限", "Expire in years": "有効期限",
"Expire in years - Tooltip": "年間有効期限 - ツールチップ", "Expire in years - Tooltip": "年間有効期限 - ツールチップ",
@@ -63,9 +63,9 @@
"Private key": "Private key", "Private key": "Private key",
"Private key - Tooltip": "Private key - Tooltip", "Private key - Tooltip": "Private key - Tooltip",
"Private key copied to clipboard successfully": "秘密鍵を正常にクリップボードにコピーしました", "Private key copied to clipboard successfully": "秘密鍵を正常にクリップボードにコピーしました",
"Public key": "公開キー", "Certificate": "Certificate",
"Public key - Tooltip": "Public key - Tooltip", "Certificate - Tooltip": "Certificate - Tooltip",
"Public key copied to clipboard successfully": "公開鍵を正常にクリップボードにコピーました", "Certificate copied to clipboard successfully": "証明書はクリップボードに正常にコピーされました",
"Scope": "スコープ", "Scope": "スコープ",
"Scope - Tooltip": "スコープ → ツールチップ", "Scope - Tooltip": "スコープ → ツールチップ",
"Type": "タイプ", "Type": "タイプ",
@@ -426,7 +426,7 @@
"Host": "ホスト", "Host": "ホスト",
"Host - Tooltip": "Unique string-style identifier", "Host - Tooltip": "Unique string-style identifier",
"IdP": "IdP", "IdP": "IdP",
"IdP public key": "IdP public key", "IdP certificate": "IdP certificate",
"Issuer URL": "Issuer URL", "Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL - ツールチップ", "Issuer URL - Tooltip": "Issuer URL - ツールチップ",
"Link copied to clipboard successfully": "リンクをクリップボードにコピーしました", "Link copied to clipboard successfully": "リンクをクリップボードにコピーしました",

View File

@@ -51,11 +51,11 @@
"Bit size": "Bit size", "Bit size": "Bit size",
"Bit size - Tooltip": "Bit size - Tooltip", "Bit size - Tooltip": "Bit size - Tooltip",
"Copy private key": "Copy private key", "Copy private key": "Copy private key",
"Copy public key": "Copy public key", "Copy certificate": "Copy certificate",
"Crypto algorithm": "Crypto algorithm", "Crypto algorithm": "Crypto algorithm",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "Download private key", "Download private key": "Download private key",
"Download public key": "Download public key", "Download certificate": "Download certificate",
"Edit Cert": "Edit Cert", "Edit Cert": "Edit Cert",
"Expire in years": "Expire in years", "Expire in years": "Expire in years",
"Expire in years - Tooltip": "Expire in years - Tooltip", "Expire in years - Tooltip": "Expire in years - Tooltip",
@@ -63,9 +63,9 @@
"Private key": "Private key", "Private key": "Private key",
"Private key - Tooltip": "Private key - Tooltip", "Private key - Tooltip": "Private key - Tooltip",
"Private key copied to clipboard successfully": "Private key copied to clipboard successfully", "Private key copied to clipboard successfully": "Private key copied to clipboard successfully",
"Public key": "Public key", "Certificate": "Certificate",
"Public key - Tooltip": "Public key - Tooltip", "Certificate - Tooltip": "Certificate - Tooltip",
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully", "Certificate copied to clipboard successfully": "Certificate copied to clipboard successfully",
"Scope": "Scope", "Scope": "Scope",
"Scope - Tooltip": "Scope - Tooltip", "Scope - Tooltip": "Scope - Tooltip",
"Type": "Type", "Type": "Type",
@@ -426,7 +426,7 @@
"Host": "Host", "Host": "Host",
"Host - Tooltip": "Unique string-style identifier", "Host - Tooltip": "Unique string-style identifier",
"IdP": "IdP", "IdP": "IdP",
"IdP public key": "IdP public key", "IdP certificate": "IdP certificate",
"Issuer URL": "Issuer URL", "Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL - Tooltip", "Issuer URL - Tooltip": "Issuer URL - Tooltip",
"Link copied to clipboard successfully": "Link copied to clipboard successfully", "Link copied to clipboard successfully": "Link copied to clipboard successfully",

View File

@@ -51,11 +51,11 @@
"Bit size": "Размер бита", "Bit size": "Размер бита",
"Bit size - Tooltip": "Размер бита - Подсказка", "Bit size - Tooltip": "Размер бита - Подсказка",
"Copy private key": "Копировать закрытый ключ", "Copy private key": "Копировать закрытый ключ",
"Copy public key": "Копировать открытый ключ", "Copy certificate": "Копирование сертификата",
"Crypto algorithm": "Алгоритм крипто", "Crypto algorithm": "Алгоритм крипто",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "Скачать закрытый ключ", "Download private key": "Скачать закрытый ключ",
"Download public key": "Скачать открытый ключ", "Download certificate": "Скачать сертификат",
"Edit Cert": "Изменить сертификат", "Edit Cert": "Изменить сертификат",
"Expire in years": "Истекает через годы", "Expire in years": "Истекает через годы",
"Expire in years - Tooltip": "Истекает через годы - Подсказка", "Expire in years - Tooltip": "Истекает через годы - Подсказка",
@@ -63,9 +63,9 @@
"Private key": "Приватный ключ", "Private key": "Приватный ключ",
"Private key - Tooltip": "Приватный ключ - Подсказка", "Private key - Tooltip": "Приватный ключ - Подсказка",
"Private key copied to clipboard successfully": "Приватный ключ скопирован в буфер обмена", "Private key copied to clipboard successfully": "Приватный ключ скопирован в буфер обмена",
"Public key": "Публичный ключ", "Certificate": "сертификат",
"Public key - Tooltip": "Открытый ключ - Подсказка", "Certificate - Tooltip": "сертификат - Подсказка",
"Public key copied to clipboard successfully": "Открытый ключ успешно скопирован в буфер обмена", "Certificate copied to clipboard successfully": "Сертификат успешно скопирован в буфер обмена",
"Scope": "Сфера охвата", "Scope": "Сфера охвата",
"Scope - Tooltip": "Область применения - Подсказка", "Scope - Tooltip": "Область применения - Подсказка",
"Type": "Тип", "Type": "Тип",
@@ -426,7 +426,7 @@
"Host": "Хост", "Host": "Хост",
"Host - Tooltip": "Unique string-style identifier", "Host - Tooltip": "Unique string-style identifier",
"IdP": "ИдП", "IdP": "ИдП",
"IdP public key": "Публичный ключ IdP", "IdP certificate": "Публичный ключ IdP",
"Issuer URL": "URL эмитента", "Issuer URL": "URL эмитента",
"Issuer URL - Tooltip": "URL эмитента - Tooltip", "Issuer URL - Tooltip": "URL эмитента - Tooltip",
"Link copied to clipboard successfully": "Ссылка скопирована в буфер обмена", "Link copied to clipboard successfully": "Ссылка скопирована в буфер обмена",

View File

@@ -51,11 +51,11 @@
"Bit size": "位大小", "Bit size": "位大小",
"Bit size - Tooltip": "位大小 - 工具提示", "Bit size - Tooltip": "位大小 - 工具提示",
"Copy private key": "复制私钥", "Copy private key": "复制私钥",
"Copy public key": "复制公钥", "Copy certificate": "复制证书",
"Crypto algorithm": "加密算法", "Crypto algorithm": "加密算法",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "下载私钥", "Download private key": "下载私钥",
"Download public key": "下载公钥", "Download certificate": "下载证书",
"Edit Cert": "编辑证书", "Edit Cert": "编辑证书",
"Expire in years": "有效期(年)", "Expire in years": "有效期(年)",
"Expire in years - Tooltip": "到期年份-工具提示", "Expire in years - Tooltip": "到期年份-工具提示",
@@ -63,9 +63,9 @@
"Private key": "私钥", "Private key": "私钥",
"Private key - Tooltip": "私钥 - 工具提示", "Private key - Tooltip": "私钥 - 工具提示",
"Private key copied to clipboard successfully": "私钥已成功复制到剪贴板", "Private key copied to clipboard successfully": "私钥已成功复制到剪贴板",
"Public key": "公钥", "Certificate": "证书",
"Public key - Tooltip": "公钥 - 工具提示", "Certificate - Tooltip": "证书 - 工具提示",
"Public key copied to clipboard successfully": "公钥已成功复制到剪贴板", "Certificate copied to clipboard successfully": "证书已成功复制到剪贴板",
"Scope": "用途", "Scope": "用途",
"Scope - Tooltip": "范围 - 工具提示", "Scope - Tooltip": "范围 - 工具提示",
"Type": "类型", "Type": "类型",
@@ -426,7 +426,7 @@
"Host": "主机", "Host": "主机",
"Host - Tooltip": "主机", "Host - Tooltip": "主机",
"IdP": "IdP", "IdP": "IdP",
"IdP public key": "IdP 公钥", "IdP certificate": "IdP 公钥",
"Issuer URL": "发行者网址", "Issuer URL": "发行者网址",
"Issuer URL - Tooltip": "发行者URL - 工具提示", "Issuer URL - Tooltip": "发行者URL - 工具提示",
"Link copied to clipboard successfully": "链接已成功复制到剪贴板", "Link copied to clipboard successfully": "链接已成功复制到剪贴板",