From f5255a655c039cc96b04b04dc0a98262b0474198 Mon Sep 17 00:00:00 2001
From: meskio <meskio@sindominio.net>
Date: Mon, 21 Dec 2020 19:46:10 +0100
Subject: [PATCH] Add order permitions

---
 api/api.go         |  4 ++--
 api/api_test.go    | 16 +++++++++++++++-
 api/auth.go        | 44 ++++++++++++++++++++++++++++++++++++++++++++
 api/member_test.go | 22 ++++++++++++++++++++--
 api/order_test.go  | 24 ++++++++++++------------
 5 files changed, 93 insertions(+), 17 deletions(-)

diff --git a/api/api.go b/api/api.go
index 39fafa7..a392615 100644
--- a/api/api.go
+++ b/api/api.go
@@ -57,11 +57,11 @@ func Init(dbPath string, signKey string, mail *Mail, r *mux.Router) error {
 	r.HandleFunc("/topup", a.authAdminNum(a.AddTopup)).Methods("POST")
 
 	r.HandleFunc("/order", a.auth(a.ListOrders)).Methods("GET")
-	r.HandleFunc("/order", a.authNum(a.AddOrder)).Methods("POST")
+	r.HandleFunc("/order", a.authOrderNum(a.AddOrder)).Methods("POST")
 	r.HandleFunc("/order/{id:[0-9]+}", a.authNum(a.GetOrder)).Methods("GET")
 	r.HandleFunc("/order/{id:[0-9]+}", a.authNumRole(a.DeleteOrder)).Methods("DELETE")
 	r.HandleFunc("/order/active", a.auth(a.ListActiveOrders)).Methods("GET")
-	r.HandleFunc("/order/picks", a.authNum(a.ListOrderPicks)).Methods("GET")
+	r.HandleFunc("/order/picks", a.authOrderNum(a.ListOrderPicks)).Methods("GET")
 	r.HandleFunc("/order/{id:[0-9]+}/purchase", a.authNum(a.AddOrderPurchase)).Methods("POST")
 	return nil
 }
diff --git a/api/api_test.go b/api/api_test.go
index 534fea8..a4eb323 100644
--- a/api/api_test.go
+++ b/api/api_test.go
@@ -36,6 +36,7 @@ type testAPI struct {
 	server     *httptest.Server
 	testPath   string
 	token      string
+	tokenOrder string
 	tokenAdmin string
 }
 
@@ -63,6 +64,15 @@ func newTestAPI(t *testing.T) *testAPI {
 	if err != nil {
 		t.Fatal("Can't generate token:", err)
 	}
+	tokenOrder := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
+		"num":  testMemberOrder.Num,
+		"role": "order",
+		"exp":  time.Now().Add(time.Hour * 24).Unix(),
+	})
+	tokenOrderString, err := tokenOrder.SignedString([]byte(signKey))
+	if err != nil {
+		t.Fatal("Can't generate token:", err)
+	}
 	tokenAdmin := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
 		"num":  testMemberAdmin.Num,
 		"role": "admin",
@@ -73,13 +83,17 @@ func newTestAPI(t *testing.T) *testAPI {
 		t.Fatal("Can't generate token:", err)
 	}
 
-	return &testAPI{t, server.URL, &http.Client{}, server, testPath, tokenString, tokenAdminString}
+	return &testAPI{t, server.URL, &http.Client{}, server, testPath, tokenString, tokenOrderString, tokenAdminString}
 }
 
 func (ta *testAPI) do(method string, url string, body interface{}, respBody interface{}) *http.Response {
 	return ta.doToken(ta.token, method, url, body, respBody)
 }
 
+func (ta *testAPI) doOrder(method string, url string, body interface{}, respBody interface{}) *http.Response {
+	return ta.doToken(ta.tokenOrder, method, url, body, respBody)
+}
+
 func (ta *testAPI) doAdmin(method string, url string, body interface{}, respBody interface{}) *http.Response {
 	return ta.doToken(ta.tokenAdmin, method, url, body, respBody)
 }
diff --git a/api/auth.go b/api/auth.go
index e348b95..a905c21 100644
--- a/api/auth.go
+++ b/api/auth.go
@@ -246,6 +246,50 @@ func (a *api) authAdminNum(fn func(int, http.ResponseWriter, *http.Request)) fun
 	}
 }
 
+func roleOrder(role string) bool {
+	return role == "admin" || role == "order"
+}
+
+func (a *api) authOrder(fn func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, req *http.Request) {
+		token := req.Header.Get("x-authentication")
+		ok, claims := a.validateToken(token)
+		if !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		role, ok := claims["role"].(string)
+		if !ok || !roleOrder(role) {
+			w.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		fn(w, req)
+	}
+}
+
+func (a *api) authOrderNum(fn func(int, http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
+	return func(w http.ResponseWriter, req *http.Request) {
+		token := req.Header.Get("x-authentication")
+		ok, claims := a.validateToken(token)
+		if !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		role, ok := claims["role"].(string)
+		if !ok || !roleOrder(role) {
+			log.Println(role)
+			w.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		num, ok := claims["num"].(float64)
+		if !ok {
+			w.WriteHeader(http.StatusUnauthorized)
+			return
+		}
+		fn(int(num), w, req)
+	}
+}
+
 func (a *api) authNumRole(fn func(int, string, http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, req *http.Request) {
 		token := req.Header.Get("x-authentication")
diff --git a/api/member_test.go b/api/member_test.go
index 045ebe0..10db059 100644
--- a/api/member_test.go
+++ b/api/member_test.go
@@ -9,6 +9,7 @@ import (
 
 var (
 	testMemberLogin      = "foo"
+	testMemberOrderLogin = "foobar"
 	testMemberAdminLogin = "bar"
 )
 
@@ -24,9 +25,21 @@ var testMember = db.MemberReq{
 	Password: "password",
 }
 
-var testMemberAdmin = db.MemberReq{
+var testMemberOrder = db.MemberReq{
 	Member: db.Member{
 		Num:     20,
+		Login:   &testMemberOrderLogin,
+		Name:    "FooBar Faz",
+		Email:   "foobar@example.com",
+		Role:    "order",
+		Balance: 12000,
+	},
+	Password: "password",
+}
+
+var testMemberAdmin = db.MemberReq{
+	Member: db.Member{
+		Num:     30,
 		Login:   &testMemberAdminLogin,
 		Name:    "Bar Baz",
 		Email:   "bar@example.com",
@@ -47,7 +60,7 @@ func TestMemberAddList(t *testing.T) {
 		t.Fatal("Can't get members:", resp.Status)
 	}
 
-	if len(members) != 2 {
+	if len(members) != 3 {
 		t.Fatal("Wrong number of members", len(members), members)
 	}
 	if members[0].Name != testMember.Name {
@@ -153,6 +166,11 @@ func (tapi *testAPI) addTestMember() {
 		tapi.t.Fatal("Can't create member:", resp.Status)
 	}
 
+	resp = tapi.doAdmin("POST", "/member", testMemberOrder, nil)
+	if resp.StatusCode != http.StatusCreated {
+		tapi.t.Fatal("Can't create member:", resp.Status)
+	}
+
 	resp = tapi.doAdmin("POST", "/member", testMemberAdmin, nil)
 	if resp.StatusCode != http.StatusCreated {
 		tapi.t.Fatal("Can't create member:", resp.Status)
diff --git a/api/order_test.go b/api/order_test.go
index eae2fa9..1f312e2 100644
--- a/api/order_test.go
+++ b/api/order_test.go
@@ -76,7 +76,7 @@ func TestOrderDelete(t *testing.T) {
 
 	order := testOrder
 	order.Deadline = time.Now().Add(-24 * time.Hour)
-	resp := tapi.do("POST", "/order", order, nil)
+	resp := tapi.doOrder("POST", "/order", order, nil)
 	if resp.StatusCode != http.StatusCreated {
 		t.Fatal("Can't create order:", resp.Status)
 	}
@@ -103,7 +103,7 @@ func TestOrderDelete(t *testing.T) {
 		t.Error("Deactivated wrong orders:", orders)
 	}
 
-	resp = tapi.do("DELETE", fmt.Sprintf("/order/%d", orders[0].ID), nil, nil)
+	resp = tapi.doOrder("DELETE", fmt.Sprintf("/order/%d", orders[0].ID), nil, nil)
 	if resp.StatusCode != http.StatusOK {
 		t.Fatal("Can't delete order:", resp.Status)
 	}
@@ -198,7 +198,7 @@ func TestOrderNoDeactivation(t *testing.T) {
 	order := testOrder
 	now := time.Now()
 	order.Deadline = time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, time.Local)
-	resp := tapi.do("POST", "/order", order, nil)
+	resp := tapi.doOrder("POST", "/order", order, nil)
 	if resp.StatusCode != http.StatusCreated {
 		t.Fatal("Can't create order:", resp.Status)
 	}
@@ -234,13 +234,13 @@ func TestOrderDeactivation(t *testing.T) {
 
 	order := testOrder
 	order.Deadline = time.Now().Add(-24 * time.Hour)
-	resp := tapi.do("POST", "/order", order, nil)
+	resp := tapi.doOrder("POST", "/order", order, nil)
 	if resp.StatusCode != http.StatusCreated {
 		t.Fatal("Can't create order:", resp.Status)
 	}
 
 	var orders []db.Order
-	resp = tapi.do("GET", "/order/active", nil, &orders)
+	resp = tapi.doOrder("GET", "/order/active", nil, &orders)
 	if resp.StatusCode != http.StatusOK {
 		t.Fatal("Can't get transactions:", resp.Status)
 	}
@@ -280,7 +280,7 @@ func TestOrderDeactivation(t *testing.T) {
 	total := 3 * testProduct.Price
 
 	var transactions []db.Transaction
-	resp = tapi.do("GET", "/transaction/mine", nil, &transactions)
+	resp = tapi.doOrder("GET", "/transaction/mine", nil, &transactions)
 	if resp.StatusCode != http.StatusOK {
 		t.Fatal("Can't get transactions:", resp.Status)
 	}
@@ -295,12 +295,12 @@ func TestOrderDeactivation(t *testing.T) {
 	}
 
 	var member db.Member
-	resp = tapi.do("GET", "/member/me", nil, &member)
+	resp = tapi.doOrder("GET", "/member/me", nil, &member)
 	if resp.StatusCode != http.StatusOK {
 		t.Fatal("Can't member:", resp.Status)
 	}
-	if member.Balance != testMember.Balance+total {
-		t.Fatal("Wrong member balance:", member.Balance, testMember.Balance, total)
+	if member.Balance != testMemberOrder.Balance+total {
+		t.Fatal("Wrong member balance:", member.Balance, testMemberOrder.Balance, total)
 	}
 }
 
@@ -429,13 +429,13 @@ func TestOrderPicks(t *testing.T) {
 
 	testOrderOld := testOrder
 	testOrderOld.Deadline = time.Now().Add(-24 * time.Hour)
-	resp := tapi.do("POST", "/order", testOrderOld, nil)
+	resp := tapi.doOrder("POST", "/order", testOrderOld, nil)
 	if resp.StatusCode != http.StatusCreated {
 		tapi.t.Fatal("Can't create old order:", resp.Status)
 	}
 
 	var orders []db.Order
-	resp = tapi.do("GET", "/order/picks", nil, &orders)
+	resp = tapi.doOrder("GET", "/order/picks", nil, &orders)
 	if resp.StatusCode != http.StatusOK {
 		t.Fatal("Can't get orders picks:", resp.Status)
 	}
@@ -452,7 +452,7 @@ func TestOrderPicks(t *testing.T) {
 }
 
 func (tapi *testAPI) addTestOrder() {
-	resp := tapi.do("POST", "/order", testOrder, nil)
+	resp := tapi.doOrder("POST", "/order", testOrder, nil)
 	if resp.StatusCode != http.StatusCreated {
 		tapi.t.Fatal("Can't create order:", resp.Status)
 	}
-- 
GitLab