Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions .travis.yml

This file was deleted.

115 changes: 115 additions & 0 deletions _research/vote_storage/benchmark_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package vote_storage_test

import (
"math/rand"
"testing"

"github.com/dancannon/gorethink"
"github.com/dchest/uniuri"
)

var (
session *gorethink.Session
key2find string
table2search string
)

func init() {
var err error
session, err = gorethink.Connect(gorethink.ConnectOpts{
Address: "127.0.0.1:28015",
})
if err != nil {
panic(err)
}

key2find = uniuri.New()

// Create a new table
gorethink.Db("test").TableDrop("benchmark_keys_list").Run(session)
gorethink.Db("test").TableCreate("benchmark_keys_list").Run(session)

var klist []*KeysList

// Populate with sample data
for n := 0; n < 300; n++ {
keys := rndStringSlice(999)
keys = randomlyInsert(keys, key2find)

y := uniuri.New()
if n == 153 {
table2search = y
}

klist = append(klist, &KeysList{
ID: y,
Voted: keys,
})
}

gorethink.Db("test").Table("benchmark_keys_list").Insert(klist).Run(session)
}

func rndStringSlice(count int) []string {
var r []string
for i := 0; i < count; i++ {
r = append(r, uniuri.New())
}
return r
}

func randomlyInsert(s []string, x string) []string {
i := rand.Intn(len(s) - 1)

s = append(s[:i], append([]string{x}, s[i:]...)...)

return s
}

type KeysList struct {
ID string `gorethink:"id"`
Voted []string `gorethink:"voted"`
}

func BenchmarkContains(b *testing.B) {
for n := 0; n < b.N; n++ {
contains, err := gorethink.Db("test").Table("benchmark_keys_list").Get(table2search).Field("voted").Contains(key2find).Run(session)
if err != nil {
b.Log(err)
b.Fail()
}

var res bool
err = contains.One(&res)
if err != nil {
b.Log(err)
b.Fail()
}
if !res {
b.Log("invalid response")
b.Fail()
}
}
}

func BenchmarkAppend(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := gorethink.Db("test").Table("benchmark_keys_list").Get(table2search).Field("voted").Append(uniuri.New()).Run(session)
if err != nil {
b.Log(err)
b.Fail()
}
}
}

func BenchmarkDelete(b *testing.B) {
for n := 0; n < b.N; n++ {
_, err := gorethink.Db("test").Table("benchmark_keys_list").Get(table2search).Field("voted").DeleteAt(
gorethink.Expr(gorethink.Db("test").Table("benchmark_keys_list").Get(table2search).Field("voted").IndexesOf(key2find).AtIndex(0)),
).Run(session)
if err != nil {
b.Log(err)
b.Fail()
}
}
}
5 changes: 5 additions & 0 deletions _vagrant/Vagrantfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# redis
config.vm.network "forwarded_port", guest: 6379, host: 6379

config.vm.provider "virtualbox" do |v|
v.memory = 2048
v.cpus = 4
end

# load ansible playbook
config.vm.provision "shell", path: "deploy.sh"
end
4 changes: 2 additions & 2 deletions db/table_keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ type KeysTable struct {
RethinkCRUD
}

func (k *KeysTable) FindByName(name string) ([]*models.Key, error) {
func (k *KeysTable) FindByOwner(id string) ([]*models.Key, error) {
var results []*models.Key

if err := k.FindByAndFetch("owner_name", name, &results); err != nil {
if err := k.FindByAndFetch("owner", id, &results); err != nil {
return nil, err
}

Expand Down
12 changes: 3 additions & 9 deletions models/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,11 @@ type Account struct {
Billing BillingData `json:"billing" gorethink:"billing"`

// Password is the password used to login to the account.
// It's hashed and salted using a cryptographically strong method (bcrypt|scrypt).
// It's hashed and salted using scrypt.
Password string `json:"-" gorethink:"password"`

// PgpExpDate is an RFC3339-encoded string containing the expiry date of the user's public key
PgpExpDate string `json:"pgp_exp_date" gorethink:"pgp_exp_date"`

// PgpFingerprint is a SHA-512 hash of the user's public key
PgpFingerprint string `json:"pgp_fingerprint" gorethink:"pgp_fingerprint"`

// PgpPublicKey is a copy of the user's current public key. It can also be found in the 'keys' db.
PgpPublicKey string `json:"pgp_public_key" gorethink:"pgp_public_key"`
// PGPKey is the fingerprint of account's default key
PGPKey string `json:"pgp_key" gorethink:"pgp_key"`

// Settings contains data needed to customize the user experience.
// TODO Work in progress
Expand Down
25 changes: 12 additions & 13 deletions models/key.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
package models

type Key struct {
Resource
Expiring
Resource // ID is the fingerprint, Name is empty
Expiring // ExpiryDate is either empty or expiring, user can set it

// ID is the fingerprint
//Body []byte `json:"body" gorethink:"body"` // Raw key contents

Image []byte `json:"image" gorethink:"image"`

// the actual key
Key string `json:"key" gorethink:"key"`

OwnerName string `json:"owner_name" gorethink:"owner_name"`

// the actual id
KeyID string `json:"key_id" gorethink:"key_id"`
KeyIDShort string `json:"key_id_short" gorethink:"key_id_short"`
Headers map[string]string `json:"headers" gorethink:"headers"` // Headers passed with the key
Email string `json:"email" gorethink:"email"` // Address associated with the key
Algorithm string `json:"algorithm" gorethink:"algorithm"` // Algorithm of the key
Length uint16 `json:"length" gorethink:"length"` // Length of the key
Key string `json:"key" gorethink:"key"` // Armor-encoded key
KeyID string `json:"key_id" gorethink:"key_id"` // PGP key ID
KeyIDShort string `json:"key_id_short" gorethink:"key_id_short"` // Shorter version of above
Reliability int `json:"reliability" gorethink:"reliability"` // Reliability algorithm cached result
MasterKey string `json:"master_key" gorethink:"mater_key"` // MasterKey's ID - no idea how it works
}
46 changes: 37 additions & 9 deletions routes/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/zenazn/goji/web"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"

"github.com/lavab/api/env"
"github.com/lavab/api/models"
Expand All @@ -34,8 +35,17 @@ func KeysList(w http.ResponseWriter, r *http.Request) {
return
}

account, err := env.Accounts.FindAccountByName(user)
if err != nil {
utils.JSONResponse(w, 409, &KeysListResponse{
Success: false,
Message: "Invalid username",
})
return
}

// Find all keys owner by user
keys, err := env.Keys.FindByName(user)
keys, err := env.Keys.FindByOwner(account.ID)
if err != nil {
utils.JSONResponse(w, 500, &KeysListResponse{
Success: false,
Expand All @@ -59,8 +69,7 @@ func KeysList(w http.ResponseWriter, r *http.Request) {

// KeysCreateRequest contains the data passed to the KeysCreate endpoint.
type KeysCreateRequest struct {
Key string `json:"key" schema:"key"` // gpg armored key
Image string `json:"image" schema:"image"` // todo
Key string `json:"key" schema:"key"` // gpg armored key
}

// KeysCreateResponse contains the result of the KeysCreate request.
Expand Down Expand Up @@ -105,6 +114,21 @@ func KeysCreate(c web.C, w http.ResponseWriter, r *http.Request) {
return
}

// Parse using armor pkg
block, err := armor.Decode(strings.NewReader(input.Key))
if err != nil {
utils.JSONResponse(w, 409, &KeysCreateResponse{
Success: false,
Message: "Invalid key format",
})

env.Log.WithFields(logrus.Fields{
"error": err,
"list": entityList,
}).Warn("Cannot parse an armored key #2")
return
}

// Get the account from db
account, err := env.Accounts.GetAccount(session.Owner)
if err != nil {
Expand Down Expand Up @@ -132,17 +156,21 @@ func KeysCreate(c web.C, w http.ResponseWriter, r *http.Request) {
// Allocate a new key
key := &models.Key{
Resource: models.MakeResource(
session.Owner,
account.ID,
fmt.Sprintf(
"%d/%s public key",
"%s/%d/%s",
utils.GetAlgorithmName(publicKey.PrimaryKey.PubKeyAlgo),
bitLength,
publicKey.PrimaryKey.KeyIdString(),
),
),
OwnerName: account.Name,
Key: input.Key,
KeyID: publicKey.PrimaryKey.KeyIdString(),
KeyIDShort: publicKey.PrimaryKey.KeyIdShortString(),
Headers: block.Header,
Algorithm: utils.GetAlgorithmName(publicKey.PrimaryKey.PubKeyAlgo),
Length: bitLength,
Key: input.Key,
KeyID: publicKey.PrimaryKey.KeyIdString(),
KeyIDShort: publicKey.PrimaryKey.KeyIdShortString(),
Reliability: 0,
}

// Update id as we can't do it directly during allocation
Expand Down
21 changes: 21 additions & 0 deletions utils/pgp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package utils

import "golang.org/x/crypto/openpgp/packet"

// GetAlgorithmName returns algorithm's name depending on its ID
func GetAlgorithmName(id packet.PublicKeyAlgorithm) string {
switch id {
case 1, 2, 3:
return "RSA"
case 16:
return "ElGamal"
case 17:
return "DSA"
case 18:
return "ECDH"
case 19:
return "ECDSA"
default:
return "unknown"
}
}