forked from casdoor/casdoor
feat: merge CasWAF's cert related code into Casdoor's cert code (#5171)
This commit is contained in:
107
certificate/account.go
Normal file
107
certificate/account.go
Normal file
@@ -0,0 +1,107 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
||||
"github.com/casbin/lego/v4/acme"
|
||||
"github.com/casbin/lego/v4/certcrypto"
|
||||
"github.com/casbin/lego/v4/lego"
|
||||
"github.com/casbin/lego/v4/registration"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
)
|
||||
|
||||
type Account struct {
|
||||
Email string
|
||||
Registration *registration.Resource
|
||||
Key crypto.PrivateKey
|
||||
}
|
||||
|
||||
/** Implementation of the registration.User interface **/
|
||||
|
||||
// GetEmail returns the email address for the account.
|
||||
func (a *Account) GetEmail() string {
|
||||
return a.Email
|
||||
}
|
||||
|
||||
// GetPrivateKey returns the private RSA account key.
|
||||
func (a *Account) GetPrivateKey() crypto.PrivateKey {
|
||||
return a.Key
|
||||
}
|
||||
|
||||
// GetRegistration returns the server registration.
|
||||
func (a *Account) GetRegistration() *registration.Resource {
|
||||
return a.Registration
|
||||
}
|
||||
|
||||
func getLegoClientAndAccount(email string, privateKey string, devMode bool) (*lego.Client, *Account, error) {
|
||||
key, err := decodeEccKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
account := &Account{
|
||||
Email: email,
|
||||
Key: key,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(account)
|
||||
if devMode {
|
||||
config.CADirURL = lego.LEDirectoryStaging
|
||||
} else {
|
||||
config.CADirURL = lego.LEDirectoryProduction
|
||||
}
|
||||
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
config.HTTPClient = proxy.ProxyHttpClient
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client, account, err
|
||||
}
|
||||
|
||||
// GetAcmeClient Incoming an email ,a privatekey and a Boolean value that controls the opening of the test environment
|
||||
// When this function is started for the first time, it will initialize the account-related configuration,
|
||||
// After initializing the configuration, It will try to obtain an account based on the private key,
|
||||
// if it fails, it will create an account based on the private key.
|
||||
// This account will be used during the running of the program
|
||||
func GetAcmeClient(email string, privateKey string, devMode bool) (*lego.Client, error) {
|
||||
// Create a user. New accounts need an email and private key to start.
|
||||
client, account, err := getLegoClientAndAccount(email, privateKey, devMode)
|
||||
|
||||
// try to obtain an account based on the private key
|
||||
account.Registration, err = client.Registration.ResolveAccountByKey()
|
||||
if err != nil {
|
||||
acmeError, ok := err.(*acme.ProblemDetails)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if acmeError.Type != "urn:ietf:params:acme:error:accountDoesNotExist" {
|
||||
return nil, acmeError
|
||||
}
|
||||
|
||||
// Failed to get account, so create an account based on the private key.
|
||||
account.Registration, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
47
certificate/account_test.go
Normal file
47
certificate/account_test.go
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/beego/beego/v2/server/web"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetClient(t *testing.T) {
|
||||
err := web.LoadAppConfig("ini", "../conf/app.conf")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
proxy.InitHttpClient()
|
||||
|
||||
eccKey := util.ReadStringFromPath("acme_account.key")
|
||||
println(eccKey)
|
||||
|
||||
client, err := GetAcmeClient("acme2@casbin.org", eccKey, false)
|
||||
assert.Nil(t, err)
|
||||
pem, key, err := ObtainCertificateAli(client, "casbin.com", accessKeyId, accessKeySecret)
|
||||
assert.Nil(t, err)
|
||||
println(pem)
|
||||
println()
|
||||
println(key)
|
||||
}
|
||||
20
certificate/conf.go
Normal file
20
certificate/conf.go
Normal file
@@ -0,0 +1,20 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
var (
|
||||
accessKeyId = ""
|
||||
accessKeySecret = ""
|
||||
)
|
||||
151
certificate/dns.go
Normal file
151
certificate/dns.go
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/casbin/lego/v4/certificate"
|
||||
"github.com/casbin/lego/v4/challenge/dns01"
|
||||
"github.com/casbin/lego/v4/cmd"
|
||||
"github.com/casbin/lego/v4/lego"
|
||||
"github.com/casbin/lego/v4/providers/dns/alidns"
|
||||
"github.com/casbin/lego/v4/providers/dns/godaddy"
|
||||
)
|
||||
|
||||
type AliConf struct {
|
||||
Domains []string // The domain names for which you want to apply for a certificate
|
||||
AccessKey string // Aliyun account's AccessKey, if this is not empty, Secret is required.
|
||||
Secret string
|
||||
RAMRole string // Use Ramrole to control aliyun account
|
||||
SecurityToken string // Optional
|
||||
Path string // The path to store cert file
|
||||
Timeout int // Maximum waiting time for certificate application, in minutes
|
||||
}
|
||||
|
||||
type GodaddyConf struct {
|
||||
Domains []string // The domain names for which you want to apply for a certificate
|
||||
APIKey string // GoDaddy account's API Key
|
||||
APISecret string
|
||||
Path string // The path to store cert file
|
||||
Timeout int // Maximum waiting time for certificate application, in minutes
|
||||
}
|
||||
|
||||
// getCert Verify domain ownership, then obtain a certificate, and finally store it locally.
|
||||
// Need to pass in an AliConf struct, some parameters are required, other parameters can be left blank
|
||||
func getAliCert(client *lego.Client, conf AliConf) (string, string, error) {
|
||||
if conf.Timeout <= 0 {
|
||||
conf.Timeout = 3
|
||||
}
|
||||
|
||||
config := alidns.NewDefaultConfig()
|
||||
config.PropagationTimeout = time.Duration(conf.Timeout) * time.Minute
|
||||
config.APIKey = conf.AccessKey
|
||||
config.SecretKey = conf.Secret
|
||||
config.RAMRole = conf.RAMRole
|
||||
config.SecurityToken = conf.SecurityToken
|
||||
|
||||
dnsProvider, err := alidns.NewDNSProvider(config)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Choose a local DNS service provider to increase the authentication speed
|
||||
servers := []string{"223.5.5.5:53"}
|
||||
err = client.Challenge.SetDNS01Provider(dnsProvider, dns01.CondOption(len(servers) > 0, dns01.AddRecursiveNameservers(dns01.ParseNameservers(servers))), dns01.DisableCompletePropagationRequirement())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Obtain the certificate
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: conf.Domains,
|
||||
Bundle: true,
|
||||
}
|
||||
cert, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return string(cert.Certificate), string(cert.PrivateKey), nil
|
||||
}
|
||||
|
||||
func getGoDaddyCert(client *lego.Client, conf GodaddyConf) (string, string, error) {
|
||||
if conf.Timeout <= 0 {
|
||||
conf.Timeout = 3
|
||||
}
|
||||
|
||||
config := godaddy.NewDefaultConfig()
|
||||
config.PropagationTimeout = time.Duration(conf.Timeout) * time.Minute
|
||||
config.PollingInterval = time.Duration(conf.Timeout) * time.Minute / 9
|
||||
config.APIKey = conf.APIKey
|
||||
config.APISecret = conf.APISecret
|
||||
|
||||
dnsProvider, err := godaddy.NewDNSProvider(config)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Choose a local DNS service provider to increase the authentication speed
|
||||
servers := []string{"223.5.5.5:53"}
|
||||
err = client.Challenge.SetDNS01Provider(dnsProvider, dns01.CondOption(len(servers) > 0, dns01.AddRecursiveNameservers(dns01.ParseNameservers(servers))), dns01.DisableCompletePropagationRequirement())
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
// Obtain the certificate
|
||||
request := certificate.ObtainRequest{
|
||||
Domains: conf.Domains,
|
||||
Bundle: true,
|
||||
}
|
||||
cert, err := client.Certificate.Obtain(request)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
return string(cert.Certificate), string(cert.PrivateKey), nil
|
||||
}
|
||||
|
||||
func ObtainCertificateAli(client *lego.Client, domain string, accessKey string, accessSecret string) (string, string, error) {
|
||||
conf := AliConf{
|
||||
Domains: []string{fmt.Sprintf("*.%s", domain), domain},
|
||||
AccessKey: accessKey,
|
||||
Secret: accessSecret,
|
||||
RAMRole: "",
|
||||
SecurityToken: "",
|
||||
Path: "",
|
||||
Timeout: 3,
|
||||
}
|
||||
return getAliCert(client, conf)
|
||||
}
|
||||
|
||||
func ObtainCertificateGoDaddy(client *lego.Client, domain string, accessKey string, accessSecret string) (string, string, error) {
|
||||
conf := GodaddyConf{
|
||||
Domains: []string{fmt.Sprintf("*.%s", domain), domain},
|
||||
APIKey: accessKey,
|
||||
APISecret: accessSecret,
|
||||
Path: "",
|
||||
Timeout: 3,
|
||||
}
|
||||
return getGoDaddyCert(client, conf)
|
||||
}
|
||||
|
||||
func SaveCert(path, filename string, cert *certificate.Resource) {
|
||||
// Store the certificate file locally
|
||||
certsStorage := cmd.NewCertificatesStorageLib(path, filename, true)
|
||||
certsStorage.CreateRootFolder()
|
||||
certsStorage.SaveResource(cert)
|
||||
}
|
||||
55
certificate/ecc.go
Normal file
55
certificate/ecc.go
Normal file
@@ -0,0 +1,55 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// generateEccKey generates a public and private key pair.(NIST P-256)
|
||||
func generateEccKey() (*ecdsa.PrivateKey, error) {
|
||||
return ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
}
|
||||
|
||||
// encodeEccKey Return the input private key object as string type private key
|
||||
func encodeEccKey(privateKey *ecdsa.PrivateKey) (string, error) {
|
||||
x509Encoded, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: x509Encoded})
|
||||
return string(pemEncoded), nil
|
||||
}
|
||||
|
||||
// decodeEccKey Return the entered private key string as a private key object that can be used
|
||||
func decodeEccKey(pemEncoded string) (*ecdsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(pemEncoded))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("invalid PEM-encoded EC private key")
|
||||
}
|
||||
x509Encoded := block.Bytes
|
||||
privateKey, err := x509.ParseECPrivateKey(x509Encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
34
certificate/ecc_test.go
Normal file
34
certificate/ecc_test.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2021 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build !skipCi
|
||||
// +build !skipCi
|
||||
|
||||
package certificate
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGenerateEccKey(t *testing.T) {
|
||||
eccKey, err := generateEccKey()
|
||||
assert.Nil(t, err)
|
||||
eccKeyStr, err := encodeEccKey(eccKey)
|
||||
assert.Nil(t, err)
|
||||
println(eccKeyStr)
|
||||
util.WriteStringToPath(eccKeyStr, "acme_account.key")
|
||||
}
|
||||
@@ -183,3 +183,40 @@ func (c *ApiController) DeleteCert() {
|
||||
c.Data["json"] = wrapActionResponse(object.DeleteCert(&cert))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
// UpdateCertDomainExpire
|
||||
// @Title UpdateCertDomainExpire
|
||||
// @Tag Cert API
|
||||
// @Description update cert domain expire time
|
||||
// @Param id query string true "The ID of the cert"
|
||||
// @Success 200 {object} controllers.Response The Response object
|
||||
// @router /update-cert-domain-expire [post]
|
||||
func (c *ApiController) UpdateCertDomainExpire() {
|
||||
if _, ok := c.RequireSignedIn(); !ok {
|
||||
return
|
||||
}
|
||||
|
||||
id := c.Ctx.Input.Query("id")
|
||||
cert, err := object.GetCert(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
domainExpireTime, err := object.GetDomainExpireTime(cert.Name)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if domainExpireTime == "" {
|
||||
c.ResponseError("Failed to determine domain expiration time for domain " + cert.Name +
|
||||
". Please verify that the domain is valid, publicly resolvable, and has a retrievable expiration date, " +
|
||||
"or update the domain expiration time manually.")
|
||||
return
|
||||
}
|
||||
cert.DomainExpireTime = domainExpireTime
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.UpdateCert(id, cert))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
||||
11
go.mod
11
go.mod
@@ -22,6 +22,7 @@ require (
|
||||
github.com/beego/beego/v2 v2.3.8
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin/v2 v2.77.2
|
||||
github.com/casbin/lego/v4 v4.5.4
|
||||
github.com/casdoor/go-sms-sender v0.25.0
|
||||
github.com/casdoor/gomail/v2 v2.2.0
|
||||
github.com/casdoor/ldapserver v1.2.0
|
||||
@@ -47,6 +48,8 @@ require (
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/lestrrat-go/jwx v1.2.29
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/likexian/whois v1.15.1
|
||||
github.com/likexian/whois-parser v1.24.9
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3
|
||||
github.com/markbates/goth v1.82.0
|
||||
github.com/microsoft/go-mssqldb v1.9.0
|
||||
@@ -129,6 +132,7 @@ require (
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
|
||||
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
@@ -188,6 +192,7 @@ require (
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||
github.com/likexian/gokit v0.25.13 // indirect
|
||||
github.com/line/line-bot-sdk-go v7.8.0+incompatible // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/markbates/going v1.0.0 // indirect
|
||||
@@ -195,6 +200,7 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-ieproxy v0.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/miekg/dns v1.1.43 // indirect
|
||||
github.com/mileusna/viber v1.0.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
@@ -211,14 +217,17 @@ require (
|
||||
github.com/prometheus/common v0.48.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/qiniu/go-sdk/v7 v7.12.1 // indirect
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 // indirect
|
||||
github.com/redis/go-redis/v9 v9.5.5 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
|
||||
github.com/rs/zerolog v1.33.0 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.0.1 // indirect
|
||||
github.com/scim2/filter-parser/v2 v2.2.0 // indirect
|
||||
github.com/sendgrid/rest v2.6.9+incompatible // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
@@ -238,6 +247,7 @@ require (
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/twilio/twilio-go v1.13.0 // indirect
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.5 // indirect
|
||||
github.com/urfave/cli v1.22.5 // indirect
|
||||
github.com/utahta/go-linenotify v0.5.0 // indirect
|
||||
github.com/volcengine/volc-sdk-golang v1.0.117 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
@@ -272,6 +282,7 @@ require (
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
lukechampine.com/uint128 v1.2.0 // indirect
|
||||
|
||||
28
go.sum
28
go.sum
@@ -778,6 +778,7 @@ github.com/alibabacloud-go/tea-xml v1.1.1/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCE
|
||||
github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
|
||||
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1183/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107 h1:qagvUyrgOnBIlVRQWOyCZGVKUIYbMBdGdJ104vBpRFU=
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.63.107/go.mod h1:SOSDHfe1kX91v3W5QiBsWSLqeLxImobbMX1mxrFHsVQ=
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
|
||||
@@ -850,6 +851,8 @@ github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRt
|
||||
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
|
||||
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
|
||||
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
|
||||
github.com/casbin/lego/v4 v4.5.4 h1:WdVEj1A5KmKZheNuFNLF/5+UUkpXLt9mEOrLX3E81Vo=
|
||||
github.com/casbin/lego/v4 v4.5.4/go.mod h1:JjTyJgN5pyrDPcg3+aAM1NtFQIXl8zDgsoSS1TnVpJ8=
|
||||
github.com/casdoor/casdoor-go-sdk v0.50.0 h1:bUYbz/MzJuWfLKJbJM0+U0YpYewAur+THp5TKnufWZM=
|
||||
github.com/casdoor/casdoor-go-sdk v0.50.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
|
||||
github.com/casdoor/go-sms-sender v0.25.0 h1:eF4cOCSbjVg7+0uLlJQnna/FQ0BWW+Fp/x4cXhzQu1Y=
|
||||
@@ -911,6 +914,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb h1:7X9nrm+LNWdxzQOiCjy0G51rNUxbH35IDHCjAMvogyM=
|
||||
github.com/cschomburg/go-pushbullet v0.0.0-20171206132031-67759df45fbb/go.mod h1:RfQ9wji3fjcSEsQ+uFCtIh3+BXgcZum8Kt3JxvzYzlk=
|
||||
@@ -1308,6 +1313,7 @@ github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aW
|
||||
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
|
||||
github.com/jinzhu/configor v1.2.1 h1:OKk9dsR8i6HPOCZR8BcMtcEImAFjIhbJFZNyn5GCZko=
|
||||
github.com/jinzhu/configor v1.2.1/go.mod h1:nX89/MOmDba7ZX7GCyU/VIaQ2Ar2aizBl2d3JLF/rDc=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
@@ -1321,7 +1327,9 @@ github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUB
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA=
|
||||
github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -1390,6 +1398,12 @@ github.com/lib/pq v1.7.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/likexian/gokit v0.25.13 h1:p2Uw3+6fGG53CwdU2Dz0T6bOycdb2+bAFAa3ymwWVkM=
|
||||
github.com/likexian/gokit v0.25.13/go.mod h1:qQhEWFBEfqLCO3/vOEo2EDKd+EycekVtUK4tex+l2H4=
|
||||
github.com/likexian/whois v1.15.1 h1:6vTMI8n9s1eJdmcO4R9h1x99aQWIZZX1CD3am68gApU=
|
||||
github.com/likexian/whois v1.15.1/go.mod h1:/nxmQ6YXvLz+qTxC/QFtEJNAt0zLuRxJrKiWpBJX8X0=
|
||||
github.com/likexian/whois-parser v1.24.9 h1:BT6fzO3lj3F07yzVv0YXoaj+K4Ush0/cF+Yp6tvJJgk=
|
||||
github.com/likexian/whois-parser v1.24.9/go.mod h1:b6STMHHDaSKbd4PzGrP50wWE5NzeBUETa/hT9gI0G9I=
|
||||
github.com/line/line-bot-sdk-go v7.8.0+incompatible h1:Uf9/OxV0zCVfqyvwZPH8CrdiHXXmMRa/L91G3btQblQ=
|
||||
github.com/line/line-bot-sdk-go v7.8.0+incompatible/go.mod h1:0RjLjJEAU/3GIcHkC3av6O4jInAbt25nnZVmOFUgDBg=
|
||||
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275 h1:IZycmTpoUtQK3PD60UYBwjaCUHUP7cML494ao9/O8+Q=
|
||||
@@ -1434,6 +1448,7 @@ github.com/microsoft/go-mssqldb v1.9.0 h1:5Vq+u2f4LDujJNeZn62Z4kBDEC9MjLv0ukRzOu
|
||||
github.com/microsoft/go-mssqldb v1.9.0/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg=
|
||||
github.com/miekg/dns v1.1.43/go.mod h1:+evo5L0630/F6ca/Z9+GAqzhjGyn8/c+TBaOyfEl0V4=
|
||||
github.com/mileusna/viber v1.0.1 h1:gWB6/lKoWYVxkH0Jb8jRnGIRZ/9DEM7RBZRJHRfdYWs=
|
||||
github.com/mileusna/viber v1.0.1/go.mod h1:Pxu/iPMnYjnHgu+bEp3SiKWHWmlf/kDp/yOX8XUdYrQ=
|
||||
@@ -1572,6 +1587,8 @@ github.com/qiniu/dyn v1.3.0/go.mod h1:E8oERcm8TtwJiZvkQPbcAh0RL8jO1G0VXJMW3FAWdk
|
||||
github.com/qiniu/go-sdk/v7 v7.12.1 h1:FZG5dhs2MZBV/mHVhmHnsgsQ+j1gSE0RqIoA2WwEDwY=
|
||||
github.com/qiniu/go-sdk/v7 v7.12.1/go.mod h1:btsaOc8CA3hdVloULfFdDgDc+g4f3TDZEFsDY0BLE+w=
|
||||
github.com/qiniu/x v1.10.5/go.mod h1:03Ni9tj+N2h2aKnAz+6N0Xfl8FwMEDRC2PAlxekASDs=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2 h1:dq90+d51/hQRaHEqRAsQ1rE/pC1GUS4sc2rCbbFsAIY=
|
||||
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.5.5 h1:51VEyMF8eOO+NUHFm8fpg+IOc1xFuFOhxs3R+kPu1FM=
|
||||
@@ -1595,6 +1612,7 @@ github.com/russellhaering/gosaml2 v0.9.0 h1:CNMnH42z/GirrKjdmNrSS6bAAs47F9bPdl4P
|
||||
github.com/russellhaering/gosaml2 v0.9.0/go.mod h1:byViER/1YPUa0Puj9ROZblpoq2jsE7h/CJmitzX0geU=
|
||||
github.com/russellhaering/goxmldsig v1.2.0 h1:Y6GTTc9Un5hCxSzVz4UIWQ/zuVwDvzJk80guqzwx6Vg=
|
||||
github.com/russellhaering/goxmldsig v1.2.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
|
||||
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
|
||||
@@ -1616,6 +1634,7 @@ github.com/shirou/gopsutil/v4 v4.25.9 h1:JImNpf6gCVhKgZhtaAHJ0serfFGtlfIlSC08eaK
|
||||
github.com/shirou/gopsutil/v4 v4.25.9/go.mod h1:gxIxoC+7nQRwUl/xNhutXlD8lq+jxTgpIkEf3rADHL8=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
@@ -1636,7 +1655,9 @@ github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQ
|
||||
github.com/slack-go/slack v0.15.0 h1:LE2lj2y9vqqiOf+qIIy0GvEoxgF1N5yLGZffmEZykt0=
|
||||
github.com/slack-go/slack v0.15.0/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
|
||||
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
|
||||
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
|
||||
@@ -1653,6 +1674,7 @@ github.com/streadway/handy v0.0.0-20200128134331-0f66f006fb2e/go.mod h1:qNTQ5P5J
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.3.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
@@ -1712,6 +1734,8 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK
|
||||
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.5 h1:GIltVwMDUqQj4iPL/emsZAMhEYWjLTwZqpOxdkdDrM8=
|
||||
github.com/ucloud/ucloud-sdk-go v0.22.5/go.mod h1:dyLmFHmUfgb4RZKYQP9IArlvQ2pxzFthfhwxRzOEPIw=
|
||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/utahta/go-linenotify v0.5.0 h1:E1tJaB/XhqRY/iz203FD0MaHm10DjQPOq5/Mem2A3Gs=
|
||||
github.com/utahta/go-linenotify v0.5.0/go.mod h1:KsvBXil2wx+ByaCR0e+IZKTbp4pDesc7yjzRigLf6pE=
|
||||
@@ -2632,13 +2656,17 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.66.6/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
|
||||
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
|
||||
@@ -17,6 +17,7 @@ package object
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/certificate"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
)
|
||||
@@ -33,6 +34,13 @@ type Cert struct {
|
||||
BitSize int `json:"bitSize"`
|
||||
ExpireInYears int `json:"expireInYears"`
|
||||
|
||||
ExpireTime string `xorm:"varchar(100)" json:"expireTime"`
|
||||
DomainExpireTime string `xorm:"varchar(100)" json:"domainExpireTime"`
|
||||
Provider string `xorm:"varchar(100)" json:"provider"`
|
||||
Account string `xorm:"varchar(100)" json:"account"`
|
||||
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
|
||||
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
|
||||
|
||||
Certificate string `xorm:"mediumtext" json:"certificate"`
|
||||
PrivateKey string `xorm:"mediumtext" json:"privateKey"`
|
||||
}
|
||||
@@ -224,6 +232,20 @@ func (p *Cert) populateContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if p.Type == "SSL" {
|
||||
if p.Certificate != "" {
|
||||
expireTime, err := util.GetCertExpireTime(p.Certificate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ExpireTime = expireTime
|
||||
} else {
|
||||
p.ExpireTime = ""
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if len(p.CryptoAlgorithm) < 3 {
|
||||
err := fmt.Errorf("populateContent() error, unsupported crypto algorithm: %s", p.CryptoAlgorithm)
|
||||
return err
|
||||
@@ -258,6 +280,42 @@ func (p *Cert) populateContent() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func RenewCert(cert *Cert) (bool, error) {
|
||||
useProxy := false
|
||||
if cert.Provider == "GoDaddy" {
|
||||
useProxy = true
|
||||
}
|
||||
|
||||
client, err := GetAcmeClient(useProxy)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
var certStr, privateKey string
|
||||
if cert.Provider == "Aliyun" {
|
||||
certStr, privateKey, err = certificate.ObtainCertificateAli(client, cert.Name, cert.AccessKey, cert.AccessSecret)
|
||||
} else if cert.Provider == "GoDaddy" {
|
||||
certStr, privateKey, err = certificate.ObtainCertificateGoDaddy(client, cert.Name, cert.AccessKey, cert.AccessSecret)
|
||||
} else {
|
||||
return false, fmt.Errorf("unknown provider: %s", cert.Provider)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
expireTime, err := util.GetCertExpireTime(certStr)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
cert.ExpireTime = expireTime
|
||||
cert.Certificate = certStr
|
||||
cert.PrivateKey = privateKey
|
||||
|
||||
return UpdateCert(cert.GetId(), cert)
|
||||
}
|
||||
|
||||
func getCertByApplication(application *Application) (*Cert, error) {
|
||||
if application.Cert != "" {
|
||||
return getCertByName(application.Cert)
|
||||
|
||||
75
object/cert_whois.go
Normal file
75
object/cert_whois.go
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/likexian/whois"
|
||||
whoisparser "github.com/likexian/whois-parser"
|
||||
)
|
||||
|
||||
func getDomainExpireTime(domainName string) (string, error) {
|
||||
domainName, err := util.GetBaseDomain(domainName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
server := ""
|
||||
if strings.HasSuffix(domainName, ".com") || strings.HasSuffix(domainName, ".net") {
|
||||
server = "whois.verisign-grs.com"
|
||||
} else if strings.HasSuffix(domainName, ".org") {
|
||||
server = "whois.pir.org"
|
||||
} else if strings.HasSuffix(domainName, ".io") {
|
||||
server = "whois.nic.io"
|
||||
} else if strings.HasSuffix(domainName, ".co") {
|
||||
server = "whois.nic.co"
|
||||
} else if strings.HasSuffix(domainName, ".cn") {
|
||||
server = "whois.cnnic.cn"
|
||||
} else if strings.HasSuffix(domainName, ".run") {
|
||||
server = "whois.nic.run"
|
||||
} else {
|
||||
server = "grs-whois.hichina.com" // com, net, cc, tv
|
||||
}
|
||||
|
||||
client := whois.NewClient()
|
||||
//if server != "whois.cnnic.cn" && server != "grs-whois.hichina.com" {
|
||||
// dialer := proxy.GetProxyDialer()
|
||||
// if dialer != nil {
|
||||
// client.SetDialer(dialer)
|
||||
// }
|
||||
//}
|
||||
|
||||
data, err := client.Whois(domainName, server)
|
||||
if err != nil {
|
||||
if !strings.HasSuffix(domainName, ".run") || data == "" {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
whoisInfo, err := whoisparser.Parse(data)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
res := whoisInfo.Domain.ExpirationDateInTime.Local().Format(time.RFC3339)
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func GetDomainExpireTime(domainName string) (string, error) {
|
||||
return getDomainExpireTime(domainName)
|
||||
}
|
||||
103
object/site_cert_account.go
Normal file
103
object/site_cert_account.go
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/lego/v4/acme"
|
||||
"github.com/casbin/lego/v4/certcrypto"
|
||||
"github.com/casbin/lego/v4/lego"
|
||||
"github.com/casbin/lego/v4/registration"
|
||||
"github.com/casdoor/casdoor/certificate"
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
)
|
||||
|
||||
func getLegoClientAndAccount(email string, privateKey string, devMode bool, useProxy bool) (*lego.Client, *certificate.Account, error) {
|
||||
eccKey, err := decodeEccKey(privateKey)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
account := &certificate.Account{
|
||||
Email: email,
|
||||
Key: eccKey,
|
||||
}
|
||||
|
||||
config := lego.NewConfig(account)
|
||||
if devMode {
|
||||
config.CADirURL = lego.LEDirectoryStaging
|
||||
} else {
|
||||
config.CADirURL = lego.LEDirectoryProduction
|
||||
}
|
||||
|
||||
config.Certificate.KeyType = certcrypto.RSA2048
|
||||
|
||||
if useProxy {
|
||||
config.HTTPClient = proxy.ProxyHttpClient
|
||||
} else {
|
||||
config.HTTPClient = proxy.DefaultHttpClient
|
||||
}
|
||||
|
||||
client, err := lego.NewClient(config)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return client, account, nil
|
||||
}
|
||||
|
||||
func getAcmeClient(email string, privateKey string, devMode bool, useProxy bool) (*lego.Client, error) {
|
||||
// Create a user. New accounts need an email and private key to start.
|
||||
client, account, err := getLegoClientAndAccount(email, privateKey, devMode, useProxy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// try to obtain an account based on the private key
|
||||
account.Registration, err = client.Registration.ResolveAccountByKey()
|
||||
if err != nil {
|
||||
acmeError, ok := err.(*acme.ProblemDetails)
|
||||
if !ok {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if acmeError.Type != "urn:ietf:params:acme:error:accountDoesNotExist" {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Failed to get account, so create an account based on the private key.
|
||||
account.Registration, err = client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func GetAcmeClient(useProxy bool) (*lego.Client, error) {
|
||||
acmeEmail := conf.GetConfigString("acmeEmail")
|
||||
acmePrivateKey := conf.GetConfigString("acmePrivateKey")
|
||||
if acmeEmail == "" {
|
||||
return nil, fmt.Errorf("acmeEmail should not be empty")
|
||||
}
|
||||
if acmePrivateKey == "" {
|
||||
return nil, fmt.Errorf("acmePrivateKey should not be empty")
|
||||
}
|
||||
|
||||
return getAcmeClient(acmeEmail, acmePrivateKey, false, useProxy)
|
||||
}
|
||||
59
object/site_cert_ecc.go
Normal file
59
object/site_cert_ecc.go
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2023 The casbin Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package object
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/elliptic"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// generateEccKey generates a public and private key pair.(NIST P-256)
|
||||
func generateEccKey() *ecdsa.PrivateKey {
|
||||
privateKey, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
|
||||
return privateKey
|
||||
}
|
||||
|
||||
// encodeEccKey Return the input private key object as string type private key
|
||||
func encodeEccKey(privateKey *ecdsa.PrivateKey) string {
|
||||
x509Encoded, err := x509.MarshalECPrivateKey(privateKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
pemEncoded := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: x509Encoded})
|
||||
return string(pemEncoded)
|
||||
}
|
||||
|
||||
// decodeEccKey Return the entered private key string as a private key object that can be used
|
||||
func decodeEccKey(pemEncoded string) (*ecdsa.PrivateKey, error) {
|
||||
pemEncoded = strings.ReplaceAll(pemEncoded, "\\n", "\n")
|
||||
block, _ := pem.Decode([]byte(pemEncoded))
|
||||
if block == nil {
|
||||
return nil, fmt.Errorf("decodeEccKey() error, block should not be nil")
|
||||
}
|
||||
|
||||
x509Encoded := block.Bytes
|
||||
privateKey, err := x509.ParseECPrivateKey(x509Encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return privateKey, nil
|
||||
}
|
||||
@@ -132,6 +132,7 @@ func InitAPI() {
|
||||
web.Router("/api/update-cert", &controllers.ApiController{}, "POST:UpdateCert")
|
||||
web.Router("/api/add-cert", &controllers.ApiController{}, "POST:AddCert")
|
||||
web.Router("/api/delete-cert", &controllers.ApiController{}, "POST:DeleteCert")
|
||||
web.Router("/api/update-cert-domain-expire", &controllers.ApiController{}, "POST:UpdateCertDomainExpire")
|
||||
|
||||
web.Router("/api/get-roles", &controllers.ApiController{}, "GET:GetRoles")
|
||||
web.Router("/api/get-role", &controllers.ApiController{}, "GET:GetRole")
|
||||
|
||||
50
util/cert.go
Normal file
50
util/cert.go
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2026 The Casdoor Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"golang.org/x/net/publicsuffix"
|
||||
)
|
||||
|
||||
func GetCertExpireTime(s string) (string, error) {
|
||||
block, _ := pem.Decode([]byte(s))
|
||||
if block == nil {
|
||||
return "", errors.New("getCertExpireTime() error, block should not be nil")
|
||||
} else if block.Type != "CERTIFICATE" {
|
||||
return "", fmt.Errorf("getCertExpireTime() error, block.Type should be \"CERTIFICATE\" instead of %s", block.Type)
|
||||
}
|
||||
|
||||
certificate, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
t := certificate.NotAfter
|
||||
return t.Local().Format(time.RFC3339), nil
|
||||
}
|
||||
|
||||
func GetBaseDomain(domain string) (string, error) {
|
||||
// abc.com -> abc.com
|
||||
// abc.com.it -> abc.com.it
|
||||
// subdomain.abc.io -> abc.io
|
||||
// subdomain.abc.org.us -> abc.org.us
|
||||
return publicsuffix.EffectiveTLDPlusOne(domain)
|
||||
}
|
||||
@@ -16,8 +16,17 @@ package util
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func GetHostname() string {
|
||||
name, err := os.Hostname()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
func IsInternetIp(ip string) bool {
|
||||
ipStr, _, err := net.SplitHostPort(ip)
|
||||
if err != nil {
|
||||
|
||||
@@ -81,6 +81,24 @@ class CertEditPage extends React.Component {
|
||||
value = this.parseCertField(key, value);
|
||||
|
||||
const cert = this.state.cert;
|
||||
const previousType = cert.type;
|
||||
if (key === "type") {
|
||||
if (value === "SSL") {
|
||||
cert.cryptoAlgorithm = "RSA";
|
||||
cert.certificate = "";
|
||||
cert.privateKey = "";
|
||||
} else if (previousType === "SSL" && value !== "SSL") {
|
||||
// Clear SSL-specific sensitive and derived fields when leaving SSL type
|
||||
cert.provider = "";
|
||||
cert.account = "";
|
||||
cert.accessKey = "";
|
||||
cert.accessSecret = "";
|
||||
cert.certificate = "";
|
||||
cert.privateKey = "";
|
||||
cert.expireTime = "";
|
||||
cert.domainExpireTime = "";
|
||||
}
|
||||
}
|
||||
cert[key] = value;
|
||||
this.setState({
|
||||
cert: cert,
|
||||
@@ -157,6 +175,7 @@ class CertEditPage extends React.Component {
|
||||
})}>
|
||||
{
|
||||
[
|
||||
{id: "SSL", name: "SSL"},
|
||||
{id: "x509", name: "x509"},
|
||||
{id: "Payment", name: "Payment"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
@@ -184,7 +203,10 @@ class CertEditPage extends React.Component {
|
||||
this.updateCertField("privateKey", "");
|
||||
})}>
|
||||
{
|
||||
[
|
||||
(this.state.cert.type === "SSL" ? [
|
||||
{id: "RSA", name: "RSA"},
|
||||
{id: "ECC", name: "ECC"},
|
||||
] : [
|
||||
{id: "RS256", name: "RS256 (RSA + SHA256)"},
|
||||
{id: "RS384", name: "RS384 (RSA + SHA384)"},
|
||||
{id: "RS512", name: "RS512 (RSA + SHA512)"},
|
||||
@@ -194,13 +216,13 @@ class CertEditPage extends React.Component {
|
||||
{id: "PS256", name: "PS256 (RSASSA-PSS using SHA256 and MGF1 with SHA256)"},
|
||||
{id: "PS384", name: "PS384 (RSASSA-PSS using SHA384 and MGF1 with SHA384)"},
|
||||
{id: "PS512", name: "PS512 (RSASSA-PSS using SHA512 and MGF1 with SHA512)"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
]).map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
this.state.cert.cryptoAlgorithm.startsWith("ES") ? null : (
|
||||
this.state.cert.cryptoAlgorithm.startsWith("ES") || this.state.cert.type === "SSL" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("cert:Bit size"), i18next.t("cert:Bit size - Tooltip"))} :
|
||||
@@ -219,16 +241,91 @@ class CertEditPage extends React.Component {
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("cert:Expire in years"), i18next.t("cert:Expire in years - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber value={this.state.cert.expireInYears} onChange={value => {
|
||||
this.updateCertField("expireInYears", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{
|
||||
this.state.cert.type === "SSL" ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("cert:Expire in years"), i18next.t("cert:Expire in years - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<InputNumber value={this.state.cert.expireInYears} onChange={value => {
|
||||
this.updateCertField("expireInYears", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
{
|
||||
this.state.cert.type === "SSL" ? (
|
||||
<React.Fragment>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{i18next.t("cert:Expire time")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={Setting.getFormattedDate(this.state.cert.expireTime)} onChange={e => {
|
||||
this.updateCertField("expireTime", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{i18next.t("cert:Domain expire")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={Setting.getFormattedDate(this.state.cert.domainExpireTime)} onChange={e => {
|
||||
this.updateCertField("domainExpireTime", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{i18next.t("cert:Provider")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.cert.provider} onChange={(value => {this.updateCertField("provider", value);})}>
|
||||
{
|
||||
[
|
||||
{id: "GoDaddy", name: "GoDaddy"},
|
||||
{id: "Aliyun", name: "Aliyun"},
|
||||
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
|
||||
}
|
||||
</Select>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{i18next.t("cert:Account")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.cert.account} onChange={e => {
|
||||
this.updateCertField("account", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{i18next.t("cert:Access key")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.cert.accessKey} onChange={e => {
|
||||
this.updateCertField("accessKey", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{i18next.t("cert:Access secret")}:
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input.Password value={this.state.cert.accessSecret} onChange={e => {
|
||||
this.updateCertField("accessSecret", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
</React.Fragment>
|
||||
) : null
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("cert:Certificate"), i18next.t("cert:Certificate - Tooltip"))} :
|
||||
|
||||
@@ -88,6 +88,28 @@ class CertListPage extends BaseListPage {
|
||||
});
|
||||
}
|
||||
|
||||
refreshCert(i) {
|
||||
const cert = this.state.data[i];
|
||||
CertBackend.refreshDomainExpire(cert.owner, cert.name)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", `Failed to refresh domain expire: ${res.msg}`);
|
||||
} else {
|
||||
Setting.showMessage("success", "Domain expire refreshed successfully");
|
||||
this.fetch({
|
||||
pagination: {
|
||||
...this.state.pagination,
|
||||
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
)
|
||||
.catch(error => {
|
||||
Setting.showMessage("error", `Domain expire failed to refresh: ${error}`);
|
||||
});
|
||||
}
|
||||
|
||||
renderTable(certs) {
|
||||
const columns = [
|
||||
{
|
||||
@@ -194,6 +216,12 @@ class CertListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
record.type === "SSL" ? (
|
||||
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{margin: "10px 10px 10px 0"}} type="default" onClick={() => this.refreshCert(index)}>{i18next.t("general:Refresh")}
|
||||
</Button>
|
||||
) : null
|
||||
}
|
||||
<Button disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)} style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/certs/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||
<PopconfirmModal
|
||||
disabled={!Setting.isAdminUser(this.props.account) && (record.owner !== this.props.account.owner)}
|
||||
|
||||
@@ -79,3 +79,10 @@ export function deleteCert(cert) {
|
||||
},
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function refreshDomainExpire(owner, name) {
|
||||
return fetch(`${Setting.ServerUrl}/api/update-cert-domain-expire?id=${owner}/${encodeURIComponent(name)}`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user