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, *, *, GET, /api/get-app-login, *, *
p, *, *, POST, /api/logout, *, *
p, *, *, GET, /api/logout, *, *
p, *, *, GET, /api/get-account, *, *
p, *, *, GET, /api/userinfo, *, *
p, *, *, *, /api/login/oauth, *, *

View File

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

View File

@@ -112,6 +112,11 @@ func (c *ApiController) GetUser() {
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.ServeJSON()
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -25,6 +25,7 @@ import (
func InitDb() {
existed := initBuiltInOrganization()
if !existed {
initBuiltInPermission()
initBuiltInProvider()
initBuiltInUser()
initBuiltInApplication()
@@ -70,6 +71,8 @@ func initBuiltInOrganization() bool {
{Name: "Bio", Visible: true, ViewRule: "Public", ModifyRule: "Self"},
{Name: "Tag", 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: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
@@ -167,7 +170,7 @@ func readTokenFromFile() (string, string) {
}
func initBuiltInCert() {
tokenJwtPublicKey, tokenJwtPrivateKey := readTokenFromFile()
tokenJwtCertificate, tokenJwtPrivateKey := readTokenFromFile()
cert := getCert("admin", "cert-built-in")
if cert != nil {
return
@@ -183,7 +186,7 @@ func initBuiltInCert() {
CryptoAlgorithm: "RS256",
BitSize: 4096,
ExpireInYears: 20,
PublicKey: tokenJwtPublicKey,
Certificate: tokenJwtCertificate,
PrivateKey: tokenJwtPrivateKey,
}
AddCert(cert)
@@ -230,3 +233,25 @@ func initBuiltInProvider() {
func initWebAuthn() {
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: "Tag", 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: "Properties", Visible: false, 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
//or https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-key
for _, cert := range certs {
certPemBlock := []byte(cert.PublicKey)
certPemBlock := []byte(cert.Certificate)
certDerBlock, _ := pem.Decode(certPemBlock)
x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes)

View File

@@ -229,3 +229,13 @@ func removePolicies(permission *Permission) {
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")
provider := getProvider(product.Owner, "provider_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()
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 {
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 {
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
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{
Space: "samlp",
Tag: "Response",
@@ -177,8 +177,8 @@ type Attribute struct {
func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, error) {
//_, originBackend := getOriginFromHost(host)
cert := getCertByApplication(application)
block, _ := pem.Decode([]byte(cert.PublicKey))
publicKey := base64.StdEncoding.EncodeToString(block.Bytes)
block, _ := pem.Decode([]byte(cert.Certificate))
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
origin := beego.AppConfig.String("origin")
originFrontend, originBackend := getOriginFromHost(host)
@@ -199,7 +199,7 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
KeyInfo: KeyInfo{
X509Data: X509Data{
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")
}
// get public key string
// get certificate string
cert := getCertByApplication(application)
block, _ := pem.Decode([]byte(cert.PublicKey))
publicKey := base64.StdEncoding.EncodeToString(block.Bytes)
block, _ := pem.Decode([]byte(cert.Certificate))
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
_, originBackend := getOriginFromHost(host)
// 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{
PrivateKey: cert.PrivateKey,
X509Certificate: publicKey,
X509Certificate: certificate,
}
ctx := dsig.NewDefaultSigningContext(randomKeyStore)
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) {
escapedPath := escapePath(fullFilePath)
escapedPath = escapePath(escapedPath)
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
host := ""
@@ -73,9 +72,9 @@ func getUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
host = fmt.Sprintf("%s/%s", host, provider.Bucket)
}
fileUrl := util.UrlJoin(host, objectKey)
fileUrl := util.UrlJoin(host, escapePath(objectKey))
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

View File

@@ -241,11 +241,11 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
samlResponse := NewSamlResponse11(user, request.RequestID, host)
cert := getCertByApplication(application)
block, _ := pem.Decode([]byte(cert.PublicKey))
publicKey := base64.StdEncoding.EncodeToString(block.Bytes)
block, _ := pem.Decode([]byte(cert.Certificate))
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
randomKeyStore := &X509Key{
PrivateKey: cert.PrivateKey,
X509Certificate: publicKey,
X509Certificate: certificate,
}
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"])
}
// RSA public key
publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.PublicKey))
// RSA certificate
certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
if err != nil {
return nil, err
}
return publicKey, nil
return certificate, nil
})
if t != nil {

View File

@@ -23,10 +23,10 @@ import (
func TestGenerateRsaKeys(t *testing.T) {
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.
util.WriteStringToPath(publicKey, fmt.Sprintf("%s.pem", fileId))
// 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))

View File

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

View File

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

View File

@@ -28,7 +28,7 @@ type AlipayPaymentProvider struct {
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{}
client, err := alipay.NewClient(appId, appPrivateKey, true)
@@ -36,7 +36,7 @@ func NewAlipayPaymentProvider(appId string, appPublicKey string, appPrivateKey s
panic(err)
}
err = client.SetCertSnByContent([]byte(appPublicKey), []byte(authorityRootPublicKey), []byte(authorityPublicKey))
err = client.SetCertSnByContent([]byte(appCertificate), []byte(authorityRootPublicKey), []byte(authorityPublicKey))
if err != nil {
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)
}
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" {
return NewAlipayPaymentProvider(appId, appPublicKey, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
return NewAlipayPaymentProvider(appId, appCertificate, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
} else if typ == "GC" {
return NewGcPaymentProvider(appId, clientSecret, host)
}

View File

@@ -48,7 +48,7 @@ func initAPI() {
beego.Router("/api/signup", &controllers.ApiController{}, "POST:Signup")
beego.Router("/api/login", &controllers.ApiController{}, "POST:Login")
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/userinfo", &controllers.ApiController{}, "GET:GetUserinfo")
beego.Router("/api/unlink", &controllers.ApiController{}, "POST:Unlink")

View File

@@ -2418,6 +2418,21 @@
}
},
"/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": {
"tags": [
"Login API"
@@ -3096,14 +3111,120 @@
],
"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": {
"2127.0xc000398090.false": {
"2127.0xc000427560.false": {
"title": "false",
"type": "object"
},
"2161.0xc0003980c0.false": {
"2161.0xc000427590.false": {
"title": "false",
"type": "object"
},
@@ -3221,10 +3342,10 @@
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/2127.0xc000398090.false"
"$ref": "#/definitions/2127.0xc000427560.false"
},
"data2": {
"$ref": "#/definitions/2161.0xc0003980c0.false"
"$ref": "#/definitions/2161.0xc000427590.false"
},
"msg": {
"type": "string"
@@ -3329,12 +3450,18 @@
"enablePassword": {
"type": "boolean"
},
"enableSamlCompress": {
"type": "boolean"
},
"enableSignUp": {
"type": "boolean"
},
"enableSigninSession": {
"type": "boolean"
},
"enableWebAuthn": {
"type": "boolean"
},
"expireInHours": {
"type": "integer",
"format": "int64"
@@ -3444,7 +3571,7 @@
"privateKey": {
"type": "string"
},
"publicKey": {
"certificate": {
"type": "string"
},
"scope": {
@@ -4507,6 +4634,12 @@
"updatedTime": {
"type": "string"
},
"webauthnCredentials": {
"type": "array",
"items": {
"$ref": "#/definitions/webauthn.Credential"
}
},
"wechat": {
"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": {
"title": "Engine",
"type": "object"

View File

@@ -1584,6 +1584,16 @@ paths:
schema:
$ref: '#/definitions/object.TokenError'
/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:
tags:
- Login API
@@ -2028,11 +2038,80 @@ paths:
tags:
- Verification API
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:
2127.0xc000398090.false:
2127.0xc000427560.false:
title: "false"
type: object
2161.0xc0003980c0.false:
2161.0xc000427590.false:
title: "false"
type: object
Response:
@@ -2113,9 +2192,9 @@ definitions:
type: object
properties:
data:
$ref: '#/definitions/2127.0xc000398090.false'
$ref: '#/definitions/2127.0xc000427560.false'
data2:
$ref: '#/definitions/2161.0xc0003980c0.false'
$ref: '#/definitions/2161.0xc000427590.false'
msg:
type: string
name:
@@ -2185,10 +2264,14 @@ definitions:
type: boolean
enablePassword:
type: boolean
enableSamlCompress:
type: boolean
enableSignUp:
type: boolean
enableSigninSession:
type: boolean
enableWebAuthn:
type: boolean
expireInHours:
type: integer
format: int64
@@ -2263,7 +2346,7 @@ definitions:
type: string
privateKey:
type: string
publicKey:
certificate:
type: string
scope:
type: string
@@ -2977,6 +3060,10 @@ definitions:
type: string
updatedTime:
type: string
webauthnCredentials:
type: array
items:
$ref: '#/definitions/webauthn.Credential'
wechat:
type: string
wecom:
@@ -3035,6 +3122,21 @@ definitions:
type: string
url:
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:
title: Engine
type: object

View File

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

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 * as Setting from "./Setting";
import i18next from "i18next";
@@ -86,6 +86,8 @@ class AccountTable extends React.Component {
{name: "Bio", displayName: i18next.t("user:Bio")},
{name: "Tag", displayName: i18next.t("user:Tag")},
{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: "Properties", displayName: i18next.t("user:Properties")},
{name: "Is admin", displayName: i18next.t("user:Is admin")},

View File

@@ -17,7 +17,7 @@ import "./App.less";
import {Helmet} from "react-helmet";
import * as Setting from "./Setting";
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 OrganizationListPage from "./OrganizationListPage";
import OrganizationEditPage from "./OrganizationEditPage";
@@ -235,6 +235,8 @@ class App extends Component {
AuthBackend.logout()
.then((res) => {
if (res.status === "ok") {
const owner = this.state.account.owner;
this.setState({
account: null
});
@@ -243,7 +245,9 @@ class App extends Component {
let redirectUri = res.data2;
if (redirectUri !== null && redirectUri !== undefined && redirectUri !== "") {
Setting.goToLink(redirectUri);
}else{
} else if (owner !== "built-in") {
Setting.goToLink(`${window.location.origin}/login/${owner}`);
} else {
Setting.goToLinkSoft(this, "/");
}
} 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/: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="/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/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} />)} />

View File

@@ -164,25 +164,25 @@ class CertEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<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 span={9} >
<Button style={{marginRight: "10px", marginBottom: "10px"}} onClick={() => {
copy(this.state.cert.publicKey);
Setting.showMessage("success", i18next.t("cert:Public key copied to clipboard successfully"));
copy(this.state.cert.certificate);
Setting.showMessage("success", i18next.t("cert:Certificate copied to clipboard successfully"));
}}
>
{i18next.t("cert:Copy public key")}
{i18next.t("cert:Copy certificate")}
</Button>
<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");
}}
>
{i18next.t("cert:Download public key")}
{i18next.t("cert:Download certificate")}
</Button>
<TextArea autoSize={{minRows: 30, maxRows: 30}} value={this.state.cert.publicKey} onChange={e => {
this.updateCertField("publicKey", e.target.value);
<TextArea autoSize={{minRows: 30, maxRows: 30}} value={this.state.cert.certificate} onChange={e => {
this.updateCertField("certificate", e.target.value);
}} />
</Col>
<Col span={1} />

View File

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

View File

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

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 LdapBackend from "./backend/LdapBackend";
import i18next from "i18next";

View File

@@ -57,6 +57,8 @@ class OrganizationListPage extends BaseListPage {
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
{name: "Tag", 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: "Properties", visible: false, 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
// 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 React from "react";
import * as UserBackend from "./backend/UserBackend";

View File

@@ -622,7 +622,7 @@ class ProviderEditPage extends React.Component {
</Row>
<Row style={{marginTop: "20px"}} >
<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 span={22} >
<Input value={this.state.provider.idP} onChange={e => {

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 * as Setting from "./Setting";
import i18next from "i18next";

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// 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 React from "react";
import * as Setting from "./Setting";

View File

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

View File

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

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 * as Setting from "./Setting";
import i18next from "i18next";

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 * as Setting from "./Setting";
import i18next from "i18next";

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 * as Setting from "./Setting";
import i18next from "i18next";

View File

@@ -427,6 +427,32 @@ class UserEditPage extends React.Component {
</Col>
</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") {
return (
!this.isSelfOrAdmin() ? null : (

View File

@@ -13,7 +13,7 @@
// limitations under the License.
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 * as Setting from "./Setting";
import i18next from "i18next";

View File

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

View File

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

View File

@@ -14,7 +14,7 @@
import React from "react";
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 AuthBackend from "./AuthBackend";
import i18next from "i18next";
@@ -79,19 +79,28 @@ class SignupPage extends React.Component {
}
UNSAFE_componentWillMount() {
if (this.state.applicationName !== undefined) {
this.getApplication();
let applicationName = this.state.applicationName;
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 {
Util.showMessage("error", `Unknown application name: ${this.state.applicationName}`);
Util.showMessage("error", `Unknown application name: ${applicationName}`);
}
}
getApplication() {
if (this.state.applicationName === undefined) {
getApplication(applicationName) {
if (applicationName === undefined) {
return;
}
ApplicationBackend.getApplication("admin", this.state.applicationName)
ApplicationBackend.getApplication("admin", applicationName)
.then((application) => {
this.setState({
application: application,

View File

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

View File

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

View File

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

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 public key",
"Copy certificate": "Copy certificate",
"Crypto algorithm": "Crypto algorithm",
"Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip",
"Download private key": "Download private key",
"Download public key": "Download public key",
"Download certificate": "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": "Public key",
"Public key - Tooltip": "Public key - Tooltip",
"Public key copied to clipboard successfully": "Public key copied to clipboard successfully",
"Certificate": "Certificate",
"Certificate - Tooltip": "Certificate - Tooltip",
"Certificate 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 public key": "IdP public key",
"IdP certificate": "IdP certificate",
"Issuer URL": "Issuer URL",
"Issuer URL - Tooltip": "Issuer URL - Tooltip",
"Link copied to clipboard successfully": "Link copied to clipboard successfully",

View File

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

View File

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

View File

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

View File

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