From 4d3292e1031038da9ca26af8eae8ff690b786f9c Mon Sep 17 00:00:00 2001 From: meskio <meskio@sindominio.net> Date: Mon, 28 Sep 2020 21:00:35 +0200 Subject: [PATCH] Add admin authentication --- api/api.go | 21 +++++++++++---------- api/auth.go | 40 +++++++++++++++++++++++++++++++++++++++- api/auth_test.go | 1 + api/member.go | 10 ++++++++-- api/transaction.go | 8 +++++++- src/Dashboard.js | 2 +- src/Head.js | 1 - 7 files changed, 67 insertions(+), 16 deletions(-) diff --git a/api/api.go b/api/api.go index 6c32111..065d611 100644 --- a/api/api.go +++ b/api/api.go @@ -36,21 +36,22 @@ func Init(dbPath string, signKey string, r *mux.Router) error { r.HandleFunc("/signin", a.SignIn).Methods("POST") - r.HandleFunc("/member", a.auth(a.ListMembers)).Methods("GET") - r.HandleFunc("/member", a.auth(a.AddMember)).Methods("POST") - r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.GetMember)).Methods("GET") - r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.UpdateMember)).Methods("PUT") - r.HandleFunc("/member/{num:[0-9]+}", a.auth(a.DeleteMember)).Methods("DELETE") - r.HandleFunc("/member/{num:[0-9]+}/purchase", a.auth(a.GetMemberTransactions)).Methods("GET") + r.HandleFunc("/member", a.authAdmin(a.ListMembers)).Methods("GET") + r.HandleFunc("/member", a.authAdmin(a.AddMember)).Methods("POST") + r.HandleFunc("/member/me", a.authNum(a.getMemberNum)).Methods("GET") + r.HandleFunc("/member/{num:[0-9]+}", a.authAdmin(a.GetMember)).Methods("GET") + r.HandleFunc("/member/{num:[0-9]+}", a.authAdmin(a.UpdateMember)).Methods("PUT") + r.HandleFunc("/member/{num:[0-9]+}", a.authAdmin(a.DeleteMember)).Methods("DELETE") + r.HandleFunc("/member/{num:[0-9]+}/purchase", a.authAdmin(a.GetMemberTransactions)).Methods("GET") r.HandleFunc("/product", a.auth(a.ListProducts)).Methods("GET") - r.HandleFunc("/product", a.auth(a.AddProduct)).Methods("POST") + r.HandleFunc("/product", a.authAdmin(a.AddProduct)).Methods("POST") r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.GetProduct)).Methods("GET") - r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.UpdateProduct)).Methods("PUT") - r.HandleFunc("/product/{code:[0-9]+}", a.auth(a.DeleteProduct)).Methods("DELETE") + r.HandleFunc("/product/{code:[0-9]+}", a.authAdmin(a.UpdateProduct)).Methods("PUT") + r.HandleFunc("/product/{code:[0-9]+}", a.authAdmin(a.DeleteProduct)).Methods("DELETE") r.HandleFunc("/transaction", a.auth(a.ListTransactions)).Methods("GET") - r.HandleFunc("/transaction/{id:[0-9]+}", a.auth(a.GetTransaction)).Methods("GET") + r.HandleFunc("/transaction/{id:[0-9]+}", a.authNumRole(a.GetTransaction)).Methods("GET") r.HandleFunc("/transaction/mine", a.authNum(a.getTransactionsByMember)).Methods("GET") r.HandleFunc("/purchase", a.authNum(a.AddPurchase)).Methods("POST") diff --git a/api/auth.go b/api/auth.go index 7f8e996..b8c747b 100644 --- a/api/auth.go +++ b/api/auth.go @@ -75,7 +75,6 @@ func (a *api) authNum(fn func(int, http.ResponseWriter, *http.Request)) func(htt token := req.Header.Get("x-authentication") ok, claims := a.validateToken(token) if !ok { - log.Print("foo") w.WriteHeader(http.StatusUnauthorized) return } @@ -88,6 +87,45 @@ func (a *api) authNum(fn func(int, http.ResponseWriter, *http.Request)) func(htt } } +func (a *api) authAdmin(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 || role != "admin" { + w.WriteHeader(http.StatusUnauthorized) + return + } + fn(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") + ok, claims := a.validateToken(token) + if !ok { + w.WriteHeader(http.StatusUnauthorized) + return + } + num, ok := claims["num"].(float64) + if !ok { + w.WriteHeader(http.StatusUnauthorized) + return + } + role, ok := claims["role"].(string) + if !ok { + w.WriteHeader(http.StatusUnauthorized) + return + } + fn(int(num), role, w, req) + } +} + func (a *api) newToken(num int, role string) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{ "num": num, diff --git a/api/auth_test.go b/api/auth_test.go index 534937f..3f5dbef 100644 --- a/api/auth_test.go +++ b/api/auth_test.go @@ -15,6 +15,7 @@ func TestSignIn(t *testing.T) { } member.Num = 10 member.Name = "foo" + member.Role = "admin" member.Password = "password" resp := tapi.do("POST", "/member", member, nil) if resp.StatusCode != http.StatusCreated { diff --git a/api/member.go b/api/member.go index 60fa3e5..b274b90 100644 --- a/api/member.go +++ b/api/member.go @@ -4,6 +4,7 @@ import ( "encoding/json" "log" "net/http" + "strconv" "github.com/gorilla/mux" "gorm.io/gorm" @@ -81,14 +82,19 @@ func (a *api) ListMembers(w http.ResponseWriter, req *http.Request) { func (a *api) GetMember(w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) + num, _ := strconv.Atoi(vars["num"]) + a.getMemberNum(num, w, req) +} + +func (a *api) getMemberNum(num int, w http.ResponseWriter, req *http.Request) { var member Member - err := a.db.Where("num = ?", vars["num"]).First(&member).Error + err := a.db.Where("num = ?", num).First(&member).Error if err != nil { if err.Error() == "record not found" { w.WriteHeader(http.StatusNotFound) return } - log.Printf("Can't get member %s: %v", vars["num"], err) + log.Printf("Can't get member %d: %v", num, err) w.WriteHeader(http.StatusInternalServerError) return } diff --git a/api/transaction.go b/api/transaction.go index ee73e30..59100dd 100644 --- a/api/transaction.go +++ b/api/transaction.go @@ -42,7 +42,7 @@ func (a *api) ListTransactions(w http.ResponseWriter, req *http.Request) { } } -func (a *api) GetTransaction(w http.ResponseWriter, req *http.Request) { +func (a *api) GetTransaction(num int, role string, w http.ResponseWriter, req *http.Request) { vars := mux.Vars(req) var transaction Transaction err := a.db.Preload("Purchase.Product"). @@ -57,6 +57,12 @@ func (a *api) GetTransaction(w http.ResponseWriter, req *http.Request) { w.WriteHeader(http.StatusInternalServerError) return } + + if transaction.MemberNum != num && role != "admin" { + w.WriteHeader(http.StatusUnauthorized) + return + } + w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) err = json.NewEncoder(w).Encode(transaction) diff --git a/src/Dashboard.js b/src/Dashboard.js index c8b9aa0..d6ebe60 100644 --- a/src/Dashboard.js +++ b/src/Dashboard.js @@ -19,7 +19,7 @@ class Dashboard extends React.Component { render() { return ( <Fetcher - url={"/api/member/"+this.context.num.toString()} + url={"/api/member/me"} onFetch={member => this.setState({ balance: member.balance, name: member.name})} > <Container> <Row> diff --git a/src/Head.js b/src/Head.js index 7422b9a..fe5da85 100644 --- a/src/Head.js +++ b/src/Head.js @@ -13,7 +13,6 @@ function Head(props) { <Nav className="mr-auto"> <Nav.Link href="/">Dashboard</Nav.Link> <Nav.Link href="/products">Productos</Nav.Link> - <Nav.Link href="/members">Socias</Nav.Link> </Nav> <Form inline> <Button -- GitLab