Checkpoint 4

This commit is contained in:
Arti Zirk 2023-07-30 01:03:52 +03:00
parent 172dbcc210
commit 9116635efc
5 changed files with 208 additions and 17 deletions

1
.env
View File

@ -1,4 +1,5 @@
KDOORPI_API_ALLOWED=http://127.0.0.1:3333/allowed
KDOORPI_API_LONGPOLL=http://127.0.0.1:3333/longpoll
KDOORPI_API_SWIPE=http://127.0.0.1:3333/cardswipe
KDOORPI_API_KEY=keykey
KDOORPI_MOCK_HW=true

View File

@ -31,6 +31,7 @@ type cardList struct {
AllowedUids []struct {
Token card `json:"token"`
} `json:"allowed_uids"`
KeepOpenUntil *time.Time `json:"keep_open_until,omitempty"`
}
type ValidUids map[string]bool // bool has no meaning
@ -48,10 +49,16 @@ type Config struct {
}
}
type KeepDoorOpen struct {
until time.Time
timer *time.Timer
}
var config Config
var globalLock sync.Mutex
var validUids ValidUids
var wiegand Wiegand
var keepDoorOpen KeepDoorOpen
func main() {
@ -83,6 +90,9 @@ func setup() {
if config.mock {
log.Println("MOCK mode enabled")
if config.door == "" {
config.door = "mockdoor"
}
wiegand = &WiegandMock{}
} else {
wiegand = WiegandSetup(wiegand_a, wiegand_b, wiegand_bit_timeout, solenoid)
@ -122,6 +132,12 @@ func OpenAndCloseDoor(w Wiegand) error {
if err != nil {
return err
}
if keepDoorOpen.until.After(time.Now().Add(5 * time.Second)) {
fmt.Println("Door is already open")
return nil
}
fmt.Println("Door is now open")
time.Sleep(5 * time.Second)
@ -146,16 +162,17 @@ func cardRunner(w Wiegand) {
hashedHex := hashCardUid(card)
log.Println(hashedHex)
globalLock.Lock()
ok := validUids[hashedHex]
globalLock.Unlock()
go func() {
err := sendSwipeEvent(hashedHex)
err := sendSwipeEvent(hashedHex, ok)
if err != nil {
log.Println("Failed to send swipe event: %v", err)
}
}()
globalLock.Lock()
ok := validUids[hashedHex]
globalLock.Unlock()
if ok {
log.Println("Opening door")
err := OpenAndCloseDoor(w)
@ -266,14 +283,46 @@ func reloadTokens() error {
validUids[val.Token.UidHash] = true
}
if cl.KeepOpenUntil != nil {
updateKeepOpenDoor(*cl.KeepOpenUntil)
}
return nil
}
func sendSwipeEvent(cardUidHash string) error {
func updateKeepOpenDoor(newKeepOpenTime time.Time) {
// is there one active?
if keepDoorOpen.timer != nil {
keepDoorOpen.timer.Stop()
keepDoorOpen = KeepDoorOpen{}
}
if newKeepOpenTime.After(time.Now()) {
log.Printf("Keeping door open until %v", newKeepOpenTime)
wiegand.OpenDoor()
timer := time.AfterFunc(time.Until(newKeepOpenTime), handleKeepDoorOpenCloseCleanup)
keepDoorOpen = KeepDoorOpen{
timer: timer,
until: newKeepOpenTime,
}
} else {
wiegand.CloseDoor()
}
}
func handleKeepDoorOpenCloseCleanup() {
fmt.Println("Keep door open time is reached!")
wiegand.CloseDoor()
keepDoorOpen = KeepDoorOpen{}
}
func sendSwipeEvent(cardUidHash string, success bool) error {
swipeEvent := map[string]string{
"uid_hash": cardUidHash,
"door": config.door,
"timestamp": time.Now().Format(time.DateTime),
"success": fmt.Sprint(success),
}
data, err := json.Marshal(swipeEvent)

View File

@ -3,12 +3,17 @@
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"os"
"os/signal"
"strings"
"sync"
"time"
)
@ -25,9 +30,21 @@ type cardToken struct {
}
type cardList struct {
AllowedUids []cardToken `json:"allowed_uids"`
AllowedUids []cardToken `json:"allowed_uids"`
KeepOpenUntil *time.Time `json:"keep_open_until,omitempty"`
}
var keys []cardToken = []cardToken{
//{card{UidHash: "d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}},
{card{UidHash: "873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},
{card{UidHash: "a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},
{card{UidHash: "d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},
}
var keyLock sync.RWMutex
var keepOpenUntil *time.Time
func getRoot(w http.ResponseWriter, r *http.Request) {
fmt.Printf("got / request\n")
io.WriteString(w, "This is my website!\n")
@ -35,14 +52,13 @@ func getRoot(w http.ResponseWriter, r *http.Request) {
func getAllowed(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
keyLock.RLock()
keys := cardList{
AllowedUids: []cardToken{
{card{UidHash: "d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}},
{card{UidHash: "873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},
{card{UidHash: "aabbccddeeff"}},
},
AllowedUids: keys,
KeepOpenUntil: keepOpenUntil,
}
respJson, _ := json.Marshal(keys)
keyLock.RUnlock()
_, err := w.Write(respJson)
if err != nil {
return
@ -115,10 +131,116 @@ func (doobserver *DoorBoyServer) postOpenDoor(w http.ResponseWriter, r *http.Req
}
func postCardSwipe(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Got swipe request without data")
return
}
log.Println("Swipe Request with key:", string(data))
w.Write([]byte("OK"))
}
func (doobserver *DoorBoyServer) postAddCard(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Got swipe request without data")
return
}
cardHex := string(data)
if len(cardHex) != 128 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Key invalid"))
return
}
keyLock.RLock()
found := false
for _, key := range keys {
if key.Token.UidHash == cardHex {
// key is already in there!
found = true
}
}
keyLock.RUnlock()
if found {
return
}
keyLock.Lock()
keys = append(keys, cardToken{Token: card{UidHash: cardHex}})
keyLock.Unlock()
for _, datastream := range doobserver.longPollers {
datastream <- "update"
}
log.Println("Added new key:", string(data))
}
func (doobserver *DoorBoyServer) postRemoveCard(w http.ResponseWriter, r *http.Request) {
data, err := io.ReadAll(r.Body)
if err != nil {
log.Println("Got swipe request without data")
return
}
cardHex := string(data)
if len(cardHex) != 128 {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("Key invalid"))
return
}
keyLock.Lock()
foundIndex := -1
for i, key := range keys {
if key.Token.UidHash == cardHex {
// key is already in there!
foundIndex = i
}
}
if foundIndex == -1 {
keyLock.Unlock()
return
}
if foundIndex == len(keys)-1 {
keys = keys[:foundIndex]
} else {
keys = append(keys[:foundIndex], keys[foundIndex+1:]...)
}
keyLock.Unlock()
for _, datastream := range doobserver.longPollers {
datastream <- "update"
}
log.Println("Remove key:", string(data))
}
func (doobserver *DoorBoyServer) postKeepDoorOpen(w http.ResponseWriter, r *http.Request) {
rs, _ := io.ReadAll(r.Body)
parsedTime, err := time.Parse(time.RFC3339, strings.TrimSpace(string(rs)))
if err != nil {
fmt.Println("Error with parsing time: %v", err)
w.WriteHeader(http.StatusBadRequest)
}
keepOpenUntil = &parsedTime
doobserver.postOpenDoor(w, r)
}
func main() {
fmt.Println("Running...")
var dumpKeys []cardToken
savedKeys, err := os.ReadFile("keys.json")
err = json.Unmarshal(savedKeys, &dumpKeys)
if err == nil {
for _, k := range dumpKeys {
keys = append(keys, k)
}
log.Println("Loaded keys successfully!")
}
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
defer cancel()
doorboyserver := DoorBoyServer{longPollers: map[string]chan string{}}
http.HandleFunc("/", getRoot)
http.HandleFunc("/allowed", getAllowed)
@ -126,13 +248,28 @@ func main() {
http.HandleFunc("/jspoll", doorboyserver.getLongPollJson)
http.HandleFunc("/open", doorboyserver.postOpenDoor)
http.HandleFunc("/cardswipe", postCardSwipe)
http.HandleFunc("/keepdooropen", doorboyserver.postKeepDoorOpen)
http.HandleFunc("/addcard", doorboyserver.postAddCard)
http.HandleFunc("/removecard", doorboyserver.postRemoveCard)
err := http.ListenAndServe(":3333", nil)
go func() {
err := http.ListenAndServe(":3333", nil)
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error starting server: %s\n", err)
os.Exit(1)
if errors.Is(err, http.ErrServerClosed) {
fmt.Printf("server closed\n")
} else if err != nil {
fmt.Printf("error starting server: %s\n", err)
os.Exit(1)
}
}()
<-ctx.Done()
log.Println("Shutting down!")
keyLock.Lock()
data, err := json.Marshal(keys)
if err == nil {
os.WriteFile("keys.json", data, 0600)
log.Println("Saved keys successfully!")
}
keyLock.Unlock()
}

1
godoor_server/keys.json Normal file
View File

@ -0,0 +1 @@
[{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},{"token":{"uid_hash":"d72c87d0f077c7766f2985dfab30e8955c373a13a1e93d315203939f542ff86e73ee37c31f4c4b571f4719fa8e3589f12db8dcb57ea9f56764bb7d58f64cf705"}},{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}},{"token":{"uid_hash":"873636abbcf597a4835afc1c9b16a72eb6175b7ab278a8f18ab13c50172c90ea97cc3e258efd9cc1d885d7ea32d87fd006907892793e7cd6c468417bd8b8421a"}},{"token":{"uid_hash":"a08094343b4057777af4935b79df586d7eb999117883a53393e8b46f1ab19577b12039a5d2e0b9d0364bbed5c82d83a507492fea47ace633acf23da2dcf1560e"}},{"token":{"uid_hash":"d78271547cde009726b159dca09e53bee72feebe90b3eb7cb6e394caafc30bdb1f1567efc2f19bbdf3c6922e0bebed910ee4fa4f5b13bd379651da4f620f3559"}}]

View File

@ -1,6 +1,7 @@
package main
import (
"log"
"time"
)
@ -11,11 +12,13 @@ type WiegandMock struct {
}
func (w *WiegandMock) OpenDoor() error {
log.Println("DOOR: Opened")
w.openState = true
return nil
}
func (w *WiegandMock) CloseDoor() error {
log.Println("DOOR: Closing")
w.openState = false
return nil
}