Add UUID utils

This commit is contained in:
Daniel 2020-05-26 10:16:02 +02:00
parent ae14db9b23
commit 9283b00621
3 changed files with 117 additions and 4 deletions

View file

@ -8,8 +8,7 @@ import (
"github.com/safing/portbase/database"
"github.com/safing/portbase/database/record"
"github.com/safing/portbase/log"
"github.com/gofrs/uuid"
"github.com/safing/portbase/utils"
)
// Notification types
@ -96,7 +95,7 @@ func notify(nType uint8, id string, msg string, actions ...Action) *Notification
}
if id == "" {
id = uuid.NewV4().String()
id = utils.DerivedInstanceUUID(msg).String()
}
n := Notification{
@ -121,7 +120,7 @@ func (n *Notification) Save() *Notification {
n.Created = time.Now().Unix()
}
if n.GUID == "" {
n.GUID = uuid.NewV4().String()
n.GUID = utils.RandomUUID(n.ID).String()
}
// make ack notification if there are no defined actions

45
utils/uuid.go Normal file
View file

@ -0,0 +1,45 @@
package utils
import (
"encoding/binary"
"time"
"github.com/gofrs/uuid"
)
var (
constantUUID = uuid.Must(uuid.FromString("e8dba9f7-21e2-4c82-96cb-6586922c6422"))
instanceUUID = RandomUUID("instance")
)
// RandomUUID returns a new random UUID with optionally provided ns
func RandomUUID(ns string) uuid.UUID {
randUUID, err := uuid.NewV4()
switch {
case err != nil:
// fallback
// should practically never happen
return uuid.NewV5(uuidFromTime(), ns)
case ns != "":
// mix ns into the UUID
return uuid.NewV5(randUUID, ns)
default:
return randUUID
}
}
// DerivedUUID returns a new UUID that is derived from the input only, and therefore is always reproducible.
func DerivedUUID(input string) uuid.UUID {
return uuid.NewV5(constantUUID, input)
}
// DerivedInstanceUUID returns a new UUID that is derived from the input, but is unique per instance (execution) and therefore is only reproducible with the same process.
func DerivedInstanceUUID(input string) uuid.UUID {
return uuid.NewV5(instanceUUID, input)
}
func uuidFromTime() uuid.UUID {
var timeUUID uuid.UUID
binary.LittleEndian.PutUint64(timeUUID[:], uint64(time.Now().UnixNano()))
return timeUUID
}

69
utils/uuid_test.go Normal file
View file

@ -0,0 +1,69 @@
package utils
import (
"testing"
"time"
"github.com/gofrs/uuid"
)
func TestUUID(t *testing.T) {
// check randomness
a := RandomUUID("")
a2 := RandomUUID("")
if a.String() == a2.String() {
t.Error("should not match")
}
// check with input
b := RandomUUID("b")
b2 := RandomUUID("b")
if b.String() == b2.String() {
t.Error("should not match")
}
// check with long input
c := RandomUUID("TG8UkxS+4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE")
c2 := RandomUUID("TG8UkxS+4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE4rVrDxHtDAaNab1CBpygzmX1g5mJA37jbQ5q2uE")
if c.String() == c2.String() {
t.Error("should not match")
}
// check for nanosecond precision
d := uuidFromTime()
time.Sleep(2 * time.Nanosecond)
d2 := uuidFromTime()
if d.String() == d2.String() {
t.Error("should not match")
}
// check mixing
timeUUID := uuidFromTime()
e := uuid.NewV5(timeUUID, "e")
e2 := uuid.NewV5(timeUUID, "e2")
if e.String() == e2.String() {
t.Error("should not match")
}
// check deriving
f := DerivedUUID("f")
f2 := DerivedUUID("f")
f3 := DerivedUUID("f3")
if f.String() != f2.String() {
t.Error("should match")
}
if f.String() == f3.String() {
t.Error("should not match")
}
// check instance deriving
g := DerivedInstanceUUID("g")
g2 := DerivedInstanceUUID("g")
g3 := DerivedInstanceUUID("g3")
if g.String() != g2.String() {
t.Error("should match")
}
if g.String() == g3.String() {
t.Error("should not match")
}
}