changed public id format to be 128 bit length.

This commit is contained in:
didier amyot committed 2015-07-05 18:16:40 -04:00
1 parent 78d50d9a6d
commit d2cf9d7498
11 files changed
+187 -22

No files matched your search

+2 -2
View File
@@ -6,13 +6,13 @@ import (
) )
func GetAllDoormen(w http.ResponseWriter, request *http.Request) { func GetAllDoormen(w http.ResponseWriter, request *http.Request) {
fmt.Fprint(w, `{"doormen" :[`)
doormenIds := conn.GetAllDoormen() doormenIds := conn.GetAllDoormen()
buff := <-doormenIds buff := <-doormenIds
if buff == nil { if buff == nil {
fmt.Fprint(w, "]}") fmt.Fprint(w, `{"doormen" :[]}`)
return return
} }
fmt.Fprint(w, `{"doormen" :[`)
for doormanId := range doormenIds { for doormanId := range doormenIds {
fmt.Fprint(w, getDoormenIdsAsJson(buff)+`,`) fmt.Fprint(w, getDoormenIdsAsJson(buff)+`,`)
buff = doormanId buff = doormanId
+1 -1
View File
@@ -29,7 +29,7 @@ func TestGetAllDoormen(t *testing.T) {
GetAllDoormen(w, req) GetAllDoormen(w, req)
d := json.NewDecoder(w.Body) d := json.NewDecoder(w.Body)
m := make(map[string][]*doormen.DoormanDefinition) m := make(map[string][]*doormen.DoormanId)
if err := d.Decode(&m); err != nil { if err := d.Decode(&m); err != nil {
t.Error(err) t.Error(err)
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/didiercrunch/doormanserver/doormen" "github.com/didiercrunch/doormanserver/doormen"
"github.com/didiercrunch/doormanserver/shared"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"net/http" "net/http"
) )
@@ -30,6 +31,6 @@ func CreateDoorman(w http.ResponseWriter, request *http.Request) {
return return
} }
go publisher.Emit(wdef.Id, wdef) go publisher.Emit(wdef.Id, wdef)
w.Header().Set("location", "/api/doormen/"+wdef.Id.Hex()) w.Header().Set("location", "/api/doormen/"+shared.ObjectIdToPublicId(wdef.Id))
w.WriteHeader(201) w.WriteHeader(201)
} }
+7 -1
View File
@@ -3,6 +3,7 @@ package api
import ( import (
"bytes" "bytes"
"github.com/didiercrunch/doormanserver/inmemoryconnector" "github.com/didiercrunch/doormanserver/inmemoryconnector"
"github.com/didiercrunch/doormanserver/shared"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@@ -11,7 +12,12 @@ import (
) )
func GetIdFromLocation(p string) bson.ObjectId { func GetIdFromLocation(p string) bson.ObjectId {
return bson.ObjectIdHex(path.Base(p)) if id, err := shared.PublicIdToObjectId(path.Base(p)); err != nil {
panic(err)
} else {
return id
}
} }
func TestCreateDoorman(t *testing.T) { func TestCreateDoorman(t *testing.T) {
+5 -3
View File
@@ -3,6 +3,7 @@ package api
import ( import (
"fmt" "fmt"
"github.com/didiercrunch/doormanserver/doormen" "github.com/didiercrunch/doormanserver/doormen"
"github.com/didiercrunch/doormanserver/shared"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"log" "log"
@@ -10,15 +11,16 @@ import (
) )
func getDoormenIdsAsJson(id *doormen.DoormanId) string { func getDoormenIdsAsJson(id *doormen.DoormanId) string {
return fmt.Sprintf(`{"name": "%v", "id": "%v", "url": "/api/doormen/%v"}`, id.Name, id.Id.Hex(), id.Id.Hex()) publicId := shared.ObjectIdToPublicId(id.Id)
return fmt.Sprintf(`{"name": "%v", "id": "%v", "url": "/api/doormen/%v"}`, id.Name, publicId, publicId)
} }
func getDoormanIdFromRequest(w http.ResponseWriter, request *http.Request) bson.ObjectId { func getDoormanIdFromRequest(w http.ResponseWriter, request *http.Request) bson.ObjectId {
id := mux.Vars(request)["id"] id := mux.Vars(request)["id"]
if !bson.IsObjectIdHex(id) { if id, err := shared.PublicIdToObjectId(id); err != nil {
return "" return ""
} else { } else {
return bson.ObjectIdHex(id) return id
} }
} }
+4 -9
View File
@@ -17,11 +17,6 @@ type DoormanValue struct {
Probability *big.Rat `json:"probability"` Probability *big.Rat `json:"probability"`
} }
type doormanValue struct {
Name string
Probability string
}
type DoormanId struct { type DoormanId struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id"` Id bson.ObjectId `json:"id,omitempty" bson:"_id"`
Name string `json:"name" bson:"name"` Name string `json:"name" bson:"name"`
@@ -35,10 +30,10 @@ func (wId *DoormanId) AsJson() string {
} }
type DoormanDefinition struct { type DoormanDefinition struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id"` Id bson.ObjectId `bson:"_id"`
Name string `json:"name" bson:"name"` Name string `bson:"name"`
Values []*DoormanValue `json:"values" bson:"values"` Values []*DoormanValue `bson:"values"`
OwnerEmails []string `json:"emails" bson:"emails"` OwnerEmails []string `bson:"emails"`
} }
func NewDoormanDefinition(name string, ownerEmails ...string) *DoormanDefinition { func NewDoormanDefinition(name string, ownerEmails ...string) *DoormanDefinition {
+64
View File
@@ -1,10 +1,17 @@
package doormen package doormen
import ( import (
"encoding/json"
"github.com/didiercrunch/doormanserver/shared"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"math/big" "math/big"
) )
type doormanValue struct {
Name string
Probability string
}
func (val *DoormanValue) GetBSON() (interface{}, error) { func (val *DoormanValue) GetBSON() (interface{}, error) {
ret := &doormanValue{val.Name, ""} ret := &doormanValue{val.Name, ""}
txt, err := val.Probability.MarshalText() txt, err := val.Probability.MarshalText()
@@ -24,3 +31,60 @@ func (val *DoormanValue) SetBSON(raw bson.Raw) error {
val.Probability = new(big.Rat) val.Probability = new(big.Rat)
return val.Probability.UnmarshalText([]byte(v.Probability)) return val.Probability.UnmarshalText([]byte(v.Probability))
} }
type jsonDoormanDefinition struct {
Id string `json:"id,omitempty"`
Name string `json:"name"`
Values []*DoormanValue `json:"values"`
OwnerEmails []string `json:"emails"`
}
func (dm *DoormanDefinition) MarshalJSON() ([]byte, error) {
jdm := new(jsonDoormanDefinition)
jdm.Name = dm.Name
jdm.OwnerEmails = dm.OwnerEmails
jdm.Values = dm.Values
jdm.Id = shared.ObjectIdToPublicId(dm.Id)
return json.Marshal(jdm)
}
func (dm *DoormanDefinition) UnmarshalJSON(data []byte) error {
jsm := new(jsonDoormanDefinition)
var err error
if err = json.Unmarshal(data, jsm); err != nil {
return err
}
if dm.Id, err = shared.PublicIdToObjectId(jsm.Id); err != nil {
return err
}
dm.Name = jsm.Name
dm.Values = jsm.Values
dm.OwnerEmails = jsm.OwnerEmails
return nil
}
type jsonDoormanId struct {
Name string `json:"name"`
Id string `json:"id"`
}
func (did *DoormanId) MarshalJSON() ([]byte, error) {
jdid := new(jsonDoormanId)
jdid.Name = did.Name
jdid.Id = shared.ObjectIdToPublicId(did.Id)
return json.Marshal(jdid)
}
func (did *DoormanId) UnmarshalJSON(data []byte) error {
jdid := new(jsonDoormanId)
if err := json.Unmarshal(data, jdid); err != nil {
return err
}
var err error
if did.Id, err = shared.PublicIdToObjectId(jdid.Id); err != nil {
return err
}
did.Name = jdid.Name
return nil
}
+53
View File
@@ -1,6 +1,7 @@
package doormen package doormen
import ( import (
"encoding/base64"
"encoding/json" "encoding/json"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"reflect" "reflect"
@@ -22,6 +23,42 @@ func TestJsonEncoding(t *testing.T) {
} }
} }
func TestJsonEncodingWithEmptyIds(t *testing.T) {
dmd := QuickNewDoormanDefinition("test json", "1/4", "3/4")
dmd.Id = ""
data, err := json.Marshal(dmd)
if err != nil {
t.Error(err)
}
dmd2 := new(DoormanDefinition)
if err = json.Unmarshal(data, dmd2); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(dmd2, dmd) {
t.Error()
}
}
func TestJsonIdFormatToBeCompatibleWithSiphashKey(t *testing.T) {
dmd := QuickNewDoormanDefinition("test json", "1/4", "3/4")
data, err := json.Marshal(dmd)
if err != nil {
t.Error(err)
return
}
m := make(map[string]interface{})
if err = json.Unmarshal(data, &m); err != nil {
t.Error(err)
return
}
if b, err := base64.URLEncoding.DecodeString(m["id"].(string)); err != nil {
t.Error(err)
return
} else if len(b) != 16 {
t.Error("siphash requires a 16 bytes (128 bits) key not ", len(b))
}
}
func TestBsonEncoding(t *testing.T) { func TestBsonEncoding(t *testing.T) {
dmd := QuickNewDoormanDefinition("test bson", "1/4", "3/4") dmd := QuickNewDoormanDefinition("test bson", "1/4", "3/4")
data, err := bson.Marshal(dmd) data, err := bson.Marshal(dmd)
@@ -36,3 +73,19 @@ func TestBsonEncoding(t *testing.T) {
t.Error() t.Error()
} }
} }
func TestDoormanIdJsonEncoding(t *testing.T) {
did := &DoormanId{bson.NewObjectId(), "hahaha"}
data, err := json.Marshal(did)
if err != nil {
t.Error(err)
}
did2 := new(DoormanId)
if err = json.Unmarshal(data, did2); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(did2, did) {
t.Error()
}
}
+2 -2
View File
@@ -17,7 +17,7 @@ func GetAddressToServe() string {
return params.Host + ":" + strconv.Itoa(params.Port) return params.Host + ":" + strconv.Itoa(params.Port)
} }
func serveStatic2(w http.ResponseWriter, request *http.Request) { func serveStatic(w http.ResponseWriter, request *http.Request) {
filepath := request.URL.Path filepath := request.URL.Path
w.Header().Set("Cache-Control", "private, max-age=0, no-cache") w.Header().Set("Cache-Control", "private, max-age=0, no-cache")
http.ServeFile(w, request, path.Join(STATIC_DIR_PATH, filepath)) http.ServeFile(w, request, path.Join(STATIC_DIR_PATH, filepath))
@@ -26,7 +26,7 @@ func serveStatic2(w http.ResponseWriter, request *http.Request) {
func main() { func main() {
mux := http.NewServeMux() mux := http.NewServeMux()
mux.Handle("/api/", api.Create("/api")) mux.Handle("/api/", api.Create("/api"))
mux.HandleFunc("/", serveStatic2) mux.HandleFunc("/", serveStatic)
address := GetAddressToServe() address := GetAddressToServe()
fmt.Println("server running at", address) fmt.Println("server running at", address)
+1 -1
View File
@@ -69,7 +69,7 @@ func (p *Params) InsertDefaultValues() *Params {
} }
func LoadFromYamlFile(fileName string) (*Params, error) { func LoadFromYamlFile(fileName string) (*Params, error) {
if !Exists(fileName) { if !exists(fileName) {
return new(Params).InsertDefaultValues(), nil return new(Params).InsertDefaultValues(), nil
} }
data, err := ioutil.ReadFile(fileName) data, err := ioutil.ReadFile(fileName)
+46 -2
View File
@@ -1,8 +1,17 @@
package shared package shared
import "os" import (
"crypto/subtle"
"encoding/base64"
"errors"
"github.com/dchest/siphash"
"gopkg.in/mgo.v2/bson"
"os"
)
func Exists(name string) bool { var idHashKey []byte = []byte("semi-private-key")
func exists(name string) bool {
if _, err := os.Stat(name); err != nil { if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
return false return false
@@ -10,3 +19,38 @@ func Exists(name string) bool {
} }
return true return true
} }
func validateId(id []byte) error {
if len(id) != 16 {
return errors.New("valid ids are 16 bytes (126 bit) long.")
}
h := siphash.New(idHashKey)
h.Write(id[4:16])
if subtle.ConstantTimeCompare(h.Sum(nil)[0:4], id[0:4]) != 1 {
return errors.New("invalid id checksum")
}
return nil
}
func ObjectIdToPublicId(id bson.ObjectId) string {
if id == "" {
return ""
}
h := siphash.New(idHashKey)
h.Write([]byte(id))
return base64.URLEncoding.EncodeToString(append(h.Sum(nil)[0:4], []byte(id)...))
}
func PublicIdToObjectId(id string) (bson.ObjectId, error) {
if id == "" {
return "", nil
}
cypher, err := base64.URLEncoding.DecodeString(id)
if err != nil {
return "", err
}
if err := validateId(cypher); err != nil {
return "", err
}
return bson.ObjectId(cypher[4:16]), nil
}