Skip to content
Snippets Groups Projects
Commit 3e6f43b0 authored by meskio's avatar meskio :tent:
Browse files

Use login name and allow changing password

parent 90a45830
No related branches found
No related tags found
No related merge requests found
...@@ -42,6 +42,7 @@ func Init(dbPath string, signKey string, r *mux.Router) error { ...@@ -42,6 +42,7 @@ func Init(dbPath string, signKey string, r *mux.Router) error {
r.HandleFunc("/member", a.authAdmin(a.ListMembers)).Methods("GET") r.HandleFunc("/member", a.authAdmin(a.ListMembers)).Methods("GET")
r.HandleFunc("/member", a.authAdmin(a.AddMember)).Methods("POST") r.HandleFunc("/member", a.authAdmin(a.AddMember)).Methods("POST")
r.HandleFunc("/member/me", a.authNum(a.getMemberNum)).Methods("GET") r.HandleFunc("/member/me", a.authNum(a.getMemberNum)).Methods("GET")
r.HandleFunc("/member/me", a.authNum(a.UpdateMemberMe)).Methods("PUT")
r.HandleFunc("/member/{num:[0-9]+}", a.authAdmin(a.GetMember)).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.UpdateMember)).Methods("PUT")
r.HandleFunc("/member/{num:[0-9]+}", a.authAdmin(a.DeleteMember)).Methods("DELETE") r.HandleFunc("/member/{num:[0-9]+}", a.authAdmin(a.DeleteMember)).Methods("DELETE")
......
...@@ -13,7 +13,7 @@ import ( ...@@ -13,7 +13,7 @@ import (
) )
type creds struct { type creds struct {
Name string `json:"name"` Login string `json:"login"`
Password string `json:"password"` Password string `json:"password"`
NoExpire bool `json:"noExpire"` NoExpire bool `json:"noExpire"`
} }
...@@ -27,21 +27,21 @@ func (a *api) SignIn(w http.ResponseWriter, req *http.Request) { ...@@ -27,21 +27,21 @@ func (a *api) SignIn(w http.ResponseWriter, req *http.Request) {
return return
} }
var member Member var member Member
err = a.db.Where("name = ?", c.Name).First(&member).Error err = a.db.Where("login = ?", c.Login).First(&member).Error
if err != nil { if err != nil {
log.Printf("Can't locate user %s: %v", c.Name, err) log.Printf("Can't locate user %s: %v", c.Login, err)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
hash := hashPass(c.Password, member.Salt) hash := hashPass(c.Password, member.Salt)
if subtle.ConstantTimeCompare(hash, member.PassHash) == 0 { if subtle.ConstantTimeCompare(hash, member.PassHash) == 0 {
log.Printf("Invalid pass for %s", c.Name) log.Printf("Invalid pass for %s", c.Login)
w.WriteHeader(http.StatusBadRequest) w.WriteHeader(http.StatusBadRequest)
return return
} }
log.Printf("Logged in as %s", c.Name) log.Printf("Logged in as %s", c.Login)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
......
...@@ -21,14 +21,14 @@ func TestSignIn(t *testing.T) { ...@@ -21,14 +21,14 @@ func TestSignIn(t *testing.T) {
Member Member `json:"member"` Member Member `json:"member"`
} }
jsonAuth := creds{ jsonAuth := creds{
Name: testMemberAdmin.Name, Login: testMemberAdmin.Login,
Password: testMemberAdmin.Password, Password: testMemberAdmin.Password,
} }
resp = tapi.do("POST", "/signin", jsonAuth, &respMember) resp = tapi.do("POST", "/signin", jsonAuth, &respMember)
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
t.Fatal("Can't sign in:", resp.Status) t.Fatal("Can't sign in:", resp.Status)
} }
if respMember.Member.Name != testMemberAdmin.Name { if respMember.Member.Login != testMemberAdmin.Login {
t.Fatal("Unexpected member:", respMember) t.Fatal("Unexpected member:", respMember)
} }
tapi.token = respMember.Token tapi.token = respMember.Token
......
...@@ -14,7 +14,8 @@ import ( ...@@ -14,7 +14,8 @@ import (
type Member struct { type Member struct {
gorm.Model `json:"-"` gorm.Model `json:"-"`
Num int `json:"num" gorm:"unique;index"` Num int `json:"num" gorm:"unique;index"`
Name string `json:"name" gorm:"unique;index"` Login string `json:"login" gorm:"unique;index"`
Name string `json:"name"`
Email string `json:"email"` Email string `json:"email"`
Phone string `json:"phone"` Phone string `json:"phone"`
Balance int `json:"balance"` Balance int `json:"balance"`
...@@ -23,11 +24,13 @@ type Member struct { ...@@ -23,11 +24,13 @@ type Member struct {
Salt []byte `json:"-"` Salt []byte `json:"-"`
} }
type MemberReq struct {
Member
Password string `json:"password"`
}
func (a *api) AddMember(w http.ResponseWriter, req *http.Request) { func (a *api) AddMember(w http.ResponseWriter, req *http.Request) {
var memberReq struct { var memberReq MemberReq
Member
Password string `json:"password"`
}
err := json.NewDecoder(req.Body).Decode(&memberReq) err := json.NewDecoder(req.Body).Decode(&memberReq)
if err != nil { if err != nil {
log.Printf("Can't create member: %v", err) log.Printf("Can't create member: %v", err)
...@@ -36,8 +39,10 @@ func (a *api) AddMember(w http.ResponseWriter, req *http.Request) { ...@@ -36,8 +39,10 @@ func (a *api) AddMember(w http.ResponseWriter, req *http.Request) {
} }
member := Member{ member := Member{
Num: memberReq.Num, Num: memberReq.Num,
Login: memberReq.Login,
Name: memberReq.Name, Name: memberReq.Name,
Email: memberReq.Email, Email: memberReq.Email,
Phone: memberReq.Phone,
Balance: memberReq.Balance, Balance: memberReq.Balance,
Role: memberReq.Role, Role: memberReq.Role,
} }
...@@ -122,7 +127,7 @@ func (a *api) DeleteMember(w http.ResponseWriter, req *http.Request) { ...@@ -122,7 +127,7 @@ func (a *api) DeleteMember(w http.ResponseWriter, req *http.Request) {
} }
func (a *api) UpdateMember(w http.ResponseWriter, req *http.Request) { func (a *api) UpdateMember(w http.ResponseWriter, req *http.Request) {
var member Member var member MemberReq
err := json.NewDecoder(req.Body).Decode(&member) err := json.NewDecoder(req.Body).Decode(&member)
if err != nil { if err != nil {
log.Printf("Can't decode memeber: %v", err) log.Printf("Can't decode memeber: %v", err)
...@@ -131,43 +136,94 @@ func (a *api) UpdateMember(w http.ResponseWriter, req *http.Request) { ...@@ -131,43 +136,94 @@ func (a *api) UpdateMember(w http.ResponseWriter, req *http.Request) {
} }
vars := mux.Vars(req) vars := mux.Vars(req)
var dbMember Member num, err := strconv.Atoi(vars["num"])
err = a.db.Where("num = ?", vars["num"]).First(&dbMember).Error if err != nil {
log.Printf("Invalid member num %s: %v", vars["num"], err)
w.WriteHeader(http.StatusInternalServerError)
return
}
m, err := a.updateMember(num, member)
if err != nil { if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) { if errors.Is(err, gorm.ErrRecordNotFound) {
w.WriteHeader(http.StatusNotFound) w.WriteHeader(http.StatusNotFound)
return return
} }
log.Printf("Can't get member %s: %v", vars["num"], err) log.Printf("Can't update member %d: %v", num, err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
if member.Num != 0 { w.Header().Set("Content-Type", "application/json")
dbMember.Num = member.Num w.WriteHeader(http.StatusAccepted)
} err = json.NewEncoder(w).Encode(m)
if member.Name != "" { if err != nil {
dbMember.Name = member.Name log.Printf("Can't encode updated memeber: %v", err)
} w.WriteHeader(http.StatusInternalServerError)
if member.Email != "" { return
dbMember.Email = member.Email
} }
if member.Balance >= 0 { }
dbMember.Balance = member.Balance
func (a *api) UpdateMemberMe(num int, w http.ResponseWriter, req *http.Request) {
var member MemberReq
err := json.NewDecoder(req.Body).Decode(&member)
if err != nil {
log.Printf("Can't decode memeber: %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
} }
err = a.db.Save(&dbMember).Error
member.Num = 0
member.Balance = -1
m, err := a.updateMember(num, member)
if err != nil { if err != nil {
log.Printf("Can't update member %s: %v %v", vars["num"], err, member) if errors.Is(err, gorm.ErrRecordNotFound) {
w.WriteHeader(http.StatusNotFound)
return
}
log.Printf("Can't update member %d: %v", num, err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusAccepted) w.WriteHeader(http.StatusAccepted)
err = json.NewEncoder(w).Encode(dbMember) err = json.NewEncoder(w).Encode(m)
if err != nil { if err != nil {
log.Printf("Can't encode updated memeber: %v", err) log.Printf("Can't encode updated memeber: %v", err)
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
return return
} }
} }
func (a *api) updateMember(num int, member MemberReq) (Member, error) {
var dbMember Member
err := a.db.Where("num = ?", num).First(&dbMember).Error
if err != nil {
return dbMember, err
}
if member.Num != 0 {
dbMember.Num = member.Num
}
if member.Login != "" {
dbMember.Login = member.Login
}
if member.Name != "" {
dbMember.Name = member.Name
}
if member.Email != "" {
dbMember.Email = member.Email
}
if member.Balance >= 0 {
dbMember.Balance = member.Balance
}
if member.Password != "" {
dbMember.PassHash, dbMember.Salt, err = newHashPass(member.Password)
if err != nil {
return dbMember, err
}
}
err = a.db.Save(&dbMember).Error
return dbMember, err
}
...@@ -5,13 +5,11 @@ import ( ...@@ -5,13 +5,11 @@ import (
"testing" "testing"
) )
var testMember = struct { var testMember = MemberReq{
Member
Password string `json:"password"`
}{
Member: Member{ Member: Member{
Num: 10, Num: 10,
Name: "foo", Login: "foo",
Name: "Foo Baz",
Email: "foo@example.com", Email: "foo@example.com",
Role: "", Role: "",
Balance: 10000, Balance: 10000,
...@@ -19,13 +17,11 @@ var testMember = struct { ...@@ -19,13 +17,11 @@ var testMember = struct {
Password: "password", Password: "password",
} }
var testMemberAdmin = struct { var testMemberAdmin = MemberReq{
Member
Password string `json:"password"`
}{
Member: Member{ Member: Member{
Num: 20, Num: 20,
Name: "bar", Login: "bar",
Name: "Bar Baz",
Email: "bar@example.com", Email: "bar@example.com",
Role: "admin", Role: "admin",
Balance: 15000, Balance: 15000,
...@@ -47,7 +43,7 @@ func TestMemberAddList(t *testing.T) { ...@@ -47,7 +43,7 @@ func TestMemberAddList(t *testing.T) {
if len(members) != 2 { if len(members) != 2 {
t.Fatal("Wrong number of members", len(members), members) t.Fatal("Wrong number of members", len(members), members)
} }
if members[0].Name != "foo" { if members[0].Name != testMember.Name {
t.Error("Wrong name:", members[0].Name) t.Error("Wrong name:", members[0].Name)
} }
if members[0].Email != "foo@example.com" { if members[0].Email != "foo@example.com" {
...@@ -106,6 +102,37 @@ func TestMemberUpdate(t *testing.T) { ...@@ -106,6 +102,37 @@ func TestMemberUpdate(t *testing.T) {
} }
} }
func TestMemberUpdateMe(t *testing.T) {
tapi := newTestAPI(t)
defer tapi.close()
tapi.addTestMember()
member := testMember
member.Password = "foobar"
member.Email = "other@example.com"
resp := tapi.do("PUT", "/member/me", member, nil)
if resp.StatusCode != http.StatusAccepted {
t.Fatal("Can't update member:", resp.Status)
}
var gotMember Member
resp = tapi.doAdmin("GET", "/member/10", nil, &gotMember)
if resp.StatusCode != http.StatusOK {
t.Error("Can't find the member:", resp.Status)
}
if gotMember.Email != member.Email {
t.Error("Wrong email:", gotMember)
}
jsonAuth := creds{
Login: testMember.Login,
Password: member.Password,
}
resp = tapi.do("POST", "/signin", jsonAuth, nil)
if resp.StatusCode != http.StatusOK {
t.Fatal("Can't sign in:", resp.Status)
}
}
func (tapi *testAPI) addTestMember() { func (tapi *testAPI) addTestMember() {
resp := tapi.doAdmin("POST", "/member", testMember, nil) resp := tapi.doAdmin("POST", "/member", testMember, nil)
if resp.StatusCode != http.StatusCreated { if resp.StatusCode != http.StatusCreated {
......
...@@ -4,8 +4,8 @@ TOKEN="$1" ...@@ -4,8 +4,8 @@ TOKEN="$1"
ADDR="localhost:8080" ADDR="localhost:8080"
# set up members # set up members
curl -H "x-authentication: $TOKEN" -X "POST" -d '{"num": 900, "name": "admin", "email": "admin@example.com", "phone": "654321123", "password": "admin", "role": "admin", "balance": 10000}' $ADDR/api/member curl -H "x-authentication: $TOKEN" -X "POST" -d '{"num": 900, "login": "admin", "name": "Administradora", "email": "admin@example.com", "phone": "654321123", "password": "admin", "role": "admin", "balance": 10000}' $ADDR/api/member
curl -H "x-authentication: $TOKEN" -X "POST" -d '{"num": 901, "name": "socia", "email": "socia@example.com", "phone": "678900123", "password": "socia", "balance": 10000}' $ADDR/api/member curl -H "x-authentication: $TOKEN" -X "POST" -d '{"num": 901, "login": "socia", "name": "Socia Aicos", "email": "socia@example.com", "phone": "678900123", "password": "socia", "balance": 10000}' $ADDR/api/member
# set up products # set up products
curl -H "x-authentication: $TOKEN" -X "POST" -d '{"code": 234, "name": "aceite", "price": 1700, "stock": 35}' $ADDR/api/product curl -H "x-authentication: $TOKEN" -X "POST" -d '{"code": 234, "name": "aceite", "price": 1700, "stock": 35}' $ADDR/api/product
......
...@@ -22,7 +22,7 @@ class SignIn extends React.Component { ...@@ -22,7 +22,7 @@ class SignIn extends React.Component {
this.setState({ isLoading: true, error: null }); this.setState({ isLoading: true, error: null });
const body = JSON.stringify({ const body = JSON.stringify({
name: this.state.name, login: this.state.name,
password: this.state.password, password: this.state.password,
noExpire: this.state.noExpire, noExpire: this.state.noExpire,
}); });
...@@ -57,7 +57,7 @@ class SignIn extends React.Component { ...@@ -57,7 +57,7 @@ class SignIn extends React.Component {
let form = ( let form = (
<Form onSubmit={this.onFormSubmit}> <Form onSubmit={this.onFormSubmit}>
<Form.Group> <Form.Group>
<Form.Label>Nombre</Form.Label> <Form.Label>Nombre de acceso</Form.Label>
<Form.Control <Form.Control
placeholder="Nombre" placeholder="Nombre"
value={this.state.name} value={this.state.name}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment