Compare commits
2 Commits
a8f1a99667
...
557f9085f6
Author | SHA1 | Date |
---|---|---|
AJ ONeal | 557f9085f6 | 4 years ago |
AJ ONeal | 9de2f796db | 4 years ago |
7 changed files with 282 additions and 107 deletions
@ -0,0 +1,131 @@ |
|||||
|
package kvdb |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"errors" |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"os" |
||||
|
"path/filepath" |
||||
|
"strconv" |
||||
|
"strings" |
||||
|
) |
||||
|
|
||||
|
type KVDB struct { |
||||
|
Prefix string |
||||
|
Ext string |
||||
|
} |
||||
|
|
||||
|
func (kv *KVDB) Load( |
||||
|
keyif interface{}, |
||||
|
typ ...interface{}, |
||||
|
) (value interface{}, ok bool, err error) { |
||||
|
key, _ := keyif.(string) |
||||
|
if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#!:| \n") { |
||||
|
return nil, false, nil |
||||
|
} |
||||
|
|
||||
|
userFile := filepath.Join(kv.Prefix, key+"."+kv.Ext) |
||||
|
fmt.Println("Debug user file:", userFile) |
||||
|
b, err := ioutil.ReadFile(userFile) |
||||
|
if nil != err { |
||||
|
if os.IsNotExist(err) { |
||||
|
return nil, false, nil |
||||
|
} |
||||
|
fmt.Println("kvdb debug read:", err) |
||||
|
return nil, false, errors.New("database read failed") |
||||
|
} |
||||
|
|
||||
|
ok = true |
||||
|
value = b |
||||
|
if 1 == len(typ) { |
||||
|
err := json.Unmarshal(b, typ[0]) |
||||
|
if nil != err { |
||||
|
return nil, false, err |
||||
|
} |
||||
|
value = typ[0] |
||||
|
} else if len(b) > 0 && '"' == b[0] { |
||||
|
var str string |
||||
|
err := json.Unmarshal(b, &str) |
||||
|
if nil == err { |
||||
|
value = str |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return value, ok, nil |
||||
|
} |
||||
|
|
||||
|
func (kv *KVDB) Store(keyif interface{}, value interface{}) (err error) { |
||||
|
key, _ := keyif.(string) |
||||
|
if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#! \n") { |
||||
|
return errors.New("invalid key name") |
||||
|
} |
||||
|
|
||||
|
keypath := filepath.Join(kv.Prefix, key+"."+kv.Ext) |
||||
|
f, err := os.Open(keypath) |
||||
|
if nil == err { |
||||
|
s, err := f.Stat() |
||||
|
if nil != err { |
||||
|
// if we can open, we should be able to stat
|
||||
|
return errors.New("database connection failure") |
||||
|
} |
||||
|
ts := strconv.FormatInt(s.ModTime().Unix(), 10) |
||||
|
bakpath := filepath.Join(kv.Prefix, key+"."+ts+"."+kv.Ext) |
||||
|
if err := os.Rename(keypath, bakpath); nil != err { |
||||
|
// keep the old record as a backup
|
||||
|
return errors.New("database write failure") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
var b []byte |
||||
|
switch v := value.(type) { |
||||
|
case []byte: |
||||
|
b = v |
||||
|
case string: |
||||
|
b, _ = json.Marshal(v) |
||||
|
default: |
||||
|
fmt.Println("kvdb: not []byte or string:", v) |
||||
|
jsonb, err := json.Marshal(v) |
||||
|
if nil != err { |
||||
|
return err |
||||
|
} |
||||
|
b = jsonb |
||||
|
} |
||||
|
|
||||
|
if err := ioutil.WriteFile( |
||||
|
keypath, |
||||
|
b, |
||||
|
os.FileMode(0600), |
||||
|
); nil != err { |
||||
|
fmt.Println("write failure:", err) |
||||
|
return errors.New("database write failed") |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (kv *KVDB) Delete(keyif interface{}) (err error) { |
||||
|
key, _ := keyif.(string) |
||||
|
if "" == key || strings.Contains(key, "..") || strings.ContainsAny(key, "$#! \n") { |
||||
|
return errors.New("invalid key name") |
||||
|
} |
||||
|
|
||||
|
keypath := filepath.Join(kv.Prefix, key+"."+kv.Ext) |
||||
|
f, err := os.Open(keypath) |
||||
|
if nil == err { |
||||
|
s, err := f.Stat() |
||||
|
if nil != err { |
||||
|
return errors.New("database connection failure") |
||||
|
} |
||||
|
ts := strconv.FormatInt(s.ModTime().Unix(), 64) |
||||
|
if err := os.Rename(keypath, filepath.Join(kv.Prefix, key+"."+ts+"."+kv.Ext)); nil != err { |
||||
|
return errors.New("database connection failure") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (kv *KVDB) Vacuum() (err error) { |
||||
|
return nil |
||||
|
} |
@ -0,0 +1,63 @@ |
|||||
|
package kvdb |
||||
|
|
||||
|
import ( |
||||
|
"strings" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
type TestEntry struct { |
||||
|
Email string `json:"email"` |
||||
|
Subjects []string `json:"subjects"` |
||||
|
} |
||||
|
|
||||
|
var email = "john@example.com" |
||||
|
var sub = "id123" |
||||
|
var dbPrefix = "../testdb" |
||||
|
var testKV = &KVDB{ |
||||
|
Prefix: dbPrefix + "/test-entries", |
||||
|
Ext: "eml.json", |
||||
|
} |
||||
|
|
||||
|
func TestStore(t *testing.T) { |
||||
|
entry := &TestEntry{ |
||||
|
Email: email, |
||||
|
Subjects: []string{sub}, |
||||
|
} |
||||
|
|
||||
|
if err := testKV.Store(email, entry); nil != err { |
||||
|
t.Fatal(err) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
value, ok, err := testKV.Load(email, &(TestEntry{})) |
||||
|
if nil != err { |
||||
|
t.Fatal(err) |
||||
|
return |
||||
|
} |
||||
|
if !ok { |
||||
|
t.Fatal("test entry not found") |
||||
|
} |
||||
|
|
||||
|
v, ok := value.(*TestEntry) |
||||
|
if !ok { |
||||
|
t.Fatal("test entry not of type TestEntry") |
||||
|
} |
||||
|
|
||||
|
if email != v.Email || sub != strings.Join(v.Subjects, ",") { |
||||
|
t.Fatalf("value: %#v", v) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func TestNoExist(t *testing.T) { |
||||
|
value, ok, err := testKV.Load("not"+email, &(TestEntry{})) |
||||
|
if nil != err { |
||||
|
t.Fatal(err) |
||||
|
return |
||||
|
} |
||||
|
if ok { |
||||
|
t.Fatal("found entry that doesn't exist") |
||||
|
} |
||||
|
if value != nil { |
||||
|
t.Fatal("had value for entry that doesn't exist") |
||||
|
} |
||||
|
} |
@ -0,0 +1,61 @@ |
|||||
|
package xkeypairs |
||||
|
|
||||
|
import ( |
||||
|
"crypto/ecdsa" |
||||
|
"crypto/elliptic" |
||||
|
"crypto/rsa" |
||||
|
"io" |
||||
|
"log" |
||||
|
"math/rand" |
||||
|
|
||||
|
"git.rootprojects.org/root/keypairs" |
||||
|
) |
||||
|
|
||||
|
// KeyOptions are the things that we may need to know about a request to fulfill it properly
|
||||
|
type KeyOptions struct { |
||||
|
Key string `json:"key"` |
||||
|
KeyType string `json:"kty"` |
||||
|
Seed int64 `json:"-"` |
||||
|
SeedStr string `json:"seed"` |
||||
|
Claims Object `json:"claims"` |
||||
|
Header Object `json:"header"` |
||||
|
} |
||||
|
|
||||
|
// this shananigans is only for testing and debug API stuff
|
||||
|
func (o *KeyOptions) MyFooNextReader() io.Reader { |
||||
|
if 0 == o.Seed { |
||||
|
return RandomReader |
||||
|
} |
||||
|
return rand.New(rand.NewSource(o.Seed)) |
||||
|
} |
||||
|
|
||||
|
// GenPrivKey generates a 256-bit entropy RSA or ECDSA private key
|
||||
|
func GenPrivKey(opts *KeyOptions) keypairs.PrivateKey { |
||||
|
var privkey keypairs.PrivateKey |
||||
|
|
||||
|
if "RSA" == opts.KeyType { |
||||
|
keylen := 2048 |
||||
|
privkey, _ = rsa.GenerateKey(opts.MyFooNextReader(), keylen) |
||||
|
if 0 != opts.Seed { |
||||
|
for i := 0; i < maxRetry; i++ { |
||||
|
otherkey, _ := rsa.GenerateKey(opts.MyFooNextReader(), keylen) |
||||
|
otherCmp := otherkey.D.Cmp(privkey.(*rsa.PrivateKey).D) |
||||
|
if 0 != otherCmp { |
||||
|
// There are two possible keys, choose the lesser D value
|
||||
|
// See https://github.com/square/go-jose/issues/189
|
||||
|
if otherCmp < 0 { |
||||
|
privkey = otherkey |
||||
|
} |
||||
|
break |
||||
|
} |
||||
|
if maxRetry == i-1 { |
||||
|
log.Printf("error: coinflip landed on heads %d times", maxRetry) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
// TODO: EC keys may also suffer the same random problems in the future
|
||||
|
privkey, _ = ecdsa.GenerateKey(elliptic.P256(), opts.MyFooNextReader()) |
||||
|
} |
||||
|
return privkey |
||||
|
} |
Loading…
Reference in new issue