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