Commit 26ad720e authored by Heiko Reese's avatar Heiko Reese
Browse files

More documentation, little refactoring

parent 42b72864
......@@ -13,6 +13,7 @@ import (
"net/http"
"net/http/fcgi"
"path/filepath"
"sort"
"strings"
)
......@@ -29,7 +30,7 @@ var (
ccache CertCache
allCertDir string
watcherDone chan bool
newFileChan chan string = make(chan string, 64)
newFileChan chan string
)
func init() {
......@@ -85,6 +86,7 @@ func searchHandler(w http.ResponseWriter, r *http.Request) {
)
filter := MakeSimpleFilter(query)
results := ccache.Filter(filter)
sort.Sort(DefaultResultOrder(results))
switch format {
case "json":
case "html":
......@@ -102,7 +104,7 @@ func main() {
// create watcher for new certificates
log.Println("Starting filewatcher for directory", allCertDir)
NewDirectoryWatcher(allCertDir, watcherDone, newFileChan)
_, newFileChan, watcherDone = NewDirectoryWatcher(allCertDir)
go func(newFileChan chan string) {
for newcert := range newFileChan {
for _, c := range ReadCertificates(newcert) {
......
......@@ -7,11 +7,13 @@ import (
"sync"
)
// CertCache provides a map[string]*SearchableCert that is thread-safe
type CertCache struct {
certs map[string]*SearchableCert
sync.RWMutex
}
// SCFilter functions implement a selection criteria for a SearchableCert
type SCFilter func(*SearchableCert) bool
func NewCertCache() CertCache {
......@@ -20,6 +22,7 @@ func NewCertCache() CertCache {
}
}
// Add a certificate to a CertCache
func (cc *CertCache) Add(cert *x509.Certificate) (*SearchableCert, bool) {
searchableCert := CertToSearchable(cert)
......@@ -31,12 +34,14 @@ func (cc *CertCache) Add(cert *x509.Certificate) (*SearchableCert, bool) {
return &searchableCert, isPresent
}
// Delete a certificate from a CertCache
func (cc *CertCache) Delete(serial string) {
cc.Lock()
delete(cc.certs, serial)
cc.Unlock()
}
// Len returns the number of SearchableCerts in a CertCache
func (cc *CertCache) Len() int {
cc.RLock()
l := len(cc.certs)
......@@ -44,6 +49,7 @@ func (cc *CertCache) Len() int {
return l
}
// Get retrieves a SearchableCert by serial number, return nil on failure
func (cc *CertCache) Get(serial string) *SearchableCert {
cc.RLock()
cert, present := cc.certs[serial]
......@@ -55,6 +61,7 @@ func (cc *CertCache) Get(serial string) *SearchableCert {
}
}
// Filter returns all SearchableCerts that match the filter's criteria
func (cc *CertCache) Filter(filter SCFilter) []*SearchableCert {
var matches []*SearchableCert
......@@ -67,3 +74,32 @@ func (cc *CertCache) Filter(filter SCFilter) []*SearchableCert {
cc.RUnlock()
return matches
}
// sort search results in a "helpful" way
type DefaultResultOrder []*SearchableCert
func (r DefaultResultOrder) Len() int {
return len(r)
}
func (r DefaultResultOrder) Swap(i, j int) {
r[i], r[j] = r[j], r[i]
}
func (r DefaultResultOrder) Less(i, j int) bool {
switch {
case r[i].sortingKeyType < r[j].sortingKeyType:
return true
case r[i].sortingKeyType > r[j].sortingKeyType:
return false
default:
switch {
case r[i].RawCertificate.Subject.CommonName < r[j].RawCertificate.Subject.CommonName:
return true
case r[i].RawCertificate.Subject.CommonName > r[j].RawCertificate.Subject.CommonName:
return false
default:
return r[i].RawCertificate.NotBefore.Before(r[j].RawCertificate.NotBefore)
}
}
}
......@@ -7,6 +7,7 @@ import (
"log"
)
// ReadCertificates reads alls x509 certificates from a list of input files. Errors are logged and skipped.
func ReadCertificates(filenames ...string) []*x509.Certificate {
var (
allcerts []*x509.Certificate = make([]*x509.Certificate, 0, len(filenames))
......@@ -36,7 +37,7 @@ func ReadCertificates(filenames ...string) []*x509.Certificate {
}
}
} else {
log.Println(err)
log.Printf("Error reading file %s: %s", filename, err)
}
}
return allcerts
......
......@@ -5,7 +5,17 @@ import (
"log"
)
func NewDirectoryWatcher(path string, watcherDone chan bool, newFileChan chan string) *fsnotify.Watcher {
// NewDirectoryWatcher watches path for newly written files. It returns three values
// * the underlying fsnotify.Watcher
// * a chan string that receives the filename of newly written files and
// * a chan bool. Send any value in to stop the watching goroutines.
func NewDirectoryWatcher(path string) (*fsnotify.Watcher, chan string, chan bool) {
var (
watcherDone chan bool
exitEventRoutine chan bool
newFileChan chan string = make(chan string, 64)
)
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
......@@ -19,7 +29,9 @@ func NewDirectoryWatcher(path string, watcherDone chan bool, newFileChan chan st
newFileChan <- event.Name
}
case err := <-watcher.Errors:
log.Println("error:", err)
log.Printf("Error watching path %s: %s", path, err)
case <-watcherDone:
return
}
}
}()
......@@ -30,8 +42,11 @@ func NewDirectoryWatcher(path string, watcherDone chan bool, newFileChan chan st
if err != nil {
log.Fatal(err)
}
// wait for stop signal
<-watcherDone
// stop the other goroutine
exitEventRoutine <- true
}()
return watcher
return watcher, newFileChan, watcherDone
}
......@@ -7,8 +7,8 @@ import (
)
var (
serialG2Start big.Int
serialG1Cutoff big.Int
serialG2First big.Int
serialG1Final big.Int
errorCannotConvert = errors.New("Unable to convert serial number to bigint")
errorUnknownCA = errors.New("Unable to determine CA generation")
RedirTemplates = map[string]string{
......@@ -18,8 +18,10 @@ var (
)
func init() {
serialG2Start.SetString("8926168349745120614054526923", 10)
serialG1Cutoff.SetString("99000000000000000000000000000", 10) // TODO: anpassen sobald bekannt
// this is the serial of the first certificate of KIT-CA G2
serialG2First.SetString("8926168349745120614054526923", 10)
// this is the serial of the last certificate of KIT-CA G1
serialG1Final.SetString("9999999999999999999999999999", 10) // TODO: anpassen sobald bekannt
}
......@@ -30,13 +32,13 @@ func GetIssuer(serial string, ccache *CertCache) (string, error) {
if !ok {
return "", errorCannotConvert
}
/* das erlaubt dann nicht-legale seriennummern an den gesicherten enden
/* auskommentiert, weil: das erlaubt dann nicht-legale seriennummern an den gesicherten enden
// alte CA (kurze nummern, serial kleiner als erstes g2)
if len(serial) == 8 || len(serial) == 14 || sernum.Cmp(&serialG2Start) < 1 {
if len(serial) == 8 || len(serial) == 14 || sernum.Cmp(&serialG2First) < 1 {
return kitcag1, nil
}
// neue CA (seriennummer größer als letztes g1)
if sernum.Cmp(&serialG1Cutoff) == 1 {
if sernum.Cmp(&serialG1Final) == 1 {
return kitcag2, nil
}
*/
......
......@@ -5,6 +5,7 @@ import (
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"git.scc.kit.edu/heiko.reese/CertificateStats"
"log"
"reflect"
"regexp"
......@@ -12,25 +13,37 @@ import (
"time"
)
var (
Benutzer = "benutzer"
Server = "server"
Pseudonym = "pseudonym"
Gruppe = "gruppe"
Extern = "extern"
)
type SearchableCert struct {
Serial string `json:serial`
HexSerial string `json:hexserial`
SignatureAlgorithm string `json:sigalg`
Subject string `json:subject`
Issuer string `json:issuer`
KeyLength int `json:keylength`
NotBefore time.Time `json:notbefore`
NotAfter time.Time `json:notafter`
DNSNames []string `json:san`
EmailAddresses []string `json:email`
IPAddresses []string `json:ip`
CAGeneration *string
searchablestring string // TODO
RawCertificate *x509.Certificate
Serial string `json:serial`
HexSerial string `json:hexserial`
SignatureAlgorithm string `json:sigalg`
Subject string `json:subject`
Issuer string `json:issuer`
KeyLength int `json:keylength`
NotBefore time.Time `json:notbefore`
NotAfter time.Time `json:notafter`
DNSNames []string `json:dnsnames`
EmailAddresses []string `json:emails`
IPAddresses []string `json:ipaddresses`
CAGeneration *string `json:whichca`
Type *string `json:type`
sortingKeyType int `json:-`
searchablestring string `json:-`
RawCertificate *x509.Certificate `json:-`
}
// NeverMatch does not match any SearchableCert
func NeverMatch(c *SearchableCert) bool { return false }
// MakeSimpleFilter creates a SCFilter that based on a simple substring match
func MakeSimpleFilter(query string) SCFilter {
// TODO: better cleanup of query string
quoted := strings.ToLower(regexp.QuoteMeta(query))
......@@ -49,6 +62,10 @@ func MakeSimpleFilter(query string) SCFilter {
}
}
// DnToString turns a DistinguishedName into a readable string
// According to the relevant RFCs, there is no canonical form.
// The returned format is designed to be sortable and have good
// readability.
func DnToString(n pkix.Name) string {
var parts []string
// CN
......@@ -83,6 +100,7 @@ func DnToString(n pkix.Name) string {
return strings.Join(parts, ",")
}
// CertToSearchable converts an x509.Certificate into a SearchableCert
func CertToSearchable(c *x509.Certificate) SearchableCert {
cert := SearchableCert{
Serial: c.SerialNumber.Text(10),
......@@ -112,6 +130,7 @@ func CertToSearchable(c *x509.Certificate) SearchableCert {
cert.CAGeneration = &kitcag2
}
// build string used for simple search
var buffer bytes.Buffer
buffer.WriteString(cert.Serial)
buffer.WriteString(" ")
......@@ -125,5 +144,24 @@ func CertToSearchable(c *x509.Certificate) SearchableCert {
buffer.WriteString(strings.Join(cert.IPAddresses, " "))
cert.searchablestring = strings.ToLower(buffer.String())
// Sorting order: Group > User > Ext > Server > Pseudonym
switch {
case CertificateStats.FilterIsPseudonym(c):
cert.Type = &Pseudonym
cert.sortingKeyType = 16
case CertificateStats.FilterIsGroup(c):
cert.Type = &Gruppe
cert.sortingKeyType = 1
case CertificateStats.FilterIsExternal(c):
cert.Type = &Extern
cert.sortingKeyType = 4
case CertificateStats.FilterIsNutzer(c):
cert.Type = &Benutzer
cert.sortingKeyType = 2
default:
cert.Type = &Server
cert.sortingKeyType = 8
}
return cert
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment