mirror of
https://github.com/outbackdingo/labca.git
synced 2026-01-27 18:19:33 +00:00
Merge branch 'release/20.10'
* release/20.10: Cosmetic fix to orders list Bump boulder version to release-2020-10-13 Set high rate limit for our domain; add rate-limits page Update jQuery and other vendor js Update acme pages to show requested/issued domain name
This commit is contained in:
@@ -33,7 +33,7 @@ index 5f93fe866..b4a0b75e0 100644
|
||||
+ max-file: "5"
|
||||
+ restart: always
|
||||
bmysql:
|
||||
image: mariadb:10.3
|
||||
image: mariadb:10.5
|
||||
+ volumes:
|
||||
+ - dbdata:/var/lib/mysql
|
||||
networks:
|
||||
|
||||
117
gui/acme.go
117
gui/acme.go
@@ -6,6 +6,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// BaseList is the generic base struct for showing lists of data
|
||||
@@ -73,10 +74,11 @@ func GetAccounts(w http.ResponseWriter, r *http.Request) (AccountList, error) {
|
||||
type Order struct {
|
||||
ID int
|
||||
RegistrationID int
|
||||
Expires string
|
||||
CertSerial string
|
||||
RequestedName string
|
||||
BeganProc bool
|
||||
Created string
|
||||
Expires string
|
||||
}
|
||||
|
||||
// OrderList is a list of Order records
|
||||
@@ -117,7 +119,7 @@ func GetAccount(w http.ResponseWriter, r *http.Request, id int) (AccountShow, er
|
||||
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id WHERE registrationID=?", strconv.Itoa(id))
|
||||
rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, n.reversedName, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id JOIN issuedNames n ON n.serial = c.serial WHERE registrationID=?", strconv.Itoa(id))
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AccountShow{}, err
|
||||
@@ -127,22 +129,23 @@ func GetAccount(w http.ResponseWriter, r *http.Request, id int) (AccountShow, er
|
||||
BaseList: BaseList{
|
||||
Title: "Certificates",
|
||||
TableClass: "rel_certificates_list",
|
||||
Header: []template.HTML{"ID", "Account ID", "Serial", "Status", "Issued", "Expires"},
|
||||
Header: []template.HTML{"ID", "Account ID", "Serial", "Issued Name", "Status", "Issued", "Expires"},
|
||||
},
|
||||
Rows: []Certificate{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
row := Certificate{}
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Serial, &row.Status, &row.Issued, &row.Expires)
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Serial, &row.IssuedName, &row.Status, &row.Issued, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AccountShow{}, err
|
||||
}
|
||||
row.IssuedName = ReverseName(row.IssuedName)
|
||||
Certificates.Rows = append(Certificates.Rows, row)
|
||||
}
|
||||
|
||||
rows, err = db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders WHERE registrationID=?", strconv.Itoa(id))
|
||||
rows, err = db.Query("SELECT o.id, o.registrationID, o.certificateSerial, n.reversedName, o.beganProcessing, o.created, o.expires FROM orders o JOIN requestedNames n ON n.orderID = o.id WHERE o.registrationID=?", strconv.Itoa(id))
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AccountShow{}, err
|
||||
@@ -152,18 +155,19 @@ func GetAccount(w http.ResponseWriter, r *http.Request, id int) (AccountShow, er
|
||||
BaseList: BaseList{
|
||||
Title: "Orders",
|
||||
TableClass: "rel_orders_list",
|
||||
Header: []template.HTML{"ID", "Account ID", "Expires", "Certificate Serial", "Began Processing?", "Created"},
|
||||
Header: []template.HTML{"ID", "Account ID", "Certificate Serial", "Requested Name", "Began Processing?", "Created", "Expires", },
|
||||
},
|
||||
Rows: []Order{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
row := Order{}
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created)
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.CertSerial, &row.RequestedName, &row.BeganProc, &row.Created, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AccountShow{}, err
|
||||
}
|
||||
row.RequestedName = ReverseName(row.RequestedName)
|
||||
Orders.Rows = append(Orders.Rows, row)
|
||||
}
|
||||
|
||||
@@ -211,7 +215,7 @@ func GetOrders(w http.ResponseWriter, r *http.Request) (OrderList, error) {
|
||||
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders")
|
||||
rows, err := db.Query("SELECT o.id, o.registrationID, o.certificateSerial, n.reversedName, o.beganProcessing, o.created, o.expires FROM orders o JOIN requestedNames n ON n.orderID = o.id")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return OrderList{}, err
|
||||
@@ -221,18 +225,19 @@ func GetOrders(w http.ResponseWriter, r *http.Request) (OrderList, error) {
|
||||
BaseList: BaseList{
|
||||
Title: "Orders",
|
||||
TableClass: "orders_list",
|
||||
Header: []template.HTML{"ID", "Account ID", "Expires", "Certificate Serial", "Began Processing?", "Created"},
|
||||
Header: []template.HTML{"ID", "Account ID", "Certificate Serial", "Requested Name", "Began Processing?", "Created", "Expires"},
|
||||
},
|
||||
Rows: []Order{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
row := Order{}
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created)
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.CertSerial, &row.RequestedName, &row.BeganProc, &row.Created, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return OrderList{}, err
|
||||
}
|
||||
row.RequestedName = ReverseName(row.RequestedName)
|
||||
Orders.Rows = append(Orders.Rows, row)
|
||||
}
|
||||
|
||||
@@ -241,12 +246,11 @@ func GetOrders(w http.ResponseWriter, r *http.Request) (OrderList, error) {
|
||||
|
||||
// Auth contains the data representing an ACME auth
|
||||
type Auth struct {
|
||||
ID string
|
||||
Identifier string
|
||||
RegistrationID int
|
||||
Status string
|
||||
Expires string
|
||||
Combinations string
|
||||
ID string
|
||||
Identifier string
|
||||
RegistrationID int
|
||||
Status string
|
||||
Expires string
|
||||
}
|
||||
|
||||
// AuthList is a list of Auth records
|
||||
@@ -262,6 +266,15 @@ type OrderShow struct {
|
||||
Related2 []BaseList
|
||||
}
|
||||
|
||||
// Helper method from sa/model.go
|
||||
var uintToStatus = map[int]string{
|
||||
0: "pending",
|
||||
1: "valid",
|
||||
2: "invalid",
|
||||
3: "deactivated",
|
||||
4: "revoked",
|
||||
}
|
||||
|
||||
// GetOrder returns an order with the given id
|
||||
func GetOrder(w http.ResponseWriter, r *http.Request, id int) (OrderShow, error) {
|
||||
db, err := sql.Open(dbType, dbConn)
|
||||
@@ -272,9 +285,11 @@ func GetOrder(w http.ResponseWriter, r *http.Request, id int) (OrderShow, error)
|
||||
|
||||
defer db.Close()
|
||||
|
||||
partial := "SELECT id, identifier, registrationID, status, expires, combinations FROM "
|
||||
partial := "SELECT id, identifier, registrationID, status, expires FROM "
|
||||
where := " WHERE id IN (SELECT authzID FROM orderToAuthz WHERE orderID=?)"
|
||||
rows, err := db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where, strconv.Itoa(id), strconv.Itoa(id))
|
||||
partial2 := "SELECT id, identifierValue, registrationID, status, expires FROM "
|
||||
where2 := " WHERE id IN (SELECT authzID FROM orderToAuthz2 WHERE orderID=?)"
|
||||
rows, err := db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where+" UNION "+partial2+"authz2"+where2, strconv.Itoa(id), strconv.Itoa(id), strconv.Itoa(id))
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return OrderShow{}, err
|
||||
@@ -284,22 +299,25 @@ func GetOrder(w http.ResponseWriter, r *http.Request, id int) (OrderShow, error)
|
||||
BaseList: BaseList{
|
||||
Title: "Authorizations",
|
||||
TableClass: "rel_authz_list",
|
||||
Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires", "Combinations"},
|
||||
Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires"},
|
||||
},
|
||||
Rows: []Auth{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
row := Auth{}
|
||||
err = rows.Scan(&row.ID, &row.Identifier, &row.RegistrationID, &row.Status, &row.Expires, &row.Combinations)
|
||||
err = rows.Scan(&row.ID, &row.Identifier, &row.RegistrationID, &row.Status, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return OrderShow{}, err
|
||||
}
|
||||
if s, err := strconv.Atoi(row.Status); err == nil {
|
||||
row.Status = uintToStatus[s]
|
||||
}
|
||||
Authz.Rows = append(Authz.Rows, row)
|
||||
}
|
||||
|
||||
rows, err = db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders WHERE id=?", strconv.Itoa(id))
|
||||
rows, err = db.Query("SELECT o.id, o.registrationID, o.certificateSerial, n.reversedName, o.beganProcessing, o.created, o.expires FROM orders o JOIN requestedNames n ON n.orderID = o.id WHERE o.id=?", strconv.Itoa(id))
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return OrderShow{}, err
|
||||
@@ -317,21 +335,22 @@ func GetOrder(w http.ResponseWriter, r *http.Request, id int) (OrderShow, error)
|
||||
|
||||
for rows.Next() {
|
||||
row := Order{}
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created)
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.CertSerial, &row.RequestedName, &row.BeganProc, &row.Created, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return OrderShow{}, err
|
||||
}
|
||||
OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"ID", strconv.Itoa(row.ID)})
|
||||
OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Expires", row.Expires})
|
||||
v := "false"
|
||||
if row.BeganProc {
|
||||
v = "true"
|
||||
}
|
||||
OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Began Processing?", v})
|
||||
OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Created", row.Created})
|
||||
OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Expires", row.Expires})
|
||||
|
||||
OrderDetails.Links = append(OrderDetails.Links, NameValHTML{"Certificate", template.HTML("<a href=\"" + r.Header.Get("X-Request-Base") + "/certificates/" + row.CertSerial + "\">" + row.CertSerial + "</a>")})
|
||||
OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Requested Name", ReverseName(row.RequestedName)})
|
||||
OrderDetails.Links = append(OrderDetails.Links, NameValHTML{"Account", template.HTML("<a href=\"" + r.Header.Get("X-Request-Base") + "/accounts/" + strconv.Itoa(row.RegistrationID) + "\">" + strconv.Itoa(row.RegistrationID) + "</a>")})
|
||||
}
|
||||
|
||||
@@ -348,7 +367,7 @@ func GetAuthz(w http.ResponseWriter, r *http.Request) (AuthList, error) {
|
||||
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query("SELECT id, identifier, registrationID, status, expires, combinations FROM authz UNION SELECT id, identifier, registrationID, status, expires, combinations FROM pendingAuthorizations")
|
||||
rows, err := db.Query("SELECT id, identifier, registrationID, status, expires FROM authz UNION SELECT id, identifier, registrationID, status, expires FROM pendingAuthorizations UNION SELECT id, identifierValue, registrationID, status, expires FROM authz2")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AuthList{}, err
|
||||
@@ -358,18 +377,21 @@ func GetAuthz(w http.ResponseWriter, r *http.Request) (AuthList, error) {
|
||||
BaseList: BaseList{
|
||||
Title: "Authorizations",
|
||||
TableClass: "authz_list",
|
||||
Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires", "Combinations"},
|
||||
Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires"},
|
||||
},
|
||||
Rows: []Auth{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
row := Auth{}
|
||||
err = rows.Scan(&row.ID, &row.Identifier, &row.RegistrationID, &row.Status, &row.Expires, &row.Combinations)
|
||||
err = rows.Scan(&row.ID, &row.Identifier, &row.RegistrationID, &row.Status, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AuthList{}, err
|
||||
}
|
||||
if s, err := strconv.Atoi(row.Status); err == nil {
|
||||
row.Status = uintToStatus[s]
|
||||
}
|
||||
Authz.Rows = append(Authz.Rows, row)
|
||||
}
|
||||
|
||||
@@ -441,9 +463,11 @@ func GetAuth(w http.ResponseWriter, r *http.Request, id string) (AuthShow, error
|
||||
Challenges.Rows = append(Challenges.Rows, row)
|
||||
}
|
||||
|
||||
partial := "SELECT id, identifier, registrationID, status, expires, combinations FROM "
|
||||
partial := "SELECT id, identifier, registrationID, status, expires, '', '' FROM "
|
||||
where := " WHERE id IN (SELECT authzID FROM orderToAuthz WHERE id=?)"
|
||||
rows, err = db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where, id, id)
|
||||
partial2 := "SELECT id, identifierValue, registrationID, status, expires, validationError, validationRecord FROM "
|
||||
where2 := " WHERE id IN (SELECT authzID FROM orderToAuthz2 WHERE id=?)"
|
||||
rows, err = db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where+" UNION "+partial2+"authz2"+where2, id, id, id)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AuthShow{}, err
|
||||
@@ -461,16 +485,26 @@ func GetAuth(w http.ResponseWriter, r *http.Request, id string) (AuthShow, error
|
||||
|
||||
for rows.Next() {
|
||||
row := Auth{}
|
||||
err = rows.Scan(&row.ID, &row.Identifier, &row.RegistrationID, &row.Status, &row.Expires, &row.Combinations)
|
||||
validationError := sql.NullString{}
|
||||
validationRecord := sql.NullString{}
|
||||
err = rows.Scan(&row.ID, &row.Identifier, &row.RegistrationID, &row.Status, &row.Expires, &validationError, &validationRecord)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return AuthShow{}, err
|
||||
}
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"ID", row.ID})
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Identifier", row.Identifier})
|
||||
if s, err := strconv.Atoi(row.Status); err == nil {
|
||||
row.Status = uintToStatus[s]
|
||||
}
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Status", row.Status})
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Expires", row.Expires})
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Combinations", row.Combinations})
|
||||
if validationError.Valid && validationError.String != "" {
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Validation Error", validationError.String})
|
||||
}
|
||||
if validationRecord.Valid && validationRecord.String != "" {
|
||||
AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Validation Record", validationRecord.String})
|
||||
}
|
||||
|
||||
Link := NameValHTML{"Account", template.HTML("<a href=\"" + r.Header.Get("X-Request-Base") + "/accounts/" + strconv.Itoa(row.RegistrationID) + "\">" + strconv.Itoa(row.RegistrationID) + "</a>")}
|
||||
AuthDetails.Links = append(AuthDetails.Links, Link)
|
||||
@@ -576,6 +610,7 @@ type Certificate struct {
|
||||
ID int
|
||||
RegistrationID int
|
||||
Serial string
|
||||
IssuedName string
|
||||
Status string
|
||||
Issued string
|
||||
Expires string
|
||||
@@ -587,6 +622,15 @@ type CertificateList struct {
|
||||
Rows []Certificate
|
||||
}
|
||||
|
||||
// domains are stored in reverse order...
|
||||
func ReverseName(domain string) string {
|
||||
labels := strings.Split(domain, ".")
|
||||
for i, j := 0, len(labels)-1; i < j; i, j = i+1, j-1 {
|
||||
labels[i], labels[j] = labels[j], labels[i]
|
||||
}
|
||||
return strings.Join(labels, ".")
|
||||
}
|
||||
|
||||
// GetCertificates returns the list of certificates
|
||||
func GetCertificates(w http.ResponseWriter, r *http.Request) (CertificateList, error) {
|
||||
db, err := sql.Open(dbType, dbConn)
|
||||
@@ -606,7 +650,7 @@ func GetCertificates(w http.ResponseWriter, r *http.Request) (CertificateList, e
|
||||
where = " WHERE cs.revokedDate<>'0000-00-00 00:00:00'"
|
||||
}
|
||||
|
||||
rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id" + where)
|
||||
rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, n.reversedName, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id JOIN issuedNames n ON n.serial = c.serial" + where)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return CertificateList{}, err
|
||||
@@ -616,18 +660,19 @@ func GetCertificates(w http.ResponseWriter, r *http.Request) (CertificateList, e
|
||||
BaseList: BaseList{
|
||||
Title: "Certificates",
|
||||
TableClass: "certificates_list",
|
||||
Header: []template.HTML{"ID", "Account ID", "Serial", "Status", "Issued", "Expires"},
|
||||
Header: []template.HTML{"ID", "Account ID", "Serial", "Issued Name", "Status", "Issued", "Expires"},
|
||||
},
|
||||
Rows: []Certificate{},
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
row := Certificate{}
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Serial, &row.Status, &row.Issued, &row.Expires)
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Serial, &row.IssuedName, &row.Status, &row.Issued, &row.Expires)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return CertificateList{}, err
|
||||
}
|
||||
row.IssuedName = ReverseName(row.IssuedName)
|
||||
Certificates.Rows = append(Certificates.Rows, row)
|
||||
}
|
||||
|
||||
@@ -646,6 +691,7 @@ type CertificateExtra struct {
|
||||
ID int
|
||||
RegistrationID int
|
||||
Serial string
|
||||
IssuedName string
|
||||
Digest string
|
||||
Issued string
|
||||
Expires string
|
||||
@@ -700,7 +746,7 @@ func GetCertificate(w http.ResponseWriter, r *http.Request, id int, serial strin
|
||||
defer db.Close()
|
||||
|
||||
var rows *sql.Rows
|
||||
selectWhere := "SELECT c.id, c.registrationID, c.serial, c.digest, c.issued, c.expires, cs.subscriberApproved, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, cs.ocspLastUpdated, cs.revokedDate, cs.revokedReason, cs.lastExpirationNagSent, cs.notAfter, cs.isExpired FROM certificates c JOIN certificateStatus cs ON cs.id = c.id WHERE "
|
||||
selectWhere := "SELECT c.id, c.registrationID, c.serial, n.reversedName, c.digest, c.issued, c.expires, cs.subscriberApproved, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, cs.ocspLastUpdated, cs.revokedDate, cs.revokedReason, cs.lastExpirationNagSent, cs.notAfter, cs.isExpired FROM certificates c JOIN certificateStatus cs ON cs.id = c.id JOIN issuedNames n ON n.serial = c.serial WHERE "
|
||||
|
||||
if serial != "" {
|
||||
rows, err = db.Query(selectWhere+"c.serial=?", serial)
|
||||
@@ -723,13 +769,14 @@ func GetCertificate(w http.ResponseWriter, r *http.Request, id int, serial strin
|
||||
|
||||
for rows.Next() {
|
||||
row := CertificateExtra{}
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Serial, &row.Digest, &row.Issued, &row.Expires, &row.SubscriberApproved, &row.Status, &row.OCSPLastUpdate, &row.Revoked, &row.RevokedReason, &row.LastNagSent, &row.NotAfter, &row.IsExpired)
|
||||
err = rows.Scan(&row.ID, &row.RegistrationID, &row.Serial, &row.IssuedName, &row.Digest, &row.Issued, &row.Expires, &row.SubscriberApproved, &row.Status, &row.OCSPLastUpdate, &row.Revoked, &row.RevokedReason, &row.LastNagSent, &row.NotAfter, &row.IsExpired)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return CertificateShow{}, err
|
||||
}
|
||||
CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"ID", strconv.Itoa(row.ID)})
|
||||
CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Serial", row.Serial})
|
||||
CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Issued Name", ReverseName(row.IssuedName)})
|
||||
CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Digest", row.Digest})
|
||||
CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Issued", row.Issued})
|
||||
CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Expires", row.Expires})
|
||||
|
||||
@@ -27,6 +27,9 @@ fi
|
||||
if [ "$PKI_DOMAIN_MODE" == "lockdown" ] || [ "$PKI_DOMAIN_MODE" == "whitelist" ]; then
|
||||
sed -i -e "s/^\(.*\)\(\"n_subject_common_name_included\"\)/\1\2,\n\1\"e_dnsname_not_valid_tld\"/" config/ca-a.json
|
||||
sed -i -e "s/^\(.*\)\(\"n_subject_common_name_included\"\)/\1\2,\n\1\"e_dnsname_not_valid_tld\"/" config/ca-b.json
|
||||
|
||||
sed -i -e "s/\( registrationOverrides:\)/ $PKI_LOCKDOWN_DOMAINS: 10000\n\1/" rate-limit-policies.yml
|
||||
echo " $PKI_LOCKDOWN_DOMAINS: 10000" >> rate-limit-policies.yml
|
||||
fi
|
||||
|
||||
if [ "$PKI_EXTENDED_TIMEOUT" == "1" ]; then
|
||||
@@ -85,4 +88,4 @@ cp -p $PKI_ROOT_CERT_BASE.pem test-root.pem
|
||||
openssl rsa -in $PKI_ROOT_CERT_BASE.key -pubout > test-root.pubkey.pem
|
||||
openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in test-root.key -out test-root.p8
|
||||
|
||||
chown -R `ls -l rate-limit-policies.yml | cut -d" " -f 3,4 | sed 's/ /:/g'` .
|
||||
chown -R `ls -l PKI.md | cut -d" " -f 3,4 | sed 's/ /:/g'` .
|
||||
|
||||
@@ -2255,7 +2255,7 @@ func activeNav(active string, uri string, requestBase string) []navItem {
|
||||
Name: "Public Area",
|
||||
Icon: "fa-home",
|
||||
Attrs: map[template.HTMLAttr]string{
|
||||
"href": "/",
|
||||
"href": "http://" + viper.GetString("labca.fqdn"),
|
||||
"title": "The non-Admin pages of this LabCA instance",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<p>Also if you are developing your own client application or integrating one into your own application, a local test ACME can be very handy. There is a lot of information on the internet about setting up your own PKI (Public Key Infrastructure) but those are usually not automated.</p>
|
||||
|
||||
<p>Getting Boulder up and running has quite a learning curve though and that is where <b>LabCA</b> comes in. It is a self-contained installation with a nice web GUI built on top of Boulder so you can quickly start using it. All regular management tasks can be done from the web interface. It is best installed in a Virtual Machine and uses Debian Linux as a base.</p>
|
||||
<p>Getting Boulder up and running has quite a learning curve though and that is where <b><a href="https://lab-ca.net/">LabCA</a></b> comes in. It is a self-contained installation with a nice web GUI built on top of Boulder so you can quickly start using it. All regular management tasks can be done from the web interface. It is best installed in a Virtual Machine and uses Debian Linux as a base.</p>
|
||||
|
||||
<p>NOTE: although LabCA tries to be as robust as possible, use it at your own risk. If you depend on it, make sure that you know what you are doing!</p>
|
||||
{{ end }}
|
||||
|
||||
4
install
4
install
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# LabCA: a private Certificate Authority for internal lab usage
|
||||
# (c) 2018 Arjan Hakkesteegt
|
||||
# (c) 2018-2020 Arjan Hakkesteegt
|
||||
#
|
||||
# Install with this command from a Linux machine (only tested with Debian 9):
|
||||
# curl -sSL https://raw.githubusercontent.com/hakwerk/labca/master/install | bash
|
||||
@@ -24,7 +24,7 @@ dockerComposeVersion="1.22.0"
|
||||
|
||||
labcaUrl="https://github.com/hakwerk/labca/"
|
||||
boulderUrl="https://github.com/letsencrypt/boulder/"
|
||||
boulderTag="release-2020-09-09"
|
||||
boulderTag="release-2020-10-13"
|
||||
|
||||
#
|
||||
# Color configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/mail/mailer.go b/mail/mailer.go
|
||||
index de6b1de20..60c58128b 100644
|
||||
index bb5bacaf2..946992dca 100644
|
||||
--- a/mail/mailer.go
|
||||
+++ b/mail/mailer.go
|
||||
@@ -20,10 +20,14 @@ import (
|
||||
@@ -17,7 +17,7 @@ index de6b1de20..60c58128b 100644
|
||||
)
|
||||
|
||||
type idGenerator interface {
|
||||
@@ -119,6 +123,7 @@ func New(
|
||||
@@ -121,6 +125,7 @@ func New(
|
||||
username,
|
||||
password string,
|
||||
rootCAs *x509.CertPool,
|
||||
@@ -25,7 +25,7 @@ index de6b1de20..60c58128b 100644
|
||||
from mail.Address,
|
||||
logger blog.Logger,
|
||||
stats prometheus.Registerer,
|
||||
@@ -138,6 +143,7 @@ func New(
|
||||
@@ -140,6 +145,7 @@ func New(
|
||||
server: server,
|
||||
port: port,
|
||||
rootCAs: rootCAs,
|
||||
@@ -33,7 +33,7 @@ index de6b1de20..60c58128b 100644
|
||||
},
|
||||
log: logger,
|
||||
from: from,
|
||||
@@ -178,7 +184,7 @@ func (m *MailerImpl) generateMessage(to []string, subject, body string) ([]byte,
|
||||
@@ -180,7 +186,7 @@ func (m *MailerImpl) generateMessage(to []string, subject, body string) ([]byte,
|
||||
fmt.Sprintf("To: %s", strings.Join(addrs, ", ")),
|
||||
fmt.Sprintf("From: %s", m.from.String()),
|
||||
fmt.Sprintf("Subject: %s", subject),
|
||||
@@ -42,7 +42,7 @@ index de6b1de20..60c58128b 100644
|
||||
fmt.Sprintf("Message-Id: <%s.%s.%s>", now.Format("20060102T150405"), mid.String(), m.from.Address),
|
||||
"MIME-Version: 1.0",
|
||||
"Content-Type: text/plain; charset=UTF-8",
|
||||
@@ -235,23 +241,32 @@ func (m *MailerImpl) Connect() error {
|
||||
@@ -237,23 +243,32 @@ func (m *MailerImpl) Connect() error {
|
||||
type dialerImpl struct {
|
||||
username, password, server, port string
|
||||
rootCAs *x509.CertPool
|
||||
|
||||
@@ -23,6 +23,10 @@ server {
|
||||
proxy_pass http://127.0.0.1:4002/;
|
||||
}
|
||||
|
||||
location /rate-limits {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
|
||||
location /terms/ {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
@@ -77,6 +81,10 @@ server {
|
||||
proxy_pass http://127.0.0.1:4002/;
|
||||
}
|
||||
|
||||
location /rate-limits {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
|
||||
location /terms/ {
|
||||
try_files $uri $uri.html $uri/ =404;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/cmd/notify-mailer/main.go b/cmd/notify-mailer/main.go
|
||||
index 0445a04c0..ba2be9e2f 100644
|
||||
index e00541cb1..39af62530 100644
|
||||
--- a/cmd/notify-mailer/main.go
|
||||
+++ b/cmd/notify-mailer/main.go
|
||||
@@ -37,6 +37,7 @@ type mailer struct {
|
||||
@@ -38,6 +38,7 @@ type mailer struct {
|
||||
destinations []recipient
|
||||
targetRange interval
|
||||
sleepInterval time.Duration
|
||||
@@ -10,7 +10,7 @@ index 0445a04c0..ba2be9e2f 100644
|
||||
}
|
||||
|
||||
// interval defines a range of email addresses to send to, alphabetically.
|
||||
@@ -146,7 +147,7 @@ func (m *mailer) run() error {
|
||||
@@ -147,7 +148,7 @@ func (m *mailer) run() error {
|
||||
m.log.Debugf("skipping %q: out of target range")
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
diff --git a/ra/ra.go b/ra/ra.go
|
||||
index a92965189..aeccb9c3c 100644
|
||||
index ca21ace0e..6d90d7eff 100644
|
||||
--- a/ra/ra.go
|
||||
+++ b/ra/ra.go
|
||||
@@ -28,7 +28,6 @@ import (
|
||||
"github.com/letsencrypt/boulder/identifier"
|
||||
@@ -29,7 +29,6 @@ import (
|
||||
"github.com/letsencrypt/boulder/issuance"
|
||||
blog "github.com/letsencrypt/boulder/log"
|
||||
"github.com/letsencrypt/boulder/metrics"
|
||||
- "github.com/letsencrypt/boulder/policy"
|
||||
"github.com/letsencrypt/boulder/probs"
|
||||
rapb "github.com/letsencrypt/boulder/ra/proto"
|
||||
"github.com/letsencrypt/boulder/ratelimit"
|
||||
@@ -399,7 +398,7 @@ func (ra *RegistrationAuthorityImpl) validateContacts(ctx context.Context, conta
|
||||
@@ -400,7 +399,7 @@ func (ra *RegistrationAuthorityImpl) validateContacts(ctx context.Context, conta
|
||||
contact,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,14 +4,14 @@ index be064a52e..e7ef8fcf6 100644
|
||||
+++ b/test/config/ca-a.json
|
||||
@@ -30,11 +30,7 @@
|
||||
},
|
||||
"Issuers": [{
|
||||
"ConfigFile": "test/test-ca.key-pkcs11.json",
|
||||
- "CertFile": "/tmp/intermediate-cert-rsa-a.pem",
|
||||
- "NumSessions": 2
|
||||
"issuers": [{
|
||||
"configFile": "test/test-ca.key-pkcs11.json",
|
||||
- "certFile": "/tmp/intermediate-cert-rsa-a.pem",
|
||||
- "numSessions": 2
|
||||
- },{
|
||||
- "ConfigFile": "test/test-ca.key-pkcs11.json",
|
||||
- "CertFile": "/tmp/intermediate-cert-rsa-b.pem",
|
||||
+ "CertFile": "test/test-ca.pem",
|
||||
"NumSessions": 2
|
||||
- "configFile": "test/test-ca.key-pkcs11.json",
|
||||
- "certFile": "/tmp/intermediate-cert-rsa-b.pem",
|
||||
+ "certFile": "test/test-ca.pem",
|
||||
"numSessions": 2
|
||||
}],
|
||||
"expiry": "2160h",
|
||||
|
||||
@@ -4,14 +4,14 @@ index ed2498f1a..4d24ffa94 100644
|
||||
+++ b/test/config/ca-b.json
|
||||
@@ -30,11 +30,7 @@
|
||||
},
|
||||
"Issuers": [{
|
||||
"ConfigFile": "test/test-ca.key-pkcs11.json",
|
||||
- "CertFile": "/tmp/intermediate-cert-rsa-a.pem",
|
||||
- "NumSessions": 2
|
||||
"issuers": [{
|
||||
"configFile": "test/test-ca.key-pkcs11.json",
|
||||
- "certFile": "/tmp/intermediate-cert-rsa-a.pem",
|
||||
- "numSessions": 2
|
||||
- },{
|
||||
- "ConfigFile": "test/test-ca.key-pkcs11.json",
|
||||
- "CertFile": "/tmp/intermediate-cert-rsa-b.pem",
|
||||
+ "CertFile": "test/test-ca.pem",
|
||||
"NumSessions": 2
|
||||
- "configFile": "test/test-ca.key-pkcs11.json",
|
||||
- "certFile": "/tmp/intermediate-cert-rsa-b.pem",
|
||||
+ "certFile": "test/test-ca.pem",
|
||||
"numSessions": 2
|
||||
}],
|
||||
"expiry": "2160h",
|
||||
|
||||
@@ -118,7 +118,7 @@
|
||||
<small></small>
|
||||
</div>
|
||||
<div class="col-sm-6 footer text-muted text-right" id="footer">
|
||||
<small>Copyright © 2018 LabCA</small>
|
||||
<small>Copyright © 2018-2020 LabCA</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -151,3 +151,10 @@ a.update:hover {
|
||||
.rel-notes-title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
pre.json {
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
18
www/js/dataTables.bootstrap.min.js
vendored
18
www/js/dataTables.bootstrap.min.js
vendored
@@ -1,4 +1,14 @@
|
||||
/*! DataTables Bootstrap 3 integration
|
||||
* ©2011-2015 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
(function(a){if(typeof define==="function"&&define.amd){define(["jquery","datatables.net"],function(b){return a(b,window,document)})}else{if(typeof exports==="object"){module.exports=function(b,c){if(!b){b=window}if(!c||!c.fn.dataTable){c=require("datatables.net")(b,c).$}return a(c,b,b.document)}}else{a(jQuery,window,document)}}}(function(d,b,a,e){var c=d.fn.dataTable;d.extend(true,c.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});d.extend(c.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm",sProcessing:"dataTables_processing panel panel-default"});c.ext.renderer.pageButton.bootstrap=function(l,u,t,r,q,j){var o=new c.Api(l);var m=l.oClasses;var i=l.oLanguage.oPaginate;var s=l.oLanguage.oAria.paginate||{};var h,g,f=0;var n=function(w,A){var y,v,z,x;var B=function(C){C.preventDefault();if(!d(C.currentTarget).hasClass("disabled")&&o.page()!=C.data.action){o.page(C.data.action).draw("page")}};for(y=0,v=A.length;y<v;y++){x=A[y];if(d.isArray(x)){n(w,x)}else{h="";g="";switch(x){case"ellipsis":h="…";g="disabled";break;case"first":h=i.sFirst;g=x+(q>0?"":" disabled");break;case"previous":h=i.sPrevious;g=x+(q>0?"":" disabled");break;case"next":h=i.sNext;g=x+(q<j-1?"":" disabled");break;case"last":h=i.sLast;g=x+(q<j-1?"":" disabled");break;default:h=x+1;g=q===x?"active":"";break}if(h){z=d("<li>",{"class":m.sPageButton+" "+g,id:t===0&&typeof x==="string"?l.sTableId+"_"+x:null}).append(d("<a>",{href:"#","aria-controls":l.sTableId,"aria-label":s[x],"data-dt-idx":f,tabindex:l.iTabIndex}).html(h)).appendTo(w);l.oApi._fnBindAction(z,{action:x},B);f++}}}};var k;try{k=d(u).find(a.activeElement).data("dt-idx")}catch(p){}n(d(u).empty().html('<ul class="pagination"/>').children("ul"),r);if(k!==e){d(u).find("[data-dt-idx="+k+"]").focus()}};return c}));
|
||||
/*!
|
||||
DataTables Bootstrap 3 integration
|
||||
©2011-2015 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.findInternal=function(a,b,c){a instanceof String&&(a=String(a));for(var e=a.length,d=0;d<e;d++){var f=a[d];if(b.call(c,f,d,a))return{i:d,v:f}}return{i:-1,v:void 0}};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;$jscomp.ISOLATE_POLYFILLS=!1;
|
||||
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(a,b,c){if(a==Array.prototype||a==Object.prototype)return a;a[b]=c.value;return a};$jscomp.getGlobal=function(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
|
||||
$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";var $jscomp$lookupPolyfilledValue=function(a,b){var c=$jscomp.propertyToPolyfillSymbol[b];if(null==c)return a[b];c=a[c];return void 0!==c?c:a[b]};
|
||||
$jscomp.polyfill=function(a,b,c,e){b&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(a,b,c,e):$jscomp.polyfillUnisolated(a,b,c,e))};$jscomp.polyfillUnisolated=function(a,b,c,e){c=$jscomp.global;a=a.split(".");for(e=0;e<a.length-1;e++){var d=a[e];if(!(d in c))return;c=c[d]}a=a[a.length-1];e=c[a];b=b(e);b!=e&&null!=b&&$jscomp.defineProperty(c,a,{configurable:!0,writable:!0,value:b})};
|
||||
$jscomp.polyfillIsolated=function(a,b,c,e){var d=a.split(".");a=1===d.length;e=d[0];e=!a&&e in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var f=0;f<d.length-1;f++){var l=d[f];if(!(l in e))return;e=e[l]}d=d[d.length-1];c=$jscomp.IS_SYMBOL_NATIVE&&"es6"===c?e[d]:null;b=b(c);null!=b&&(a?$jscomp.defineProperty($jscomp.polyfills,d,{configurable:!0,writable:!0,value:b}):b!==c&&($jscomp.propertyToPolyfillSymbol[d]=$jscomp.IS_SYMBOL_NATIVE?$jscomp.global.Symbol(d):$jscomp.POLYFILL_PREFIX+d,d=
|
||||
$jscomp.propertyToPolyfillSymbol[d],$jscomp.defineProperty(e,d,{configurable:!0,writable:!0,value:b})))};$jscomp.polyfill("Array.prototype.find",function(a){return a?a:function(b,c){return $jscomp.findInternal(this,b,c).v}},"es6","es3");
|
||||
(function(a){"function"===typeof define&&define.amd?define(["jquery","datatables.net"],function(b){return a(b,window,document)}):"object"===typeof exports?module.exports=function(b,c){b||(b=window);c&&c.fn.dataTable||(c=require("datatables.net")(b,c).$);return a(c,b,b.document)}:a(jQuery,window,document)})(function(a,b,c,e){var d=a.fn.dataTable;a.extend(!0,d.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-5'i><'col-sm-7'p>>",renderer:"bootstrap"});a.extend(d.ext.classes,
|
||||
{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm",sProcessing:"dataTables_processing panel panel-default"});d.ext.renderer.pageButton.bootstrap=function(f,l,A,B,m,t){var u=new d.Api(f),C=f.oClasses,n=f.oLanguage.oPaginate,D=f.oLanguage.oAria.paginate||{},h,k,v=0,y=function(q,w){var x,E=function(p){p.preventDefault();a(p.currentTarget).hasClass("disabled")||u.page()==p.data.action||u.page(p.data.action).draw("page")};
|
||||
var r=0;for(x=w.length;r<x;r++){var g=w[r];if(Array.isArray(g))y(q,g);else{k=h="";switch(g){case "ellipsis":h="…";k="disabled";break;case "first":h=n.sFirst;k=g+(0<m?"":" disabled");break;case "previous":h=n.sPrevious;k=g+(0<m?"":" disabled");break;case "next":h=n.sNext;k=g+(m<t-1?"":" disabled");break;case "last":h=n.sLast;k=g+(m<t-1?"":" disabled");break;default:h=g+1,k=m===g?"active":""}if(h){var F=a("<li>",{"class":C.sPageButton+" "+k,id:0===A&&"string"===typeof g?f.sTableId+"_"+g:null}).append(a("<a>",
|
||||
{href:"#","aria-controls":f.sTableId,"aria-label":D[g],"data-dt-idx":v,tabindex:f.iTabIndex}).html(h)).appendTo(q);f.oApi._fnBindAction(F,{action:g},E);v++}}}};try{var z=a(l).find(c.activeElement).data("dt-idx")}catch(q){}y(a(l).empty().html('<ul class="pagination"/>').children("ul"),B);z!==e&&a(l).find("[data-dt-idx="+z+"]").trigger("focus")};return d});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
188
www/js/jquery.dataTables.min.js
vendored
188
www/js/jquery.dataTables.min.js
vendored
File diff suppressed because one or more lines are too long
6
www/js/jquery.min.js
vendored
6
www/js/jquery.min.js
vendored
File diff suppressed because one or more lines are too long
118
www/js/labca.js
118
www/js/labca.js
@@ -54,6 +54,100 @@ $(function() {
|
||||
return '<span class="ellipsis" title="'+esc(d)+'">'+shortened+'…</span>';
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
* Natural Sort algorithm for Javascript - Version 0.7 - Released under MIT license
|
||||
* Author: Jim Palmer (based on chunking idea from Dave Koelle)
|
||||
* Contributors: Mike Grier (mgrier.com), Clint Priest, Kyle Adams, guillermo
|
||||
* See: http://js-naturalsort.googlecode.com/svn/trunk/naturalSort.js
|
||||
*/
|
||||
function naturalSort (a, b, html) {
|
||||
var re = /(^-?[0-9]+(\.?[0-9]*)[df]?e?[0-9]?%?$|^0x[0-9a-f]+$|[0-9]+)/gi,
|
||||
sre = /(^[ ]*|[ ]*$)/g,
|
||||
dre = /(^([\w ]+,?[\w ]+)?[\w ]+,?[\w ]+\d+:\d+(:\d+)?[\w ]?|^\d{1,4}[\/\-]\d{1,4}[\/\-]\d{1,4}|^\w+, \w+ \d+, \d{4})/,
|
||||
hre = /^0x[0-9a-f]+$/i,
|
||||
ore = /^0/,
|
||||
htmre = /(<([^>]+)>)/ig,
|
||||
// convert all to strings and trim()
|
||||
x = a.toString().replace(sre, '') || '',
|
||||
y = b.toString().replace(sre, '') || '';
|
||||
// remove html from strings if desired
|
||||
if (!html) {
|
||||
x = x.replace(htmre, '');
|
||||
y = y.replace(htmre, '');
|
||||
}
|
||||
// chunk/tokenize
|
||||
var xN = x.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
|
||||
yN = y.replace(re, '\0$1\0').replace(/\0$/,'').replace(/^\0/,'').split('\0'),
|
||||
// numeric, hex or date detection
|
||||
xD = parseInt(x.match(hre), 10) || (xN.length !== 1 && x.match(dre) && Date.parse(x)),
|
||||
yD = parseInt(y.match(hre), 10) || xD && y.match(dre) && Date.parse(y) || null;
|
||||
|
||||
// first try and sort Hex codes or Dates
|
||||
if (yD) {
|
||||
if ( xD < yD ) {
|
||||
return -1;
|
||||
}
|
||||
else if ( xD > yD ) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// natural sorting through split numeric strings and default strings
|
||||
for(var cLoc=0, numS=Math.max(xN.length, yN.length); cLoc < numS; cLoc++) {
|
||||
// find floats not starting with '0', string or 0 if not defined (Clint Priest)
|
||||
var oFxNcL = !(xN[cLoc] || '').match(ore) && parseFloat(xN[cLoc], 10) || xN[cLoc] || 0;
|
||||
var oFyNcL = !(yN[cLoc] || '').match(ore) && parseFloat(yN[cLoc], 10) || yN[cLoc] || 0;
|
||||
// handle numeric vs string comparison - number < string - (Kyle Adams)
|
||||
if (isNaN(oFxNcL) !== isNaN(oFyNcL)) {
|
||||
return (isNaN(oFxNcL)) ? 1 : -1;
|
||||
}
|
||||
// rely on string comparison if different types - i.e. '02' < 2 != '02' < '2'
|
||||
else if (typeof oFxNcL !== typeof oFyNcL) {
|
||||
oFxNcL += '';
|
||||
oFyNcL += '';
|
||||
}
|
||||
if (oFxNcL < oFyNcL) {
|
||||
return -1;
|
||||
}
|
||||
if (oFxNcL > oFyNcL) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
jQuery.extend( jQuery.fn.dataTableExt.oSort, {
|
||||
"natural-asc": function ( a, b ) {
|
||||
return naturalSort(a,b,true);
|
||||
},
|
||||
|
||||
"natural-desc": function ( a, b ) {
|
||||
return naturalSort(a,b,true) * -1;
|
||||
},
|
||||
|
||||
"natural-nohtml-asc": function( a, b ) {
|
||||
return naturalSort(a,b,false);
|
||||
},
|
||||
|
||||
"natural-nohtml-desc": function( a, b ) {
|
||||
return naturalSort(a,b,false) * -1;
|
||||
},
|
||||
|
||||
"natural-ci-asc": function( a, b ) {
|
||||
a = a.toString().toLowerCase();
|
||||
b = b.toString().toLowerCase();
|
||||
|
||||
return naturalSort(a,b,true);
|
||||
},
|
||||
|
||||
"natural-ci-desc": function( a, b ) {
|
||||
a = a.toString().toLowerCase();
|
||||
b = b.toString().toLowerCase();
|
||||
|
||||
return naturalSort(a,b,true) * -1;
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
$('#o').blur(function() {
|
||||
@@ -203,7 +297,14 @@ $(function() {
|
||||
|
||||
if ( $('.orders_list').length || $('.rel_orders_list').length ) {
|
||||
options["columnDefs"] = [ {
|
||||
targets: 3,
|
||||
targets: 2,
|
||||
render: $.fn.dataTable.render.ellipsis(15)
|
||||
} ];
|
||||
}
|
||||
|
||||
if ( $('.rel_certificates_list').length ) {
|
||||
options["columnDefs"] = [ {
|
||||
targets: 2,
|
||||
render: $.fn.dataTable.render.ellipsis(15)
|
||||
} ];
|
||||
}
|
||||
@@ -212,11 +313,8 @@ $(function() {
|
||||
options["columnDefs"] = [
|
||||
{
|
||||
targets: 0,
|
||||
render: $.fn.dataTable.render.ellipsis(15)
|
||||
},
|
||||
{
|
||||
targets: 1,
|
||||
render: $.fn.dataTable.render.ellipsis(40)
|
||||
render: $.fn.dataTable.render.ellipsis(15),
|
||||
type: 'natural'
|
||||
},
|
||||
];
|
||||
}
|
||||
@@ -287,6 +385,14 @@ $(function() {
|
||||
}
|
||||
});
|
||||
|
||||
$('.auth_show tbody tr').children().each(function () {
|
||||
if (this.textContent == 'Validation Error' || this.textContent == 'Validation Record') {
|
||||
console.log(this.nextElementSibling);
|
||||
this.nextElementSibling.innerText = JSON.stringify(JSON.parse(this.nextElementSibling.innerText), null, 2);
|
||||
$(this.nextElementSibling).wrapInner('<pre class="json"></pre>');
|
||||
}
|
||||
});
|
||||
|
||||
$(".datatable").on('draw.dt', positionFooter);
|
||||
}
|
||||
|
||||
|
||||
91
www/rate-limits.html
Normal file
91
www/rate-limits.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="LabCA is a private Certificate Authority for internal (intranet) use, based on the open source ACME Automated Certificate Management Environment implementation from Let's Encrypt (tm).">
|
||||
<meta name="keywords" content="LabCA PKI CA Certificate Authority ACME Boulder">
|
||||
<meta name="author" content="Arjan Hakkesteegt">
|
||||
|
||||
<title>LabCA</title>
|
||||
|
||||
<link href="css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="css/sb-admin-2.min.css" rel="stylesheet">
|
||||
<link href="css/font-awesome.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="css/labca.css" rel="stylesheet">
|
||||
<link rel="icon" type="image/png" href="img/fav-public.png">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
<nav class="navbar navbar-default navbar-static-top" role="navigation" style="margin-bottom: 0">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">LabCA</a>
|
||||
</div>
|
||||
|
||||
<ul class="nav navbar-top-links navbar-right">
|
||||
<li title="Login to Admin Area"><a href="/admin/"><i class="fa fa-user fa-fw admin-login"></i></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="navbar-default sidebar" role="navigation">
|
||||
<div class="sidebar-nav navbar-collapse">
|
||||
<ul class="nav" id="side-menu">
|
||||
<li><a class="public" href="/"><i class="fa fa-home fa-fw"></i> Home</a>
|
||||
</li>
|
||||
<li><a class="public" href="/certs/index.html"><i class="fa fa-download fa-fw"></i> Certificates</a>
|
||||
</li>
|
||||
<li><a class="public" href="/cps/index.html" title="Certification Practice Statement"><i class="fa fa-book fa-fw"></i> CPS</a>
|
||||
</li>
|
||||
<li><a class="public" href="/rate-limits.html" title="Rate Limits"><i class="fa fa-clock-o fa-fw"></i> Rate Limits</a>
|
||||
</li>
|
||||
<li><a class="public" href="/terms/v1" title="Usage Terms"><i class="fa fa-edit fa-fw"></i> Terms</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="page-wrapper">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<h1 class="page-header">Rate Limits</h1>
|
||||
|
||||
<p>
|
||||
It is unlikely that you hit the rate limit mechanism for your selected domain, as it is set to allow 10,000 certificates in LabCA.
|
||||
</p>
|
||||
<p>
|
||||
If your LabCA instance is set up to (also) allow official domains (not recommended), then for the other domains the main limit is
|
||||
<b>Certificates per Registered Domain</b>: 5 per 24 hours. As per the
|
||||
<a class="public" href="https://letsencrypt.org/docs/rate-limits/">Let's Encrypt™ rate limits page <i class="fa fa-external-link fa-fw ext-link"></i></a>,
|
||||
a registered domain is, generally speaking, the part of the domain you purchased from your domain name registrar. For instance,
|
||||
in the name <code>www.example.com</code>, the registered domain is <code>example.com</code>.
|
||||
In <code>new.blog.example.co.uk</code>, the registered domain is <code>example.co.uk</code>.
|
||||
</p>
|
||||
<p>
|
||||
The other limit is the <b>Duplicate Certificate</b> limit of 2 per 90 days. This applies to renewals when the old dertificate
|
||||
is still valid.
|
||||
</p>
|
||||
<p>
|
||||
<b>Revoking certificates does not reset rate limits</b>, because the resources used to issue those certificates have already been
|
||||
consumed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/jquery.min.js"></script>
|
||||
<script src="js/bootstrap.min.js"></script>
|
||||
<script src="js/metisMenu.min.js"></script>
|
||||
<script src="js/sb-admin-2.min.js"></script>
|
||||
<script src="js/labca.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user