grim/hgkeeper

Add a caching layer to the hgweb portion. This should take some strain off of mercurial anf our cpu quota
package access
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"sync"
log "github.com/sirupsen/logrus"
"golang.org/x/crypto/ssh"
)
const (
keysDir = "keys"
)
var (
keys map[string]string
keysLock sync.Mutex
)
func refreshKeys(adminRepoPath string) error {
keysLock.Lock()
defer keysLock.Unlock()
keys = map[string]string{}
keysPath := filepath.Join(adminRepoPath, keysDir)
if _, err := os.Stat(keysPath); err != nil {
if os.IsNotExist(err) {
log.Error("keys directory not found, no one will be to access hgkeeper")
return nil
}
}
entries, err := ioutil.ReadDir(keysPath)
if err != nil {
return err
}
for _, entry := range entries {
if entry.Mode().IsRegular() {
filename := filepath.Join(keysPath, entry.Name())
loadSshKey(filename, entry.Name())
}
}
return nil
}
func loadSshKey(filename, username string) {
buffer, err := ioutil.ReadFile(filename)
if err != nil {
log.Warnf("failed to read keyfile for user %s: %v", username, err)
return
}
counter := 0
// iterate through the file reading one ssh public key at a time
for len(buffer) > 0 {
var pubkey ssh.PublicKey
pubkey, _, _, buffer, err = ssh.ParseAuthorizedKey(buffer)
if err != nil {
if !strings.HasSuffix(err.Error(), "ssh: no key found") {
log.Warnf("failed to parse key file for user %s: %v", username, err)
}
continue
}
fingerprint := ssh.FingerprintSHA256(pubkey)
keys[fingerprint] = username
counter++
}
log.Infof("loaded %d keys for user %q", counter, username)
}
// UsernameFromFingerprint looks up a username from an SSH key's fingerprint
// and returns the username if found, or err if not found.
func UsernameFromFingerprint(fingerprint string) (string, error) {
keysLock.Lock()
defer keysLock.Unlock()
username, found := keys[fingerprint]
if !found {
return "", fmt.Errorf("user not found")
}
return username, nil
}
func UsernameFromPubkey(pubkey ssh.PublicKey) (string, error) {
return UsernameFromFingerprint(ssh.FingerprintSHA256(pubkey))
}