From 5e8897e41b8dcf93ec398eeec8f28916112cd5cb Mon Sep 17 00:00:00 2001 From: Yang Luo Date: Fri, 31 Dec 2021 09:36:48 +0800 Subject: [PATCH] Make cert work. --- controllers/oidc_discovery.go | 2 +- object/application.go | 1 + object/cert.go | 18 ++++++++++++++++++ object/init.go | 36 ++++++++++++++++++++++++++++++++++- object/oidc_discovery.go | 10 ++++++---- object/token.go | 4 +++- object/token_jwt.go | 19 +++++++----------- object/token_jwt_key.go | 19 +++++------------- object/token_jwt_key_test.go | 15 +++++++++++++-- routers/auto_signin_filter.go | 6 ++++-- web/src/UserListPage.js | 2 +- web/src/locales/de/data.json | 24 +++++++++++++++++++++++ web/src/locales/en/data.json | 24 +++++++++++++++++++++++ web/src/locales/fr/data.json | 24 +++++++++++++++++++++++ web/src/locales/ja/data.json | 24 +++++++++++++++++++++++ web/src/locales/ko/data.json | 24 +++++++++++++++++++++++ web/src/locales/ru/data.json | 24 +++++++++++++++++++++++ web/src/locales/zh/data.json | 24 +++++++++++++++++++++++ 18 files changed, 262 insertions(+), 38 deletions(-) diff --git a/controllers/oidc_discovery.go b/controllers/oidc_discovery.go index 51b6b678..6160626f 100644 --- a/controllers/oidc_discovery.go +++ b/controllers/oidc_discovery.go @@ -28,7 +28,7 @@ func (c *RootController) GetOidcDiscovery() { // @Tag OIDC API // @router /api/certs [get] func (c *RootController) GetOidcCert() { - jwks, err := object.GetJSONWebKeySet() + jwks, err := object.GetJsonWebKeySet() if err != nil { c.ResponseError(err.Error()) return diff --git a/object/application.go b/object/application.go index a53f643e..a73ee31b 100644 --- a/object/application.go +++ b/object/application.go @@ -31,6 +31,7 @@ type Application struct { HomepageUrl string `xorm:"varchar(100)" json:"homepageUrl"` Description string `xorm:"varchar(100)" json:"description"` Organization string `xorm:"varchar(100)" json:"organization"` + Cert string `xorm:"varchar(100)" json:"cert"` EnablePassword bool `json:"enablePassword"` EnableSignUp bool `json:"enableSignUp"` EnableSigninSession bool `json:"enableSigninSession"` diff --git a/object/cert.go b/object/cert.go index 0f04a06d..90a72692 100644 --- a/object/cert.go +++ b/object/cert.go @@ -124,6 +124,12 @@ 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 + cert.PrivateKey = privateKey + } + affected, err := adapter.Engine.Insert(cert) if err != nil { panic(err) @@ -144,3 +150,15 @@ func DeleteCert(cert *Cert) bool { func (p *Cert) GetId() string { return fmt.Sprintf("%s/%s", p.Owner, p.Name) } + +func getCertByApplication(application *Application) *Cert { + if application.Cert != "" { + return getCert("admin", application.Cert) + } else { + return GetDefaultCert() + } +} + +func GetDefaultCert() *Cert { + return getCert("admin", "cert-built-in") +} diff --git a/object/init.go b/object/init.go index 0a00c8dc..d39a238b 100644 --- a/object/init.go +++ b/object/init.go @@ -14,12 +14,23 @@ package object -import "github.com/casbin/casdoor/util" +import ( + _ "embed" + + "github.com/casbin/casdoor/util" +) + +//go:embed token_jwt_key.pem +var tokenJwtPublicKey string + +//go:embed token_jwt_key.key +var tokenJwtPrivateKey string func InitDb() { initBuiltInOrganization() initBuiltInUser() initBuiltInApplication() + initBuiltInCert() initBuiltInLdap() } @@ -90,6 +101,7 @@ func initBuiltInApplication() { Logo: "https://cdn.casbin.com/logo/logo_1024x256.png", HomepageUrl: "https://casdoor.org", Organization: "built-in", + Cert: "cert-built-in", EnablePassword: true, EnableSignUp: true, Providers: []*ProviderItem{}, @@ -109,6 +121,28 @@ func initBuiltInApplication() { AddApplication(application) } +func initBuiltInCert() { + cert := getCert("admin", "cert-built-in") + if cert != nil { + return + } + + cert = &Cert{ + Owner: "admin", + Name: "cert-built-in", + CreatedTime: util.GetCurrentTime(), + DisplayName: "Built-in Cert", + Scope: "JWT", + Type: "x509", + CryptoAlgorithm: "RSA", + BitSize: 4096, + ExpireInYears: 20, + PublicKey: tokenJwtPublicKey, + PrivateKey: tokenJwtPrivateKey, + } + AddCert(cert) +} + func initBuiltInLdap() { ldap := GetLdap("ldap-built-in") if ldap != nil { diff --git a/object/oidc_discovery.go b/object/oidc_discovery.go index e7d247b7..6e386a9c 100644 --- a/object/oidc_discovery.go +++ b/object/oidc_discovery.go @@ -72,13 +72,15 @@ func GetOidcDiscovery() OidcDiscovery { return oidcDiscovery } -func GetJSONWebKeySet() (jose.JSONWebKeySet, error) { +func GetJsonWebKeySet() (jose.JSONWebKeySet, error) { + cert := GetDefaultCert() + //follows the protocol rfc 7517(draft) //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 - certPEMBlock := []byte(tokenJwtPublicKey) - certDERBlock, _ := pem.Decode(certPEMBlock) - x509Cert, _ := x509.ParseCertificate(certDERBlock.Bytes) + certPemBlock := []byte(cert.PublicKey) + certDerBlock, _ := pem.Decode(certPemBlock) + x509Cert, _ := x509.ParseCertificate(certDerBlock.Bytes) var jwk jose.JSONWebKey jwk.Key = x509Cert.PublicKey diff --git a/object/token.go b/object/token.go index a79a3a6d..7dfab955 100644 --- a/object/token.go +++ b/object/token.go @@ -332,7 +332,9 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId Code: "", } } - claims, err := ParseJwtToken(refreshToken) + + cert := getCertByApplication(application) + claims, err := ParseJwtToken(refreshToken, cert) if err != nil { return &Code{ Message: "error: invalid refresh_token", diff --git a/object/token_jwt.go b/object/token_jwt.go index 75a436b1..a207fc1b 100644 --- a/object/token_jwt.go +++ b/object/token_jwt.go @@ -23,12 +23,6 @@ import ( "github.com/golang-jwt/jwt/v4" ) -//go:embed token_jwt_key.pem -var tokenJwtPublicKey string - -//go:embed token_jwt_key.key -var tokenJwtPrivateKey string - type Claims struct { *User Name string `json:"name,omitempty"` @@ -68,9 +62,10 @@ func generateJwtToken(application *Application, user *User, nonce string) (strin claims.ExpiresAt = jwt.NewNumericDate(refreshExpireTime) refreshToken := jwt.NewWithClaims(jwt.SigningMethodRS256, claims) - // Use "token_jwt_key.key" as RSA private key - privateKey := tokenJwtPrivateKey - key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(privateKey)) + cert := getCertByApplication(application) + + // RSA private key + key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey)) if err != nil { return "", "", err } @@ -84,14 +79,14 @@ func generateJwtToken(application *Application, user *User, nonce string) (strin return tokenString, refreshTokenString, err } -func ParseJwtToken(token string) (*Claims, error) { +func ParseJwtToken(token string, cert *Cert) (*Claims, error) { t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) { if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok { return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) } - // Use "token_jwt_key.pem" as RSA public key - publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(tokenJwtPublicKey)) + // RSA public key + publicKey, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.PublicKey)) if err != nil { return nil, err } diff --git a/object/token_jwt_key.go b/object/token_jwt_key.go index 7ea5d447..1c5c1abf 100644 --- a/object/token_jwt_key.go +++ b/object/token_jwt_key.go @@ -20,19 +20,14 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" - "fmt" "math/big" "time" - - "github.com/casbin/casdoor/util" ) -func generateRsaKeys(fileId string) { +func generateRsaKeys(bitSize int, expireInYears int, commonName string, organization string) (string, string) { // https://stackoverflow.com/questions/64104586/use-golang-to-get-rsa-key-the-same-way-openssl-genrsa // https://stackoverflow.com/questions/43822945/golang-can-i-create-x509keypair-using-rsa-key - bitSize := 4096 - // Generate RSA key. key, err := rsa.GenerateKey(rand.Reader, bitSize) if err != nil { @@ -50,12 +45,12 @@ func generateRsaKeys(fileId string) { tml := x509.Certificate{ // you can add any attr that you need NotBefore: time.Now(), - NotAfter: time.Now().AddDate(20, 0, 0), + NotAfter: time.Now().AddDate(expireInYears, 0, 0), // you have to generate a different serial number each execution SerialNumber: big.NewInt(123456), Subject: pkix.Name{ - CommonName: "Casdoor Cert", - Organization: []string{"Casdoor Organization"}, + CommonName: commonName, + Organization: []string{organization}, }, BasicConstraintsValid: true, } @@ -70,9 +65,5 @@ func generateRsaKeys(fileId string) { Bytes: cert, }) - // Write private key to file. - util.WriteBytesToPath(privateKeyPem, fmt.Sprintf("%s.key", fileId)) - - // Write certificate (aka public key) to file. - util.WriteBytesToPath(certPem, fmt.Sprintf("%s.pem", fileId)) + return string(certPem), string(privateKeyPem) } diff --git a/object/token_jwt_key_test.go b/object/token_jwt_key_test.go index 2f593fe7..d4b8cc7e 100644 --- a/object/token_jwt_key_test.go +++ b/object/token_jwt_key_test.go @@ -14,9 +14,20 @@ package object -import "testing" +import ( + "fmt" + "testing" + + "github.com/casbin/casdoor/util" +) func TestGenerateRsaKeys(t *testing.T) { fileId := "token_jwt_key" - generateRsaKeys(fileId) + publicKey, privateKey := generateRsaKeys(4096, 20, "Casdoor Cert", "Casdoor Organization") + + // Write certificate (aka public key) to file. + util.WriteStringToPath(publicKey, fmt.Sprintf("%s.pem", fileId)) + + // Write private key to file. + util.WriteStringToPath(privateKey, fmt.Sprintf("%s.key", fileId)) } diff --git a/routers/auto_signin_filter.go b/routers/auto_signin_filter.go index c2667d64..d9a57947 100644 --- a/routers/auto_signin_filter.go +++ b/routers/auto_signin_filter.go @@ -31,7 +31,8 @@ func AutoSigninFilter(ctx *context.Context) { // "/page?access_token=123" accessToken := ctx.Input.Query("accessToken") if accessToken != "" { - claims, err := object.ParseJwtToken(accessToken) + cert := object.GetDefaultCert() + claims, err := object.ParseJwtToken(accessToken, cert) if err != nil { responseError(ctx, "invalid JWT token") return @@ -71,7 +72,8 @@ func AutoSigninFilter(ctx *context.Context) { // Authorization: Bearer bearerToken bearerToken := parseBearerToken(ctx) if bearerToken != "" { - claims, err := object.ParseJwtToken(bearerToken) + cert := object.GetDefaultCert() + claims, err := object.ParseJwtToken(bearerToken, cert) if err != nil { responseError(ctx, err.Error()) return diff --git a/web/src/UserListPage.js b/web/src/UserListPage.js index 848a5cdc..2967a08b 100644 --- a/web/src/UserListPage.js +++ b/web/src/UserListPage.js @@ -137,7 +137,7 @@ class UserListPage extends BaseListPage { title: i18next.t("general:Name"), dataIndex: 'name', key: 'name', - width: (Setting.isMobile()) ? "80px" : "100px", + width: (Setting.isMobile()) ? "80px" : "110px", fixed: 'left', sorter: true, ...this.getColumnSearchProps('name'), diff --git a/web/src/locales/de/data.json b/web/src/locales/de/data.json index c53c521f..e8ce8f91 100644 --- a/web/src/locales/de/data.json +++ b/web/src/locales/de/data.json @@ -33,6 +33,29 @@ "Token format": "Token format", "Token format - Tooltip": "Token format - Tooltip" }, + "cert": { + "Bit size": "Bit size", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "Copy private key", + "Copy public key": "Copy public key", + "Crypto algorithm": "Crypto algorithm", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "Download private key", + "Download public key": "Download public key", + "Edit Cert": "Edit Cert", + "Expire in years": "Expire in years", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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", + "Scope": "Scope", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "Code You Received", "Email code": "Email code", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "Avatar to show to others", "Back Home": "Back Home", "Captcha": "Captcha", + "Certs": "Certs", "Client IP": "Client IP", "Created time": "Created time", "Default avatar": "Default avatar", diff --git a/web/src/locales/en/data.json b/web/src/locales/en/data.json index 146dc2e7..a07dc955 100644 --- a/web/src/locales/en/data.json +++ b/web/src/locales/en/data.json @@ -33,6 +33,29 @@ "Token format": "Token format", "Token format - Tooltip": "Token format - Tooltip" }, + "cert": { + "Bit size": "Bit size", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "Copy private key", + "Copy public key": "Copy public key", + "Crypto algorithm": "Crypto algorithm", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "Download private key", + "Download public key": "Download public key", + "Edit Cert": "Edit Cert", + "Expire in years": "Expire in years", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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", + "Scope": "Scope", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "Code You Received", "Email code": "Email code", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "Avatar - Tooltip", "Back Home": "Back Home", "Captcha": "Captcha", + "Certs": "Certs", "Client IP": "Client IP", "Created time": "Created time", "Default avatar": "Default avatar", diff --git a/web/src/locales/fr/data.json b/web/src/locales/fr/data.json index c53c521f..e8ce8f91 100644 --- a/web/src/locales/fr/data.json +++ b/web/src/locales/fr/data.json @@ -33,6 +33,29 @@ "Token format": "Token format", "Token format - Tooltip": "Token format - Tooltip" }, + "cert": { + "Bit size": "Bit size", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "Copy private key", + "Copy public key": "Copy public key", + "Crypto algorithm": "Crypto algorithm", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "Download private key", + "Download public key": "Download public key", + "Edit Cert": "Edit Cert", + "Expire in years": "Expire in years", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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", + "Scope": "Scope", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "Code You Received", "Email code": "Email code", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "Avatar to show to others", "Back Home": "Back Home", "Captcha": "Captcha", + "Certs": "Certs", "Client IP": "Client IP", "Created time": "Created time", "Default avatar": "Default avatar", diff --git a/web/src/locales/ja/data.json b/web/src/locales/ja/data.json index c53c521f..e8ce8f91 100644 --- a/web/src/locales/ja/data.json +++ b/web/src/locales/ja/data.json @@ -33,6 +33,29 @@ "Token format": "Token format", "Token format - Tooltip": "Token format - Tooltip" }, + "cert": { + "Bit size": "Bit size", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "Copy private key", + "Copy public key": "Copy public key", + "Crypto algorithm": "Crypto algorithm", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "Download private key", + "Download public key": "Download public key", + "Edit Cert": "Edit Cert", + "Expire in years": "Expire in years", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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", + "Scope": "Scope", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "Code You Received", "Email code": "Email code", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "Avatar to show to others", "Back Home": "Back Home", "Captcha": "Captcha", + "Certs": "Certs", "Client IP": "Client IP", "Created time": "Created time", "Default avatar": "Default avatar", diff --git a/web/src/locales/ko/data.json b/web/src/locales/ko/data.json index c53c521f..e8ce8f91 100644 --- a/web/src/locales/ko/data.json +++ b/web/src/locales/ko/data.json @@ -33,6 +33,29 @@ "Token format": "Token format", "Token format - Tooltip": "Token format - Tooltip" }, + "cert": { + "Bit size": "Bit size", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "Copy private key", + "Copy public key": "Copy public key", + "Crypto algorithm": "Crypto algorithm", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "Download private key", + "Download public key": "Download public key", + "Edit Cert": "Edit Cert", + "Expire in years": "Expire in years", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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", + "Scope": "Scope", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "Code You Received", "Email code": "Email code", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "Avatar to show to others", "Back Home": "Back Home", "Captcha": "Captcha", + "Certs": "Certs", "Client IP": "Client IP", "Created time": "Created time", "Default avatar": "Default avatar", diff --git a/web/src/locales/ru/data.json b/web/src/locales/ru/data.json index c53c521f..e8ce8f91 100644 --- a/web/src/locales/ru/data.json +++ b/web/src/locales/ru/data.json @@ -33,6 +33,29 @@ "Token format": "Token format", "Token format - Tooltip": "Token format - Tooltip" }, + "cert": { + "Bit size": "Bit size", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "Copy private key", + "Copy public key": "Copy public key", + "Crypto algorithm": "Crypto algorithm", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "Download private key", + "Download public key": "Download public key", + "Edit Cert": "Edit Cert", + "Expire in years": "Expire in years", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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", + "Scope": "Scope", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "Type", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "Code You Received", "Email code": "Email code", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "Avatar to show to others", "Back Home": "Back Home", "Captcha": "Captcha", + "Certs": "Certs", "Client IP": "Client IP", "Created time": "Created time", "Default avatar": "Default avatar", diff --git a/web/src/locales/zh/data.json b/web/src/locales/zh/data.json index 56bdef8d..3702ce11 100644 --- a/web/src/locales/zh/data.json +++ b/web/src/locales/zh/data.json @@ -33,6 +33,29 @@ "Token format": "Access Token格式", "Token format - Tooltip": "Access Token格式" }, + "cert": { + "Bit size": "位大小", + "Bit size - Tooltip": "Bit size - Tooltip", + "Copy private key": "复制私钥", + "Copy public key": "复制公钥", + "Crypto algorithm": "加密算法", + "Crypto algorithm - Tooltip": "Crypto algorithm - Tooltip", + "Download private key": "下载私钥", + "Download public key": "下载公钥", + "Edit Cert": "修改证书", + "Expire in years": "有效期(年)", + "Expire in years - Tooltip": "Expire in years - Tooltip", + "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": "公钥已成功复制到剪贴板", + "Scope": "用途", + "Scope - Tooltip": "Scope - Tooltip", + "Type": "类型", + "Type - Tooltip": "Type - Tooltip" + }, "code": { "Code You Received": "验证码", "Email code": "邮箱验证码", @@ -69,6 +92,7 @@ "Avatar - Tooltip": "向其他人展示的头像", "Back Home": "返回到首页", "Captcha": "人机验证码", + "Certs": "证书", "Client IP": "客户端IP", "Created time": "创建时间", "Default avatar": "默认头像",