Commit 76edea90 authored by Alexis POYEN's avatar Alexis POYEN
Browse files

Technical : save user in DB instead of file

parent 86e3d068
Pipeline #4848 failed with stages
in 3 minutes and 22 seconds
[
{
"id": 1,
"idOAuth": "",
"login": "Dupond",
"role": "CLIENT",
"passwordHash": "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"
},
{
"id": 2,
"idOAuth": "",
"login": "Bakery",
"role": "CLIENT",
"passwordHash": "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"
},
{
"id": 3,
"idOAuth": "",
"login": "banker",
"role": "BANKER",
"passwordHash": "$2a$10$w6aIsC8lfMSB9tXIDRgk9OztQS.4gBQA9Uoi0X7mCzz5mlTRIx4tq"
},
{
"id": 4,
"idOAuth": "",
"login": "admin",
"role": "ADMIN",
"passwordHash": "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"
},
{
"id": 5,
"idOAuth": "1",
"login": "ADMIN",
"displayName": "Ad MIN",
"role": "ADMIN"
},
{
"id": 6,
"idOAuth": "",
"login": "banker2",
"role": "BANKER",
"passwordHash": "$2a$10$w6aIsC8lfMSB9tXIDRgk9OztQS.4gBQA9Uoi0X7mCzz5mlTRIx4tq"
}
]
\ No newline at end of file
File added
......@@ -12,8 +12,6 @@ import (
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type authzFunc func(http.Handler, []string, bool) http.Handler
// DataHandler init a gorm DB an presents API handlers
type DataHandler struct {
db *gorm.DB
......
......@@ -33,8 +33,7 @@ func init() {
func TestAll(t *testing.T) {
os.Mkdir("data", os.ModePerm)
os.Create("./data/test.db")
// Set the users file
auth.UsersFile = "../../configs/users.json"
os.Link("../../data/users.db", "./data/users.db")
// Create the mock OAuth2 server
oAuth2Server := httptest.NewServer(mocks.CreateMockOAuth2())
defer oAuth2Server.Close()
......@@ -124,6 +123,7 @@ func resetData(t *testing.T) {
os.RemoveAll("./data")
os.Mkdir("data", os.ModePerm)
os.Create("./data/test.db")
os.Link("../../data/users.db", "./data/users.db")
ts, do, _ := createTester(t)
defer ts.Close() // Close the tester
......@@ -134,6 +134,7 @@ func resetData(t *testing.T) {
token := auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Create one banker
do("POST", "/api/UserBankers", xsrfHeader, `{"UserID":3,"Name":"Banker"}`, 200, "")
// Create an other banker
......
package auth
import (
"encoding/json"
"io/ioutil"
"net/http"
"os"
"reflect"
"testing"
......@@ -39,18 +36,7 @@ func TestCheckUserHasRole(t *testing.T) {
}
}
func TestAddUser(t *testing.T) {
UsersFile = writeUsers()
defer os.Remove(UsersFile)
handler := http.HandlerFunc(AddUser)
tester.DoRequestOnHandler(t, handler, "POST", "/", noH, `{"login":"adminTest","password": "password","role":"ADMINS"}`, http.StatusOK, `[{"id":1,"idOAuth":"","login":"admin"`)
tester.DoRequestOnHandler(t, handler, "POST", "/", noH, `{"login":"admin","password": ""}`, http.StatusBadRequest, `passwords cannot be blank`)
}
func TestMatchUser(t *testing.T) {
UsersFile = "../../configs/users.json"
existingUser := User{ID: 2, Login: "Bakery", Role: "CLIENT", PasswordHash: "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"}
veryLongString, _ := common.GenerateRandomString(10000)
specialCharString := "\""
......@@ -78,9 +64,11 @@ func TestMatchUser(t *testing.T) {
{"special_char_string_as_password", args{User{Login: "Bakery", Password: specialCharString}}, User{}, true},
{"special_char_string_as_user_and_password", args{User{Login: specialCharString, Password: specialCharString}}, User{}, true},
}
os.Mkdir("data", os.ModePerm)
os.Link("../../data/users.db", "./data/users.db")
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := MatchUser(tt.args.sentUser)
got, err := NewDataHandler().MatchUser(tt.args.sentUser)
if (err != nil) != tt.wantErr {
t.Errorf("MatchUser() error = %v, wantErr %v", err, tt.wantErr)
return
......@@ -91,20 +79,3 @@ func TestMatchUser(t *testing.T) {
})
}
}
func writeUsers() (name string) {
users := []*User{
{ID: 1, Login: "admin", Password: "password"},
{ID: 2, Login: "user", Password: "password"},
}
f, err := ioutil.TempFile("", "users")
if err != nil {
panic(err)
}
defer f.Close()
err = json.NewEncoder(f).Encode(users)
if err != nil {
panic(err)
}
return f.Name()
}
package auth
import (
"errors"
"github.com/jinzhu/gorm"
)
// DataHandler has the db connection
type DataHandler struct {
db *gorm.DB
}
// NewDataHandler init a DataHandler and returns a pointer to it
func NewDataHandler() *DataHandler {
db, err := gorm.Open("sqlite3", "./data/users.db")
if err != nil {
panic("failed to connect database")
}
db.LogMode(true)
// Migrate the schema
db.AutoMigrate(&User{})
return &DataHandler{db: db}
}
func (d *DataHandler) saveUser(user User) error {
d.db.Create(&user)
return nil
}
func (d *DataHandler) getUsers() []User {
var users []User
d.db.Find(&users)
return users
}
func (d *DataHandler) removeUser(id int) error {
var o User
if err := d.db.First(&o, id).Error; err != nil {
return errors.New("id does not exist")
}
d.db.Delete(&o)
return nil
}
......@@ -4,7 +4,6 @@ import (
"encoding/json"
"errors"
"net/http"
"sort"
"strconv"
"strings"
"time"
......@@ -33,7 +32,8 @@ func (m Manager) HandleInMemoryLogin(w http.ResponseWriter, r *http.Request) {
return
}
// Try to match the user with an user in the database
user, err := MatchUser(sentUser)
d := NewDataHandler()
user, err := d.MatchUser(sentUser)
if err != nil {
http.Error(w, err.Error(), 403)
log.Logger.Printf("| %v | Login failure | %v | %v", sentUser.Login, r.RemoteAddr, log.GetCityAndCountryFromRequest(r))
......@@ -62,43 +62,34 @@ func (a ByID) Less(i, j int) bool { return a[i].ID < a[j].ID }
// ProcessUsers processes users regarding of HTTP method
func ProcessUsers(w http.ResponseWriter, req *http.Request) {
d := NewDataHandler()
switch method := req.Method; method {
case "GET":
SendUsers(w, req)
d.SendUsers(w, req)
case "POST":
AddUser(w, req)
d.AddUser(w, req)
case "DELETE":
DeleteUser(w, req)
d.DeleteUser(w, req)
default:
http.Error(w, "method not allowed", 400)
}
}
// SendUsers send users as response from an http requests
func SendUsers(w http.ResponseWriter, req *http.Request) {
var users []User
err := common.Load(UsersFile, &users)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
func (d *DataHandler) SendUsers(w http.ResponseWriter, req *http.Request) {
var users = d.getUsers()
json.NewEncoder(w).Encode(users)
}
// AddUser adds an user
func AddUser(w http.ResponseWriter, req *http.Request) {
var users []User
err := common.Load(UsersFile, &users)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
func (d *DataHandler) AddUser(w http.ResponseWriter, req *http.Request) {
var users = d.getUsers()
if req.Body == nil {
http.Error(w, "please send a request body", 400)
return
}
var newUser User
err = json.NewDecoder(req.Body).Decode(&newUser)
err := json.NewDecoder(req.Body).Decode(&newUser)
if _, ok := err.(*json.UnmarshalTypeError); !ok && err != nil {
http.Error(w, err.Error(), 400)
return
......@@ -128,54 +119,30 @@ func AddUser(w http.ResponseWriter, req *http.Request) {
return
}
}
// Sauvegarder l'utilisateur dans InMemory
users = append(users, newUser)
sort.Sort(ByID(users))
err = common.Save(UsersFile, &users)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
SendUsers(w, req)
d.saveUser(newUser)
d.SendUsers(w, req)
}
// DeleteUser adds an user
func DeleteUser(w http.ResponseWriter, req *http.Request) {
var users []User
err := common.Load(UsersFile, &users)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
func (d *DataHandler) DeleteUser(w http.ResponseWriter, req *http.Request) {
pathElements := strings.Split(req.URL.Path, "/")
idx, err := strconv.Atoi(pathElements[len(pathElements)-1])
if err != nil {
http.Error(w, "please provide an user index", 400)
return
}
// Add the user only if the name doesn't exists yet
newUsers := users[:0]
for _, user := range users {
if err == nil && user.ID != idx {
newUsers = append(newUsers, user)
}
}
err = common.Save(UsersFile, &newUsers)
err = d.removeUser(idx)
if err != nil {
http.Error(w, err.Error(), 400)
return
}
SendUsers(w, req)
d.SendUsers(w, req)
}
// MatchUser attempt to find the given user against users in configuration file
func MatchUser(sentUser User) (User, error) {
func (d *DataHandler) MatchUser(sentUser User) (User, error) {
var emptyUser User
var users []User
err := common.Load(UsersFile, &users)
if err != nil {
return emptyUser, err
}
var users = d.getUsers()
for _, user := range users {
if user.Login == sentUser.Login {
notFound := bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(sentUser.Password))
......
......@@ -6,7 +6,6 @@ import (
"fmt"
"net/http"
"os"
"sort"
"strings"
"time"
......@@ -126,7 +125,8 @@ func (m Manager) HandleOAuth2Callback() http.Handler {
userOauth2.Groups[key] = strings.TrimPrefix(strings.Split(role, ",")[0], "CN=")
}
// Store the user in cookie
user, err := getUserInMemory(userOauth2)
dh := NewDataHandler()
user, err := dh.getUserInMemory(userOauth2)
if err != nil {
http.Error(w, err.Error(), 400)
return
......@@ -147,22 +147,17 @@ func (m Manager) HandleOAuth2Callback() http.Handler {
return http.HandlerFunc(oauth2Handler)
}
func getUserInMemory(userOAuth2 UserOAuth2) (User, error) {
var user User
var users []User
err := common.Load(UsersFile, &users)
if err != nil {
return user, errors.New("Error on loading user")
}
func (d *DataHandler) getUserInMemory(userOAuth2 UserOAuth2) (User, error) {
var users = d.getUsers()
for _, val := range users {
if userOAuth2.ID == val.IDOAuth {
return val, nil
}
}
return addUserInMemory(userOAuth2)
return d.addUserInMemory(userOAuth2)
}
func addUserInMemory(userOauth2 UserOAuth2) (User, error) {
func (d *DataHandler) addUserInMemory(userOauth2 UserOAuth2) (User, error) {
var user User
// Define user role or refuse if not in a correct group
for _, userRole := range userOauth2.Groups {
......@@ -193,11 +188,6 @@ func addUserInMemory(userOauth2 UserOAuth2) (User, error) {
}
}
// Sauvegarder l'utilisateur dans InMemory
users = append(users, user)
sort.Sort(ByID(users))
err = common.Save(UsersFile, &users)
if err != nil {
return user, errors.New("Error on saving user")
}
d.saveUser(user)
return user, nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment