108 lines
2.1 KiB
Go
108 lines
2.1 KiB
Go
package main
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"encoding/base64"
|
|
"encoding/gob"
|
|
"errors"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
type user struct {
|
|
dhtVal []byte
|
|
phase int64
|
|
servers []string
|
|
log []string
|
|
nextIndex map[string]int
|
|
}
|
|
|
|
var users map[string]user
|
|
|
|
// Verify that a body was signed by this ID
|
|
func verify(id string, body []byte) error {
|
|
b, err := base64.RawURLEncoding.DecodeString(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(body) < ed25519.SignatureSize {
|
|
return errors.New("body too short")
|
|
}
|
|
message := body[:len(body)-ed25519.SignatureSize]
|
|
sig := body[len(body)-ed25519.SignatureSize:]
|
|
if !ed25519.Verify(ed25519.PublicKey(b), message, sig) {
|
|
return errors.New("signature verification failed")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Persist a user's data to disk
|
|
func persist(id string) {
|
|
writer, err := os.OpenFile(dataDir+"/"+id+"/gob", os.O_CREATE|os.O_WRONLY, 0644)
|
|
if err != nil {
|
|
return
|
|
}
|
|
enc := gob.NewEncoder(writer)
|
|
enc.Encode(users[id])
|
|
}
|
|
|
|
// Reconfigure a user based on a DHT get
|
|
func reconfigure(id string, dhtVal []byte) {
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
|
|
user := users[id]
|
|
if timestamp(dhtVal) < timestamp(user.dhtVal) {
|
|
return
|
|
}
|
|
|
|
user.dhtVal = dhtVal
|
|
servers := strings.Split(string(dhtVal[8:len(dhtVal)-ed25519.SignatureSize]), "\n")
|
|
if servers[0] == me {
|
|
if user.nextIndex == nil {
|
|
user.nextIndex = make(map[string]int)
|
|
}
|
|
for _, server := range servers {
|
|
if _, ok := user.nextIndex[server]; !ok {
|
|
user.nextIndex[server] = len(user.log)
|
|
go replicate(id, server)
|
|
}
|
|
}
|
|
}
|
|
inServers := false
|
|
for _, server := range servers {
|
|
if server == me {
|
|
inServers = true
|
|
}
|
|
}
|
|
persist(id)
|
|
if !inServers {
|
|
delete(users, id)
|
|
_ = os.RemoveAll(id)
|
|
}
|
|
}
|
|
|
|
// Handle user configuration changes
|
|
func userHandler(w http.ResponseWriter, r *http.Request) {
|
|
id := r.URL.Path[6:]
|
|
// Resolve ID to server list
|
|
val := dhtGet(id, false)
|
|
if verify(id, val) != nil {
|
|
w.WriteHeader(http.StatusNotFound)
|
|
return
|
|
}
|
|
mu.Lock()
|
|
if _, ok := users[id]; !ok {
|
|
// Add user
|
|
users[id] = user{dhtVal: val}
|
|
os.Mkdir(dataDir+"/"+id, 0755)
|
|
persist(id)
|
|
}
|
|
mu.Unlock()
|
|
|
|
reconfigure(id, val)
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
}
|