mirror of
https://github.com/outbackdingo/labca.git
synced 2026-01-27 18:19:33 +00:00
Merge branch 'release/0.8.4'
* release/0.8.4: Update datatables to fix issue with clicking on some rows Refactor code to reduce cyclomatic complexity gofmt the .go files
This commit is contained in:
@@ -52,7 +52,7 @@ Once the setup is completed, please make a backup of your Root and Issuer certif
|
||||
|
||||
### Update
|
||||
|
||||
Until issue #1 is implemented, updates can only be done from the Linux shell. On the server run this command as root to update the installation:
|
||||
Until [issue #1](https://github.com/hakwerk/labca/issues/1) is implemented, updates can only be done from the Linux shell. On the server run this command as root to update the installation:
|
||||
|
||||
```sh
|
||||
~labca/labca/install
|
||||
|
||||
1147
gui/acme.go
1147
gui/acme.go
File diff suppressed because it is too large
Load Diff
@@ -1,485 +1,563 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type CertificateInfo struct {
|
||||
IsRoot bool
|
||||
KeyTypes map[string]string
|
||||
KeyType string
|
||||
CreateType string
|
||||
IsRoot bool
|
||||
KeyTypes map[string]string
|
||||
KeyType string
|
||||
CreateType string
|
||||
|
||||
Country string
|
||||
Organization string
|
||||
OrgUnit string
|
||||
CommonName string
|
||||
Country string
|
||||
Organization string
|
||||
OrgUnit string
|
||||
CommonName string
|
||||
|
||||
ImportFile multipart.File
|
||||
ImportHandler *multipart.FileHeader
|
||||
ImportPwd string
|
||||
ImportFile multipart.File
|
||||
ImportHandler *multipart.FileHeader
|
||||
ImportPwd string
|
||||
|
||||
Key string
|
||||
Passphrase string
|
||||
Certificate string
|
||||
Key string
|
||||
Passphrase string
|
||||
Certificate string
|
||||
|
||||
RequestBase string
|
||||
Errors map[string]string
|
||||
RequestBase string
|
||||
Errors map[string]string
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Initialize() {
|
||||
ci.Errors = make(map[string]string)
|
||||
ci.Errors = make(map[string]string)
|
||||
|
||||
ci.KeyTypes = make(map[string]string)
|
||||
ci.KeyTypes["rsa4096"] = "RSA-4096"
|
||||
ci.KeyTypes["rsa3072"] = "RSA-3072"
|
||||
ci.KeyTypes["rsa2048"] = "RSA-2048"
|
||||
ci.KeyTypes["ecdsa384"] = "ECDSA-384"
|
||||
ci.KeyTypes["ecdsa256"] = "ECDSA-256"
|
||||
ci.KeyTypes = make(map[string]string)
|
||||
ci.KeyTypes["rsa4096"] = "RSA-4096"
|
||||
ci.KeyTypes["rsa3072"] = "RSA-3072"
|
||||
ci.KeyTypes["rsa2048"] = "RSA-2048"
|
||||
ci.KeyTypes["ecdsa384"] = "ECDSA-384"
|
||||
ci.KeyTypes["ecdsa256"] = "ECDSA-256"
|
||||
|
||||
ci.KeyType = "rsa4096"
|
||||
ci.KeyType = "rsa4096"
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) ValidateGenerate() {
|
||||
if strings.TrimSpace(ci.KeyType) == "" || strings.TrimSpace(ci.KeyTypes[ci.KeyType]) == "" {
|
||||
ci.Errors["KeyType"] = "Please select a key type/size"
|
||||
}
|
||||
if strings.TrimSpace(ci.Country) == "" || len(ci.Country) < 2 {
|
||||
ci.Errors["Country"] = "Please enter a valid 2-character country code"
|
||||
}
|
||||
if strings.TrimSpace(ci.Organization) == "" {
|
||||
ci.Errors["Organization"] = "Please enter an organization name"
|
||||
}
|
||||
if strings.TrimSpace(ci.CommonName) == "" {
|
||||
ci.Errors["CommonName"] = "Please enter a common name"
|
||||
}
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Validate() bool {
|
||||
ci.Errors = make(map[string]string)
|
||||
ci.Errors = make(map[string]string)
|
||||
|
||||
if ci.CreateType == "generate" {
|
||||
if strings.TrimSpace(ci.KeyType) == "" || strings.TrimSpace(ci.KeyTypes[ci.KeyType]) == "" {
|
||||
ci.Errors["KeyType"] = "Please select a key type/size"
|
||||
}
|
||||
if strings.TrimSpace(ci.Country) == "" || len(ci.Country) < 2 {
|
||||
ci.Errors["Country"] = "Please enter a valid 2-character country code"
|
||||
}
|
||||
if strings.TrimSpace(ci.Organization) == "" {
|
||||
ci.Errors["Organization"] = "Please enter an organization name"
|
||||
}
|
||||
if strings.TrimSpace(ci.CommonName) == "" {
|
||||
ci.Errors["CommonName"] = "Please enter a common name"
|
||||
}
|
||||
}
|
||||
if ci.CreateType == "generate" {
|
||||
ci.ValidateGenerate()
|
||||
}
|
||||
|
||||
if (ci.CreateType == "import") && (ci.ImportHandler != nil) {
|
||||
ext := ci.ImportHandler.Filename[len(ci.ImportHandler.Filename)-4:]
|
||||
if (ci.ImportHandler.Size == 0) || (ext != ".zip" && ext != ".pfx") {
|
||||
ci.Errors["Import"] = "Please provide a bundle (.pfx or .zip) with a key and certificate"
|
||||
}
|
||||
}
|
||||
if (ci.CreateType == "import") && (ci.ImportHandler != nil) {
|
||||
ext := ci.ImportHandler.Filename[len(ci.ImportHandler.Filename)-4:]
|
||||
if (ci.ImportHandler.Size == 0) || (ext != ".zip" && ext != ".pfx") {
|
||||
ci.Errors["Import"] = "Please provide a bundle (.pfx or .zip) with a key and certificate"
|
||||
}
|
||||
}
|
||||
|
||||
if ci.CreateType == "upload" {
|
||||
if strings.TrimSpace(ci.Key) == "" {
|
||||
ci.Errors["Key"] = "Please provide a PEM-encoded key"
|
||||
}
|
||||
if strings.TrimSpace(ci.Certificate) == "" {
|
||||
ci.Errors["Certificate"] = "Please provide a PEM-encoded certificate"
|
||||
}
|
||||
}
|
||||
if ci.CreateType == "upload" {
|
||||
if strings.TrimSpace(ci.Key) == "" {
|
||||
ci.Errors["Key"] = "Please provide a PEM-encoded key"
|
||||
}
|
||||
if strings.TrimSpace(ci.Certificate) == "" {
|
||||
ci.Errors["Certificate"] = "Please provide a PEM-encoded certificate"
|
||||
}
|
||||
}
|
||||
|
||||
return len(ci.Errors) == 0
|
||||
return len(ci.Errors) == 0
|
||||
}
|
||||
|
||||
func reportError(err error) error {
|
||||
lines := strings.Split(string(debug.Stack()), "\n")
|
||||
if len(lines) >= 5 {
|
||||
lines = append(lines[:0], lines[5:]...)
|
||||
}
|
||||
lines := strings.Split(string(debug.Stack()), "\n")
|
||||
if len(lines) >= 5 {
|
||||
lines = append(lines[:0], lines[5:]...)
|
||||
}
|
||||
|
||||
stop := len(lines)
|
||||
for i := 0; i < len(lines); i++ {
|
||||
if strings.Index(lines[i], ".ServeHTTP(") >= 0 {
|
||||
stop = i
|
||||
break
|
||||
}
|
||||
}
|
||||
lines = lines[:stop]
|
||||
lines = append(lines, "...")
|
||||
stop := len(lines)
|
||||
for i := 0; i < len(lines); i++ {
|
||||
if strings.Index(lines[i], ".ServeHTTP(") >= 0 {
|
||||
stop = i
|
||||
break
|
||||
}
|
||||
}
|
||||
lines = lines[:stop]
|
||||
lines = append(lines, "...")
|
||||
|
||||
fmt.Println(strings.Join(lines, "\n"))
|
||||
fmt.Println(strings.Join(lines, "\n"))
|
||||
|
||||
return errors.New("Error (" + err.Error() + ")! See LabCA logs for details")
|
||||
return errors.New("Error (" + err.Error() + ")! See LabCA logs for details")
|
||||
}
|
||||
|
||||
func preCreateTasks(path string) error {
|
||||
if _, err := exe_cmd("touch " + path + "index.txt"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("touch " + path + "index.txt.attr"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("touch " + path + "index.txt"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("touch " + path + "index.txt.attr"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path + "serial"); os.IsNotExist(err) {
|
||||
if err := ioutil.WriteFile(path+"serial", []byte("1000\n"), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(path + "crlnumber"); os.IsNotExist(err) {
|
||||
if err = ioutil.WriteFile(path+"crlnumber", []byte("1000\n"), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(path + "serial"); os.IsNotExist(err) {
|
||||
if err := ioutil.WriteFile(path+"serial", []byte("1000\n"), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(path + "crlnumber"); os.IsNotExist(err) {
|
||||
if err = ioutil.WriteFile(path+"crlnumber", []byte("1000\n"), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := exe_cmd("mkdir -p " + path + "certs"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("mkdir -p " + path + "certs"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Generate(path string, certBase string) error {
|
||||
// 1. Generate key
|
||||
createCmd := "genrsa -aes256 -passout pass:foobar"
|
||||
keySize := " 4096"
|
||||
if strings.HasPrefix(ci.KeyType, "ecdsa") {
|
||||
keySize = ""
|
||||
createCmd = "ecparam -genkey -name "
|
||||
if ci.KeyType == "ecdsa256" {
|
||||
createCmd = createCmd + "prime256v1"
|
||||
}
|
||||
if ci.KeyType == "ecdsa384" {
|
||||
createCmd = createCmd + "secp384r1"
|
||||
}
|
||||
} else {
|
||||
if strings.HasSuffix(ci.KeyType, "3072") {
|
||||
keySize = " 3072"
|
||||
}
|
||||
if strings.HasSuffix(ci.KeyType, "2048") {
|
||||
keySize = " 2048"
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := exe_cmd("openssl " + createCmd + " -out " + path + certBase + ".key" + keySize); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -passin pass:foobar -out " + path + certBase + ".tmp"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("mv " + path + certBase + ".tmp " + path + certBase + ".key"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
|
||||
_, _ = exe_cmd("sleep 1")
|
||||
|
||||
// 2. Generate certificate
|
||||
subject := "/C=" + ci.Country + "/O=" + ci.Organization
|
||||
if ci.OrgUnit != "" {
|
||||
subject = subject + "/OU=" + ci.OrgUnit
|
||||
}
|
||||
subject = subject + "/CN=" + ci.CommonName
|
||||
subject = strings.Replace(subject, " ", "\\\\", -1)
|
||||
|
||||
if ci.IsRoot {
|
||||
if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -days 3650 -new -x509 -extensions v3_ca -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".pem"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
} else {
|
||||
if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -new -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".csr"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("openssl ca -config " + path + "../openssl.cnf -extensions v3_intermediate_ca -days 3600 -md sha384 -notext -batch -in " + path + certBase + ".csr -out " + path + certBase + ".pem"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) ImportPkcs12(tmpFile string, tmpKey string, tmpCert string) error {
|
||||
if ci.IsRoot {
|
||||
if strings.Index(ci.ImportHandler.Filename, "labca_root") != 0 {
|
||||
fmt.Printf("WARNING: importing root from .pfx file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
} else {
|
||||
if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 {
|
||||
fmt.Printf("WARNING: importing issuer from .pfx file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
}
|
||||
|
||||
pwd := "pass:dummy"
|
||||
if ci.ImportPwd != "" {
|
||||
pwd = "pass:" + strings.Replace(ci.ImportPwd, " ", "\\\\", -1)
|
||||
}
|
||||
|
||||
if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil {
|
||||
if strings.Index(string(out), "invalid password") >= 0 {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil {
|
||||
if strings.Index(string(out), "invalid password") >= 0 {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) ImportZip(tmpFile string, tmpDir string) error {
|
||||
if ci.IsRoot {
|
||||
if (strings.Index(ci.ImportHandler.Filename, "labca_root") != 0) && (strings.Index(ci.ImportHandler.Filename, "labca_certificates") != 0) {
|
||||
fmt.Printf("WARNING: importing root from .zip file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
} else {
|
||||
if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 {
|
||||
fmt.Printf("WARNING: importing issuer from .zip file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := "unzip -j"
|
||||
if ci.ImportPwd != "" {
|
||||
cmd = cmd + " -P " + strings.Replace(ci.ImportPwd, " ", "\\\\", -1)
|
||||
} else {
|
||||
cmd = cmd + " -P dummy"
|
||||
}
|
||||
cmd = cmd + " " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -d " + tmpDir
|
||||
|
||||
if _, err := exe_cmd(cmd); err != nil {
|
||||
if err.Error() == "exit status 82" {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Import(path string, certBase string, tmpDir string, tmpKey string, tmpCert string) error {
|
||||
tmpFile := filepath.Join(tmpDir, ci.ImportHandler.Filename)
|
||||
|
||||
f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
io.Copy(f, ci.ImportFile)
|
||||
|
||||
contentType := ci.ImportHandler.Header.Get("Content-Type")
|
||||
if contentType == "application/x-pkcs12" {
|
||||
err := ci.ImportPkcs12(tmpFile, tmpKey, tmpCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else if contentType == "application/zip" {
|
||||
err := ci.ImportZip(tmpFile, tmpDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
return errors.New("Content Type '" + contentType + "' not supported!")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Upload(path string, certBase string, tmpKey string, tmpCert string) error {
|
||||
if err := ioutil.WriteFile(tmpKey, []byte(ci.Key), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pwd := "pass:dummy"
|
||||
if ci.Passphrase != "" {
|
||||
pwd = "pass:" + strings.Replace(ci.Passphrase, " ", "\\\\", -1)
|
||||
}
|
||||
|
||||
if out, err := exe_cmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil {
|
||||
if strings.Index(string(out), ":bad decrypt:") >= 0 {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
} else {
|
||||
if _, err = exe_cmd("mv " + tmpKey + "-out " + tmpKey); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(tmpCert, []byte(ci.Certificate), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) ImportCerts(path string, rootCert string, rootKey string, issuerCert string, issuerKey string) error {
|
||||
var rootSubject string
|
||||
if (rootCert != "") && (rootKey != "") {
|
||||
r, err := exe_cmd("openssl x509 -noout -subject -in " + rootCert)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
rootSubject = string(r[0 : len(r)-1])
|
||||
fmt.Printf("Import root with subject '%s'\n", rootSubject)
|
||||
}
|
||||
|
||||
r, err = exe_cmd("openssl pkey -noout -in " + rootKey)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
fmt.Println("Import root key")
|
||||
}
|
||||
}
|
||||
|
||||
if (issuerCert != "") && (issuerKey != "") {
|
||||
if ci.IsRoot {
|
||||
if err := preCreateTasks(path + "issuer/"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r, err := exe_cmd("openssl x509 -noout -subject -in " + issuerCert)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
fmt.Printf("Import issuer with subject '%s'\n", string(r[0:len(r)-1]))
|
||||
}
|
||||
|
||||
r, err = exe_cmd("openssl x509 -noout -issuer -in " + issuerCert)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
issuerIssuer := string(r[0 : len(r)-1])
|
||||
fmt.Printf("Issuer certificate issued by CA '%s'\n", issuerIssuer)
|
||||
|
||||
if rootSubject == "" {
|
||||
r, err := exe_cmd("openssl x509 -noout -subject -in data/root-ca.pem")
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
rootSubject = string(r[0 : len(r)-1])
|
||||
}
|
||||
}
|
||||
|
||||
issuerIssuer = strings.Replace(issuerIssuer, "issuer=", "", -1)
|
||||
rootSubject = strings.Replace(rootSubject, "subject=", "", -1)
|
||||
if issuerIssuer != rootSubject {
|
||||
return errors.New("Issuer not issued by our Root CA!")
|
||||
}
|
||||
}
|
||||
|
||||
r, err = exe_cmd("openssl pkey -noout -in " + issuerKey)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
fmt.Println("Import issuer key")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) MoveFiles(path string, rootCert string, rootKey string, issuerCert string, issuerKey string) error {
|
||||
if rootCert != "" {
|
||||
if _, err := exe_cmd("mv " + rootCert + " " + path); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if rootKey != "" {
|
||||
if _, err := exe_cmd("mv " + rootKey + " " + path); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if issuerCert != "" {
|
||||
if _, err := exe_cmd("mv " + issuerCert + " data/issuer/"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if issuerKey != "" {
|
||||
if _, err := exe_cmd("mv " + issuerKey + " data/issuer/"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
if (issuerCert != "") && (issuerKey != "") && ci.IsRoot {
|
||||
if err := postCreateTasks(path+"issuer/", "ca-int"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Extract(path string, certBase string, tmpDir string) error {
|
||||
var rootCert string
|
||||
var rootKey string
|
||||
var issuerCert string
|
||||
var issuerKey string
|
||||
|
||||
if ci.IsRoot {
|
||||
rootCert = filepath.Join(tmpDir, "root-ca.pem")
|
||||
rootKey = filepath.Join(tmpDir, "root-ca.key")
|
||||
|
||||
if _, err := os.Stat(rootCert); os.IsNotExist(err) {
|
||||
return errors.New("File does not contain root-ca.pem!")
|
||||
}
|
||||
if _, err := os.Stat(rootKey); os.IsNotExist(err) {
|
||||
return errors.New("File does not contain root-ca.key!")
|
||||
}
|
||||
}
|
||||
|
||||
issuerCert = filepath.Join(tmpDir, "ca-int.pem")
|
||||
issuerKey = filepath.Join(tmpDir, "ca-int.key")
|
||||
|
||||
if _, err := os.Stat(issuerCert); os.IsNotExist(err) {
|
||||
if ci.IsRoot {
|
||||
issuerCert = ""
|
||||
} else {
|
||||
return errors.New("File does not contain ca-int.pem!")
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(issuerKey); os.IsNotExist(err) {
|
||||
if ci.IsRoot {
|
||||
issuerKey = ""
|
||||
} else {
|
||||
return errors.New("File does not contain ca-int.key!")
|
||||
}
|
||||
}
|
||||
|
||||
err := ci.ImportCerts(path, rootCert, rootKey, issuerCert, issuerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// All is good now, move files to their permanent location...
|
||||
err = ci.MoveFiles(path, rootCert, rootKey, issuerCert, issuerKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ci *CertificateInfo) Create(path string, certBase string) error {
|
||||
if err := preCreateTasks(path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := preCreateTasks(path); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "labca")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpDir, err := ioutil.TempDir("", "labca")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer os.RemoveAll(tmpDir)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
var tmpKey string
|
||||
var tmpCert string
|
||||
if ci.IsRoot {
|
||||
tmpKey = filepath.Join(tmpDir, "root-ca.key")
|
||||
tmpCert = filepath.Join(tmpDir, "root-ca.pem")
|
||||
} else {
|
||||
tmpKey = filepath.Join(tmpDir, "ca-int.key")
|
||||
tmpCert = filepath.Join(tmpDir, "ca-int.pem")
|
||||
}
|
||||
var tmpKey string
|
||||
var tmpCert string
|
||||
if ci.IsRoot {
|
||||
tmpKey = filepath.Join(tmpDir, "root-ca.key")
|
||||
tmpCert = filepath.Join(tmpDir, "root-ca.pem")
|
||||
} else {
|
||||
tmpKey = filepath.Join(tmpDir, "ca-int.key")
|
||||
tmpCert = filepath.Join(tmpDir, "ca-int.pem")
|
||||
}
|
||||
|
||||
if ci.CreateType == "generate" {
|
||||
// 1. Generate key
|
||||
createCmd := "genrsa -aes256 -passout pass:foobar"
|
||||
keySize := " 4096"
|
||||
if strings.HasPrefix(ci.KeyType, "ecdsa") {
|
||||
keySize = ""
|
||||
createCmd = "ecparam -genkey -name "
|
||||
if ci.KeyType == "ecdsa256" {
|
||||
createCmd = createCmd + "prime256v1"
|
||||
}
|
||||
if ci.KeyType == "ecdsa384" {
|
||||
createCmd = createCmd + "secp384r1"
|
||||
}
|
||||
} else {
|
||||
if strings.HasSuffix(ci.KeyType, "3072") {
|
||||
keySize = " 3072"
|
||||
}
|
||||
if strings.HasSuffix(ci.KeyType, "2048") {
|
||||
keySize = " 2048"
|
||||
}
|
||||
}
|
||||
if ci.CreateType == "generate" {
|
||||
err := ci.Generate(path, certBase)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := exe_cmd("openssl " + createCmd + " -out " + path + certBase + ".key" + keySize); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -passin pass:foobar -out " + path + certBase + ".tmp"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err = exe_cmd("mv " + path + certBase + ".tmp " + path + certBase + ".key"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
} else if ci.CreateType == "import" {
|
||||
err := ci.Import(path, certBase, tmpDir, tmpKey, tmpCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, _ = exe_cmd("sleep 1")
|
||||
} else if ci.CreateType == "upload" {
|
||||
err := ci.Upload(path, certBase, tmpKey, tmpCert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. Generate certificate
|
||||
subject := "/C=" + ci.Country + "/O=" + ci.Organization
|
||||
if ci.OrgUnit != "" {
|
||||
subject = subject + "/OU=" + ci.OrgUnit
|
||||
}
|
||||
subject = subject + "/CN=" + ci.CommonName
|
||||
subject = strings.Replace(subject, " ", "\\\\", -1)
|
||||
} else {
|
||||
return fmt.Errorf("Unknown CreateType!")
|
||||
}
|
||||
|
||||
if ci.IsRoot {
|
||||
if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -days 3650 -new -x509 -extensions v3_ca -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".pem"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
} else {
|
||||
if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -new -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".csr"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("openssl ca -config " + path + "../openssl.cnf -extensions v3_intermediate_ca -days 3600 -md sha384 -notext -batch -in " + path + certBase + ".csr -out " + path + certBase + ".pem"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
// This is shared between pfx/zip upload and pem text upload
|
||||
if ci.CreateType != "generate" {
|
||||
err := ci.Extract(path, certBase, tmpDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
} else if ci.CreateType == "import" {
|
||||
tmpFile := filepath.Join(tmpDir, ci.ImportHandler.Filename)
|
||||
if err := postCreateTasks(path, certBase); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ci.IsRoot {
|
||||
if _, err := exe_cmd("openssl ca -config " + path + "openssl.cnf -gencrl -keyfile " + path + certBase + ".key -cert " + path + certBase + ".pem -out " + path + certBase + ".crl"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
|
||||
io.Copy(f, ci.ImportFile)
|
||||
|
||||
contentType := ci.ImportHandler.Header.Get("Content-Type")
|
||||
if contentType == "application/x-pkcs12" {
|
||||
if ci.IsRoot {
|
||||
if strings.Index(ci.ImportHandler.Filename, "labca_root") != 0 {
|
||||
fmt.Printf("WARNING: importing root from .pfx file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
} else {
|
||||
if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 {
|
||||
fmt.Printf("WARNING: importing issuer from .pfx file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
}
|
||||
|
||||
pwd := "pass:dummy"
|
||||
if ci.ImportPwd != "" {
|
||||
pwd = "pass:" + strings.Replace(ci.ImportPwd, " ", "\\\\", -1)
|
||||
}
|
||||
|
||||
if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil {
|
||||
if strings.Index(string(out), "invalid password") >= 0 {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil {
|
||||
if strings.Index(string(out), "invalid password") >= 0 {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
} else if contentType == "application/zip" {
|
||||
if ci.IsRoot {
|
||||
if (strings.Index(ci.ImportHandler.Filename, "labca_root") != 0) && (strings.Index(ci.ImportHandler.Filename, "labca_certificates") != 0) {
|
||||
fmt.Printf("WARNING: importing root from .zip file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
} else {
|
||||
if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 {
|
||||
fmt.Printf("WARNING: importing issuer from .zip file but name is %s\n", ci.ImportHandler.Filename)
|
||||
}
|
||||
}
|
||||
|
||||
cmd := "unzip -j"
|
||||
if ci.ImportPwd != "" {
|
||||
cmd = cmd + " -P " + strings.Replace(ci.ImportPwd, " ", "\\\\", -1)
|
||||
} else {
|
||||
cmd = cmd + " -P dummy"
|
||||
}
|
||||
cmd = cmd + " " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -d " + tmpDir
|
||||
|
||||
if _, err := exe_cmd(cmd); err != nil {
|
||||
if err.Error() == "exit status 82" {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return errors.New("Content Type '" + contentType + "' not supported!")
|
||||
}
|
||||
|
||||
} else if ci.CreateType == "upload" {
|
||||
if err := ioutil.WriteFile(tmpKey, []byte(ci.Key), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pwd := "pass:dummy"
|
||||
if ci.Passphrase != "" {
|
||||
pwd = "pass:" + strings.Replace(ci.Passphrase, " ", "\\\\", -1)
|
||||
}
|
||||
|
||||
if out, err := exe_cmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil {
|
||||
if strings.Index(string(out), ":bad decrypt:") >= 0 {
|
||||
return errors.New("Incorrect password!")
|
||||
} else {
|
||||
return reportError(err)
|
||||
}
|
||||
} else {
|
||||
if _, err = exe_cmd("mv " + tmpKey + "-out " + tmpKey); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := ioutil.WriteFile(tmpCert, []byte(ci.Certificate), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
} else {
|
||||
return fmt.Errorf("Unknown CreateType!")
|
||||
}
|
||||
|
||||
// This is shared between pfx/zip upload and pem text upload
|
||||
if ci.CreateType != "generate" {
|
||||
var rootCert string
|
||||
var rootKey string
|
||||
var issuerCert string
|
||||
var issuerKey string
|
||||
|
||||
if ci.IsRoot {
|
||||
rootCert = filepath.Join(tmpDir, "root-ca.pem")
|
||||
rootKey = filepath.Join(tmpDir, "root-ca.key")
|
||||
|
||||
if _, err := os.Stat(rootCert); os.IsNotExist(err) {
|
||||
return errors.New("File does not contain root-ca.pem!")
|
||||
}
|
||||
if _, err := os.Stat(rootKey); os.IsNotExist(err) {
|
||||
return errors.New("File does not contain root-ca.key!")
|
||||
}
|
||||
}
|
||||
|
||||
issuerCert = filepath.Join(tmpDir, "ca-int.pem")
|
||||
issuerKey = filepath.Join(tmpDir, "ca-int.key")
|
||||
|
||||
if _, err := os.Stat(issuerCert); os.IsNotExist(err) {
|
||||
if ci.IsRoot {
|
||||
issuerCert = ""
|
||||
} else {
|
||||
return errors.New("File does not contain ca-int.pem!")
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(issuerKey); os.IsNotExist(err) {
|
||||
if ci.IsRoot {
|
||||
issuerKey = ""
|
||||
} else {
|
||||
return errors.New("File does not contain ca-int.key!")
|
||||
}
|
||||
}
|
||||
|
||||
var rootSubject string
|
||||
if (rootCert != "") && (rootKey != "") {
|
||||
r, err := exe_cmd("openssl x509 -noout -subject -in " + rootCert)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
rootSubject = string(r[0 : len(r)-1])
|
||||
fmt.Printf("Import root with subject '%s'\n", rootSubject)
|
||||
}
|
||||
|
||||
r, err = exe_cmd("openssl pkey -noout -in " + rootKey)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
fmt.Println("Import root key")
|
||||
}
|
||||
}
|
||||
|
||||
if (issuerCert != "") && (issuerKey != "") {
|
||||
if ci.IsRoot {
|
||||
if err := preCreateTasks(path + "issuer/"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
r, err := exe_cmd("openssl x509 -noout -subject -in " + issuerCert)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
fmt.Printf("Import issuer with subject '%s'\n", string(r[0:len(r)-1]))
|
||||
}
|
||||
|
||||
r, err = exe_cmd("openssl x509 -noout -issuer -in " + issuerCert)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
issuerIssuer := string(r[0 : len(r)-1])
|
||||
fmt.Printf("Issuer certificate issued by CA '%s'\n", issuerIssuer)
|
||||
|
||||
if rootSubject == "" {
|
||||
r, err := exe_cmd("openssl x509 -noout -subject -in data/root-ca.pem")
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
rootSubject = string(r[0 : len(r)-1])
|
||||
}
|
||||
}
|
||||
|
||||
issuerIssuer = strings.Replace(issuerIssuer, "issuer=", "", -1)
|
||||
rootSubject = strings.Replace(rootSubject, "subject=", "", -1)
|
||||
if issuerIssuer != rootSubject {
|
||||
return errors.New("Issuer not issued by our Root CA!")
|
||||
}
|
||||
}
|
||||
|
||||
r, err = exe_cmd("openssl pkey -noout -in " + issuerKey)
|
||||
if err != nil {
|
||||
return reportError(err)
|
||||
} else {
|
||||
fmt.Println("Import issuer key")
|
||||
}
|
||||
}
|
||||
|
||||
// All is good now, move files to their permanent location...
|
||||
if rootCert != "" {
|
||||
if _, err = exe_cmd("mv " + rootCert + " " + path); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if rootKey != "" {
|
||||
if _, err = exe_cmd("mv " + rootKey + " " + path); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if issuerCert != "" {
|
||||
if _, err = exe_cmd("mv " + issuerCert + " data/issuer/"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
if issuerKey != "" {
|
||||
if _, err = exe_cmd("mv " + issuerKey + " data/issuer/"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
if (issuerCert != "") && (issuerKey != "") && ci.IsRoot {
|
||||
if err := postCreateTasks(path+"issuer/", "ca-int"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := postCreateTasks(path, certBase); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if ci.IsRoot {
|
||||
if _, err := exe_cmd("openssl ca -config " + path + "openssl.cnf -gencrl -keyfile " + path + certBase + ".key -cert " + path + certBase + ".pem -out " + path + certBase + ".crl"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func postCreateTasks(path string, certBase string) error {
|
||||
if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -out " + path + certBase + ".key.der -outform der"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -out " + path + certBase + ".key.der -outform der"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
|
||||
if _, err := exe_cmd("openssl x509 -in " + path + certBase + ".pem -out " + path + certBase + ".der -outform DER"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
if _, err := exe_cmd("openssl x509 -in " + path + certBase + ".pem -out " + path + certBase + ".der -outform DER"); err != nil {
|
||||
return reportError(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func exe_cmd(cmd string) ([]byte, error) {
|
||||
parts := strings.Fields(cmd)
|
||||
for i := 0; i < len(parts); i++ {
|
||||
parts[i] = strings.Replace(parts[i], "\\\\", " ", -1)
|
||||
}
|
||||
head := parts[0]
|
||||
parts = parts[1:]
|
||||
parts := strings.Fields(cmd)
|
||||
for i := 0; i < len(parts); i++ {
|
||||
parts[i] = strings.Replace(parts[i], "\\\\", " ", -1)
|
||||
}
|
||||
head := parts[0]
|
||||
parts = parts[1:]
|
||||
|
||||
out, err := exec.Command(head, parts...).CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Print(fmt.Sprint(err) + ": " + string(out))
|
||||
} else {
|
||||
//fmt.Println(string(out))
|
||||
}
|
||||
return out, err
|
||||
out, err := exec.Command(head, parts...).CombinedOutput()
|
||||
if err != nil {
|
||||
fmt.Print(fmt.Sprint(err) + ": " + string(out))
|
||||
} else {
|
||||
//fmt.Println(string(out))
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
|
||||
572
gui/dashboard.go
572
gui/dashboard.go
@@ -1,359 +1,363 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/dustin/go-humanize"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/dustin/go-humanize"
|
||||
"log"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Activity struct {
|
||||
Title string
|
||||
Message string
|
||||
Timestamp string
|
||||
TimestampRel string
|
||||
Class string
|
||||
Title string
|
||||
Message string
|
||||
Timestamp string
|
||||
TimestampRel string
|
||||
Class string
|
||||
}
|
||||
|
||||
func _removeAnsiColors(line string) string {
|
||||
b := make([]byte, len(line))
|
||||
var bl int
|
||||
for i := 0; i < len(line); i++ {
|
||||
c := line[i]
|
||||
if c >= 32 && c != 127 {
|
||||
b[bl] = c
|
||||
bl++
|
||||
}
|
||||
}
|
||||
line = string(b[:bl])
|
||||
line = strings.Replace(line, "[31m", "", -1)
|
||||
line = strings.Replace(line, "[1m", "", -1)
|
||||
line = strings.Replace(line, "[0m", "", -1)
|
||||
|
||||
return line
|
||||
}
|
||||
|
||||
func _parseLine(line string, loc *time.Location) Activity {
|
||||
var activity Activity
|
||||
var activity Activity
|
||||
|
||||
// Remove ansi colors
|
||||
b := make([]byte, len(line))
|
||||
var bl int
|
||||
for i := 0; i < len(line); i++ {
|
||||
c := line[i]
|
||||
if c >= 32 && c != 127 {
|
||||
b[bl] = c
|
||||
bl++
|
||||
}
|
||||
}
|
||||
line = string(b[:bl])
|
||||
line = strings.Replace(line, "[31m", "", -1)
|
||||
line = strings.Replace(line, "[1m", "", -1)
|
||||
line = strings.Replace(line, "[0m", "", -1)
|
||||
line = _removeAnsiColors(line)
|
||||
|
||||
re := regexp.MustCompile("^.*\\|\\s*(\\S)(\\S+) (\\S+) (\\S+) (.*)$")
|
||||
result := re.FindStringSubmatch(line)
|
||||
re := regexp.MustCompile("^.*\\|\\s*(\\S)(\\S+) (\\S+) (\\S+) (.*)$")
|
||||
result := re.FindStringSubmatch(line)
|
||||
|
||||
activity.Class = ""
|
||||
if result[1] == "W" {
|
||||
activity.Class = "warning"
|
||||
}
|
||||
if result[1] == "E" {
|
||||
activity.Class = "error"
|
||||
}
|
||||
activity.Class = ""
|
||||
if result[1] == "W" {
|
||||
activity.Class = "warning"
|
||||
}
|
||||
if result[1] == "E" {
|
||||
activity.Class = "error"
|
||||
}
|
||||
|
||||
timestamp, err := time.ParseInLocation("060102150405", result[2], loc)
|
||||
activity.Timestamp = ""
|
||||
activity.TimestampRel = "??"
|
||||
if err == nil {
|
||||
activity.Timestamp = timestamp.Format("02-Jan-2006 15:04:05 MST")
|
||||
activity.Timestamp = strings.Replace(activity.Timestamp, "+0000", "GMT", -1)
|
||||
activity.TimestampRel = humanize.RelTime(timestamp, time.Now(), "", "")
|
||||
}
|
||||
timestamp, err := time.ParseInLocation("060102150405", result[2], loc)
|
||||
activity.Timestamp = ""
|
||||
activity.TimestampRel = "??"
|
||||
if err == nil {
|
||||
activity.Timestamp = timestamp.Format("02-Jan-2006 15:04:05 MST")
|
||||
activity.Timestamp = strings.Replace(activity.Timestamp, "+0000", "GMT", -1)
|
||||
activity.TimestampRel = humanize.RelTime(timestamp, time.Now(), "", "")
|
||||
}
|
||||
|
||||
tail := result[3][len(result[3])-2:]
|
||||
activity.Title = ""
|
||||
switch tail {
|
||||
case "ca":
|
||||
activity.Title = "Certification Agent"
|
||||
case "ra":
|
||||
activity.Title = "Registration Agent"
|
||||
case "sa":
|
||||
activity.Title = "Storage Agent"
|
||||
case "va":
|
||||
activity.Title = "Validation Agent"
|
||||
}
|
||||
tail := result[3][len(result[3])-2:]
|
||||
activity.Title = ""
|
||||
switch tail {
|
||||
case "ca":
|
||||
activity.Title = "Certification Agent"
|
||||
case "ra":
|
||||
activity.Title = "Registration Agent"
|
||||
case "sa":
|
||||
activity.Title = "Storage Agent"
|
||||
case "va":
|
||||
activity.Title = "Validation Agent"
|
||||
}
|
||||
|
||||
message := result[5]
|
||||
idx := strings.Index(message, ".well-known/acme-challenge")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
if strings.Index(message, "Checked CAA records for") > -1 {
|
||||
message = message[0:strings.Index(message, ",")]
|
||||
}
|
||||
if strings.Index(message, "Validation result") > -1 {
|
||||
message = message[0:17]
|
||||
}
|
||||
idx = strings.Index(message, " csr=[")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
idx = strings.Index(message, " precertificate=[")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
if strings.Index(message, "Certificate request - ") > -1 {
|
||||
idx = strings.Index(message, " JSON={")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
}
|
||||
activity.Message = message
|
||||
message := result[5]
|
||||
idx := strings.Index(message, ".well-known/acme-challenge")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
if strings.Index(message, "Checked CAA records for") > -1 {
|
||||
message = message[0:strings.Index(message, ",")]
|
||||
}
|
||||
if strings.Index(message, "Validation result") > -1 {
|
||||
message = message[0:17]
|
||||
}
|
||||
idx = strings.Index(message, " csr=[")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
idx = strings.Index(message, " precertificate=[")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
if strings.Index(message, "Certificate request - ") > -1 {
|
||||
idx = strings.Index(message, " JSON={")
|
||||
if idx > -1 {
|
||||
message = message[0:idx]
|
||||
}
|
||||
}
|
||||
activity.Message = message
|
||||
|
||||
return activity
|
||||
return activity
|
||||
}
|
||||
|
||||
func _parseActivity(data string) []Activity {
|
||||
var activity []Activity
|
||||
var activity []Activity
|
||||
|
||||
lines := strings.Split(data, "\n")
|
||||
lines := strings.Split(data, "\n")
|
||||
|
||||
loc, err := time.LoadLocation(lines[0])
|
||||
if err != nil {
|
||||
log.Printf("Could not determine location: %s\n", err)
|
||||
loc = time.Local
|
||||
}
|
||||
loc, err := time.LoadLocation(lines[0])
|
||||
if err != nil {
|
||||
log.Printf("Could not determine location: %s\n", err)
|
||||
loc = time.Local
|
||||
}
|
||||
|
||||
for i := len(lines) - 2; i >= 1; i-- {
|
||||
activity = append(activity, _parseLine(lines[i], loc))
|
||||
}
|
||||
for i := len(lines) - 2; i >= 1; i-- {
|
||||
activity = append(activity, _parseLine(lines[i], loc))
|
||||
}
|
||||
|
||||
return activity
|
||||
return activity
|
||||
}
|
||||
|
||||
type Component struct {
|
||||
Name string
|
||||
Timestamp string
|
||||
TimestampRel string
|
||||
Class string
|
||||
LogUrl string
|
||||
LogTitle string
|
||||
Buttons []map[string]interface{}
|
||||
Name string
|
||||
Timestamp string
|
||||
TimestampRel string
|
||||
Class string
|
||||
LogUrl string
|
||||
LogTitle string
|
||||
Buttons []map[string]interface{}
|
||||
}
|
||||
|
||||
func _parseComponents(data string) []Component {
|
||||
var components []Component
|
||||
var components []Component
|
||||
|
||||
if data[len(data)-1:] == "\n" {
|
||||
data = data[0 : len(data)-1]
|
||||
}
|
||||
if data[len(data)-1:] == "\n" {
|
||||
data = data[0 : len(data)-1]
|
||||
}
|
||||
|
||||
parts := strings.Split(data, "|")
|
||||
parts := strings.Split(data, "|")
|
||||
|
||||
loc, err := time.LoadLocation(parts[0])
|
||||
if err != nil {
|
||||
log.Printf("Could not determine location: %s\n", err)
|
||||
loc = time.Local
|
||||
}
|
||||
loc, err := time.LoadLocation(parts[0])
|
||||
if err != nil {
|
||||
log.Printf("Could not determine location: %s\n", err)
|
||||
loc = time.Local
|
||||
}
|
||||
|
||||
nginx, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[1], loc)
|
||||
nginxReal := ""
|
||||
nginxNice := "stopped"
|
||||
nginxClass := "error"
|
||||
if err == nil {
|
||||
nginxReal = nginx.Format("02-Jan-2006 15:04:05 MST")
|
||||
nginxNice = humanize.RelTime(nginx, time.Now(), "", "")
|
||||
nginxClass = ""
|
||||
}
|
||||
nginx, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[1], loc)
|
||||
nginxReal := ""
|
||||
nginxNice := "stopped"
|
||||
nginxClass := "error"
|
||||
if err == nil {
|
||||
nginxReal = nginx.Format("02-Jan-2006 15:04:05 MST")
|
||||
nginxNice = humanize.RelTime(nginx, time.Now(), "", "")
|
||||
nginxClass = ""
|
||||
}
|
||||
|
||||
svc, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[2], loc)
|
||||
svcReal := ""
|
||||
svcNice := "stopped"
|
||||
svcClass := "error"
|
||||
if err == nil {
|
||||
svcReal = svc.Format("02-Jan-2006 15:04:05 MST")
|
||||
svcNice = humanize.RelTime(svc, time.Now(), "", "")
|
||||
svcClass = ""
|
||||
}
|
||||
svc, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[2], loc)
|
||||
svcReal := ""
|
||||
svcNice := "stopped"
|
||||
svcClass := "error"
|
||||
if err == nil {
|
||||
svcReal = svc.Format("02-Jan-2006 15:04:05 MST")
|
||||
svcNice = humanize.RelTime(svc, time.Now(), "", "")
|
||||
svcClass = ""
|
||||
}
|
||||
|
||||
boulder, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[3], loc)
|
||||
boulderReal := ""
|
||||
boulderNice := "stopped"
|
||||
boulderClass := "error"
|
||||
if err == nil {
|
||||
boulderReal = boulder.Format("02-Jan-2006 15:04:05 MST")
|
||||
boulderNice = humanize.RelTime(boulder, time.Now(), "", "")
|
||||
boulderClass = ""
|
||||
}
|
||||
boulder, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[3], loc)
|
||||
boulderReal := ""
|
||||
boulderNice := "stopped"
|
||||
boulderClass := "error"
|
||||
if err == nil {
|
||||
boulderReal = boulder.Format("02-Jan-2006 15:04:05 MST")
|
||||
boulderNice = humanize.RelTime(boulder, time.Now(), "", "")
|
||||
boulderClass = ""
|
||||
}
|
||||
|
||||
labca, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[4], loc)
|
||||
labcaReal := ""
|
||||
labcaNice := "stopped"
|
||||
labcaClass := "error"
|
||||
if err == nil {
|
||||
labcaReal = labca.Format("02-Jan-2006 15:04:05 MST")
|
||||
labcaNice = humanize.RelTime(labca, time.Now(), "", "")
|
||||
labcaClass = ""
|
||||
}
|
||||
labca, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[4], loc)
|
||||
labcaReal := ""
|
||||
labcaNice := "stopped"
|
||||
labcaClass := "error"
|
||||
if err == nil {
|
||||
labcaReal = labca.Format("02-Jan-2006 15:04:05 MST")
|
||||
labcaNice = humanize.RelTime(labca, time.Now(), "", "")
|
||||
labcaClass = ""
|
||||
}
|
||||
|
||||
components = append(components, Component{Name: "NGINX Webserver", Timestamp: nginxReal, TimestampRel: nginxNice, Class: nginxClass})
|
||||
components = append(components, Component{Name: "Host Service", Timestamp: svcReal, TimestampRel: svcNice, Class: svcClass})
|
||||
components = append(components, Component{Name: "Boulder (ACME)", Timestamp: boulderReal, TimestampRel: boulderNice, Class: boulderClass})
|
||||
components = append(components, Component{Name: "LabCA Application", Timestamp: labcaReal, TimestampRel: labcaNice, Class: labcaClass})
|
||||
components = append(components, Component{Name: "NGINX Webserver", Timestamp: nginxReal, TimestampRel: nginxNice, Class: nginxClass})
|
||||
components = append(components, Component{Name: "Host Service", Timestamp: svcReal, TimestampRel: svcNice, Class: svcClass})
|
||||
components = append(components, Component{Name: "Boulder (ACME)", Timestamp: boulderReal, TimestampRel: boulderNice, Class: boulderClass})
|
||||
components = append(components, Component{Name: "LabCA Application", Timestamp: labcaReal, TimestampRel: labcaNice, Class: labcaClass})
|
||||
|
||||
return components
|
||||
return components
|
||||
}
|
||||
|
||||
type Stat struct {
|
||||
Name string
|
||||
Hint string
|
||||
Value string
|
||||
Class string
|
||||
Name string
|
||||
Hint string
|
||||
Value string
|
||||
Class string
|
||||
}
|
||||
|
||||
func _parseStats(data string) []Stat {
|
||||
var stats []Stat
|
||||
var stats []Stat
|
||||
|
||||
if data[len(data)-1:] == "\n" {
|
||||
data = data[0 : len(data)-1]
|
||||
}
|
||||
if data[len(data)-1:] == "\n" {
|
||||
data = data[0 : len(data)-1]
|
||||
}
|
||||
|
||||
parts := strings.Split(data, "|")
|
||||
parts := strings.Split(data, "|")
|
||||
|
||||
loc, err := time.LoadLocation(parts[0])
|
||||
if err != nil {
|
||||
log.Printf("Could not determine location: %s\n", err)
|
||||
loc = time.Local
|
||||
}
|
||||
loc, err := time.LoadLocation(parts[0])
|
||||
if err != nil {
|
||||
log.Printf("Could not determine location: %s\n", err)
|
||||
loc = time.Local
|
||||
}
|
||||
|
||||
since, err := time.ParseInLocation("2006-01-02 15:04:05", parts[1], loc)
|
||||
var sinceReal string
|
||||
sinceNice := "??"
|
||||
if err == nil {
|
||||
sinceReal = since.Format("02-Jan-2006 15:04:05 MST")
|
||||
sinceNice = humanize.RelTime(since, time.Now(), "", "")
|
||||
}
|
||||
stats = append(stats, Stat{Name: "System Uptime", Hint: sinceReal, Value: sinceNice})
|
||||
since, err := time.ParseInLocation("2006-01-02 15:04:05", parts[1], loc)
|
||||
var sinceReal string
|
||||
sinceNice := "??"
|
||||
if err == nil {
|
||||
sinceReal = since.Format("02-Jan-2006 15:04:05 MST")
|
||||
sinceNice = humanize.RelTime(since, time.Now(), "", "")
|
||||
}
|
||||
stats = append(stats, Stat{Name: "System Uptime", Hint: sinceReal, Value: sinceNice})
|
||||
|
||||
numProcs, err := strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
numProcs = 0
|
||||
}
|
||||
stats = append(stats, Stat{Name: "Process Count", Value: strconv.Itoa(numProcs)})
|
||||
numProcs, err := strconv.Atoi(parts[2])
|
||||
if err != nil {
|
||||
numProcs = 0
|
||||
}
|
||||
stats = append(stats, Stat{Name: "Process Count", Value: strconv.Itoa(numProcs)})
|
||||
|
||||
memUsed, err := strconv.ParseUint(parts[3], 10, 64)
|
||||
if err != nil {
|
||||
memUsed = 0
|
||||
}
|
||||
memAvail, err := strconv.ParseUint(parts[4], 10, 64)
|
||||
if err != nil {
|
||||
memAvail = 0
|
||||
}
|
||||
memUsed, err := strconv.ParseUint(parts[3], 10, 64)
|
||||
if err != nil {
|
||||
memUsed = 0
|
||||
}
|
||||
memAvail, err := strconv.ParseUint(parts[4], 10, 64)
|
||||
if err != nil {
|
||||
memAvail = 0
|
||||
}
|
||||
|
||||
percMem := float64(0)
|
||||
if (memUsed + memAvail) > 0 {
|
||||
percMem = float64(100) * float64(memUsed) / float64(memUsed+memAvail)
|
||||
}
|
||||
percMem := float64(0)
|
||||
if (memUsed + memAvail) > 0 {
|
||||
percMem = float64(100) * float64(memUsed) / float64(memUsed+memAvail)
|
||||
}
|
||||
|
||||
usedHuman := humanize.IBytes(memUsed)
|
||||
availHuman := humanize.IBytes(memAvail)
|
||||
percHuman := fmt.Sprintf("%s %%", humanize.FtoaWithDigits(percMem, 1))
|
||||
usedHuman := humanize.IBytes(memUsed)
|
||||
availHuman := humanize.IBytes(memAvail)
|
||||
percHuman := fmt.Sprintf("%s %%", humanize.FtoaWithDigits(percMem, 1))
|
||||
|
||||
class := ""
|
||||
if percMem > 75 {
|
||||
class = "warning"
|
||||
}
|
||||
if percMem > 90 {
|
||||
class = "error"
|
||||
}
|
||||
stats = append(stats, Stat{Name: "Memory Usage", Value: percHuman, Class: class})
|
||||
stats = append(stats, Stat{Name: "Memory Used", Value: usedHuman})
|
||||
class = ""
|
||||
if memAvail < 250000000 {
|
||||
class = "warning"
|
||||
}
|
||||
if memAvail < 100000000 {
|
||||
class = "error"
|
||||
}
|
||||
stats = append(stats, Stat{Name: "Memory Available", Value: availHuman, Class: class})
|
||||
class := ""
|
||||
if percMem > 75 {
|
||||
class = "warning"
|
||||
}
|
||||
if percMem > 90 {
|
||||
class = "error"
|
||||
}
|
||||
stats = append(stats, Stat{Name: "Memory Usage", Value: percHuman, Class: class})
|
||||
stats = append(stats, Stat{Name: "Memory Used", Value: usedHuman})
|
||||
class = ""
|
||||
if memAvail < 250000000 {
|
||||
class = "warning"
|
||||
}
|
||||
if memAvail < 100000000 {
|
||||
class = "error"
|
||||
}
|
||||
stats = append(stats, Stat{Name: "Memory Available", Value: availHuman, Class: class})
|
||||
|
||||
return stats
|
||||
return stats
|
||||
}
|
||||
|
||||
func CollectDashboardData(w http.ResponseWriter, r *http.Request) (map[string]interface {}, error) {
|
||||
db, err := sql.Open(dbType, dbConn)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
func CollectDashboardData(w http.ResponseWriter, r *http.Request) (map[string]interface{}, error) {
|
||||
db, err := sql.Open(dbType, dbConn)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer db.Close()
|
||||
defer db.Close()
|
||||
|
||||
dashboardData := make(map[string]interface{})
|
||||
dashboardData["RequestBase"] = r.Header.Get("X-Request-Base")
|
||||
dashboardData := make(map[string]interface{})
|
||||
dashboardData["RequestBase"] = r.Header.Get("X-Request-Base")
|
||||
|
||||
rows, err := db.Query("SELECT count(*) FROM registrations")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
rows, err := db.Query("SELECT count(*) FROM registrations")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var dbres int
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
var dbres int
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dashboardData["NumAccounts"] = dbres
|
||||
}
|
||||
dashboardData["NumAccounts"] = dbres
|
||||
}
|
||||
|
||||
rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter >= NOW()")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter >= NOW()")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dashboardData["NumCerts"] = dbres
|
||||
}
|
||||
dashboardData["NumCerts"] = dbres
|
||||
}
|
||||
|
||||
rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter < NOW()")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter < NOW()")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dashboardData["NumExpired"] = dbres
|
||||
}
|
||||
dashboardData["NumExpired"] = dbres
|
||||
}
|
||||
|
||||
rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate<>'0000-00-00 00:00:00'")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate<>'0000-00-00 00:00:00'")
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
if rows.Next() {
|
||||
err = rows.Scan(&dbres)
|
||||
if err != nil {
|
||||
errorHandler(w, r, err, http.StatusInternalServerError)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dashboardData["NumRevoked"] = dbres
|
||||
}
|
||||
dashboardData["NumRevoked"] = dbres
|
||||
}
|
||||
|
||||
activity := getLog(w, r, "activity")
|
||||
dashboardData["Activity"] = _parseActivity(activity)
|
||||
activity := getLog(w, r, "activity")
|
||||
dashboardData["Activity"] = _parseActivity(activity)
|
||||
|
||||
components := getLog(w, r, "components")
|
||||
dashboardData["Components"] = _parseComponents(components)
|
||||
components := getLog(w, r, "components")
|
||||
dashboardData["Components"] = _parseComponents(components)
|
||||
|
||||
stats := getLog(w, r, "stats")
|
||||
dashboardData["Stats"] = _parseStats(stats)
|
||||
stats := getLog(w, r, "stats")
|
||||
dashboardData["Stats"] = _parseStats(stats)
|
||||
|
||||
return dashboardData, nil
|
||||
return dashboardData, nil
|
||||
}
|
||||
|
||||
|
||||
3814
gui/main.go
3814
gui/main.go
File diff suppressed because it is too large
Load Diff
12
www/js/dataTables.bootstrap.min.js
vendored
12
www/js/dataTables.bootstrap.min.js
vendored
@@ -1,8 +1,4 @@
|
||||
/*!
|
||||
DataTables Bootstrap 3 integration
|
||||
©2011-2014 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
(function(){var f=function(c,b){c.extend(!0,b.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-6'i><'col-sm-6'p>>",renderer:"bootstrap"});c.extend(b.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm"});b.ext.renderer.pageButton.bootstrap=function(g,f,p,k,h,l){var q=new b.Api(g),r=g.oClasses,i=g.oLanguage.oPaginate,d,e,o=function(b,f){var j,m,n,a,k=function(a){a.preventDefault();
|
||||
c(a.currentTarget).hasClass("disabled")||q.page(a.data.action).draw(!1)};j=0;for(m=f.length;j<m;j++)if(a=f[j],c.isArray(a))o(b,a);else{e=d="";switch(a){case "ellipsis":d="…";e="disabled";break;case "first":d=i.sFirst;e=a+(0<h?"":" disabled");break;case "previous":d=i.sPrevious;e=a+(0<h?"":" disabled");break;case "next":d=i.sNext;e=a+(h<l-1?"":" disabled");break;case "last":d=i.sLast;e=a+(h<l-1?"":" disabled");break;default:d=a+1,e=h===a?"active":""}d&&(n=c("<li>",{"class":r.sPageButton+" "+
|
||||
e,"aria-controls":g.sTableId,tabindex:g.iTabIndex,id:0===p&&"string"===typeof a?g.sTableId+"_"+a:null}).append(c("<a>",{href:"#"}).html(d)).appendTo(b),g.oApi._fnBindAction(n,{action:a},k))}};o(c(f).empty().html('<ul class="pagination"/>').children("ul"),k)};b.TableTools&&(c.extend(!0,b.TableTools.classes,{container:"DTTT btn-group",buttons:{normal:"btn btn-default",disabled:"disabled"},collection:{container:"DTTT_dropdown dropdown-menu",buttons:{normal:"",disabled:"disabled"}},print:{info:"DTTT_print_info"},
|
||||
select:{row:"active"}}),c.extend(!0,b.TableTools.DEFAULTS.oTags,{collection:{container:"ul",button:"li",liner:"a"}}))};"function"===typeof define&&define.amd?define(["jquery","datatables"],f):"object"===typeof exports?f(require("jquery"),require("datatables")):jQuery&&f(jQuery,jQuery.fn.dataTable)})(window,document);
|
||||
/*! 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}));
|
||||
170
www/js/jquery.dataTables.min.js
vendored
170
www/js/jquery.dataTables.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -9,7 +9,6 @@ function positionFooter() {
|
||||
if (offset < 0 ) {
|
||||
offset = 0;
|
||||
}
|
||||
console.log("applying offset of " + offset);
|
||||
$("#footer").parent().css("padding-top", offset + "px");
|
||||
}
|
||||
|
||||
@@ -283,7 +282,9 @@ $(function() {
|
||||
|
||||
$('.rel_certificates_list tbody').on('click', 'tr', function () {
|
||||
var data = table.row( this ).data();
|
||||
window.location = window.location + '/../../certificates/' + data[0]
|
||||
if (data) {
|
||||
window.location = window.location + '/../../certificates/' + data[0]
|
||||
}
|
||||
});
|
||||
|
||||
$(".datatable").on('draw.dt', positionFooter);
|
||||
|
||||
Reference in New Issue
Block a user