Commit a5dd74ec authored by Alexis POYEN's avatar Alexis POYEN
Browse files

Feat : bind technical users with app users and allow API access only to user's data

parent 593b0860
Pipeline #4632 failed with stages
in 3 minutes and 14 seconds
[
{
"id": "1",
"id": "3",
"login": "admin",
"memberOf": [
"ADMINS"
],
"passwordHash": "$2a$10$w6aIsC8lfMSB9tXIDRgk9OztQS.4gBQA9Uoi0X7mCzz5mlTRIx4tq"
},
{
"id": "1",
"login": "Dupond",
"memberOf": [
"USERS"
],
"passwordHash": "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"
},
{
"id": "2",
"login": "user",
"login": "Bakery",
"memberOf": [
"USERS"
],
......
......@@ -56,18 +56,23 @@ func (d *DataHandler) ProcessAPI(w http.ResponseWriter, r *http.Request) {
}
}
// UserClient has many BankAccounts, UserClientID is the foreign key
// UserClient has many BankAccounts and one UserBanker
type UserClient struct {
ID uint `gorm:"primary_key"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-"`
Name string
BankAccounts []BankAccount
ID uint64 `gorm:"primary_key"`
AuthType string `gorm:"primary_key"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-"`
Name string
UserBankerID uint64
AuthTypeBanker string
BankAccounts []BankAccount
}
// UserBanker has many UserClient
type UserBanker struct {
ID uint `gorm:"primary_key"`
ID uint64 `gorm:"primary_key"`
AuthType string `gorm:"primary_key"`
CreatedAt time.Time `json:"-"`
UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-"`
......@@ -82,7 +87,7 @@ type BankAccount struct {
UpdatedAt time.Time `json:"-"`
DeletedAt *time.Time `json:"-"`
Number string
UserClientID uint
UserClientID uint64
Type string
Amount int
BankOverdraft int
......@@ -109,47 +114,93 @@ func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) {
if !auth.IsAllowed(w, r, []string{"*"}) {
return
}
if id != 0 {
var o UserClient
if err := d.db.Preload("BankAccounts").First(&o, id).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
user := d.getLoggedUser(w, r)
switch user := user.(type) {
case UserBanker:
if id != 0 {
var o UserClient
if err := d.db.Preload("BankAccounts").Where("id = ?", id).First(&o).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
}
if o.UserBankerID != user.ID || o.AuthTypeBanker != user.AuthType {
http.Error(w, "You can not access this ressource", http.StatusForbidden)
return
}
json.NewEncoder(w).Encode(o)
} else {
var o []UserClient
d.db.Preload("BankAccounts").Find(&o)
json.NewEncoder(w).Encode(o)
}
json.NewEncoder(w).Encode(o)
} else {
var o []UserClient
d.db.Preload("BankAccounts").Find(&o)
json.NewEncoder(w).Encode(o)
case UserClient:
if id != 0 && int(user.ID) == id {
var o UserClient
if err := d.db.Preload("BankAccounts").Where("id = ? AND auth_type = ?", user.ID, user.AuthType).First(&o).Error; err != nil {
fmt.Println(err)
http.Error(w, "id does not exist", http.StatusNotFound)
return
}
json.NewEncoder(w).Encode(o)
} else if id != 0 && int(user.ID) != id {
http.Error(w, "You can not access this ressource", http.StatusForbidden)
} else {
var o []UserClient
d.db.Preload("BankAccounts").Find(&o)
json.NewEncoder(w).Encode(o)
}
default:
http.Error(w, "Could not get logged user", http.StatusInternalServerError)
}
case "POST":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
var o UserClient
err := json.NewDecoder(r.Body).Decode(&o)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
user := d.getLoggedUser(w, r)
switch user := user.(type) {
case UserBanker:
var o UserClient
err := json.NewDecoder(r.Body).Decode(&o)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
o.UserBankerID = user.ID
o.AuthTypeBanker = user.AuthType
d.db.Create(&o)
case UserClient:
http.Error(w, "You're not authorize to execute this method on this ressource.", http.StatusMethodNotAllowed)
default:
http.Error(w, "Could not get logged user", http.StatusInternalServerError)
}
d.db.Create(&o)
case "DELETE":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
if id != 0 {
var o UserClient
if err := d.db.First(&o, id).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
}
// Delete bank accounts of user
d.db.Where("user_client_id = ?", o.ID).Delete(&BankAccount{})
user := d.getLoggedUser(w, r)
switch user := user.(type) {
case UserBanker:
if id != 0 {
var o UserClient
if err := d.db.Where("id = ?", id).First(&o).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
}
if o.UserBankerID != user.ID || o.AuthTypeBanker != user.AuthType {
http.Error(w, "You can not access this ressource", http.StatusForbidden)
return
}
// Delete bank accounts of user
d.db.Where("user_client_id = ?", o.ID).Delete(&BankAccount{})
d.db.Delete(&o)
} else {
http.Error(w, "id is missing", http.StatusNotFound)
d.db.Delete(&o)
} else {
http.Error(w, "id is missing", http.StatusNotFound)
}
case UserClient:
http.Error(w, "You're not authorize to execute this method on this ressource.", http.StatusMethodNotAllowed)
default:
http.Error(w, "Could not get logged user", http.StatusInternalServerError)
}
default:
http.Error(w, "method not allowed", 400)
......@@ -164,43 +215,106 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request)
return
}
if id != 0 {
var o BankAccount
if err := d.db.Preload("Operations").First(&o, id).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
user := d.getLoggedUser(w, r)
switch user := user.(type) {
case UserBanker:
if id != 0 {
var o BankAccount
if err := d.db.Preload("Operations").First(&o, id).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
}
// Check that the bank account belong to a one of the banker's client
var userClient UserClient
if err := d.db.Where("id = ? and user_banker_id = ? and auth_type_banker = ?", o.UserClientID, user.ID, user.AuthType).First(&userClient).Error; err != nil {
http.Error(w, "You can not access this ressource", http.StatusForbidden)
return
}
json.NewEncoder(w).Encode(o)
} else {
var o []BankAccount
d.db.Preload("Operations").Where("user_client_id IN (?)", d.db.Table("user_clients").Select("id").Where("user_banker_id = ?", user.ID).QueryExpr()).Find(&o)
json.NewEncoder(w).Encode(o)
}
json.NewEncoder(w).Encode(o)
} else {
var o []BankAccount
d.db.Preload("Operations").Find(&o)
json.NewEncoder(w).Encode(o)
case UserClient:
if id != 0 {
var o BankAccount
if err := d.db.Preload("Operations").Where("id = ? AND user_client_id = ?", id, user.ID).First(&o).Error; err != nil {
fmt.Println(err)
http.Error(w, "You can not access this ressource", http.StatusForbidden)
return
}
json.NewEncoder(w).Encode(o)
} else {
var o []BankAccount
d.db.Preload("Operations").Where("user_client_id = ?", user.ID).Find(&o)
json.NewEncoder(w).Encode(o)
}
default:
http.Error(w, "Could not get logged user", http.StatusInternalServerError)
}
case "POST":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
var o BankAccount
err := json.NewDecoder(r.Body).Decode(&o)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
user := d.getLoggedUser(w, r)
switch user := user.(type) {
case UserBanker:
var o BankAccount
err := json.NewDecoder(r.Body).Decode(&o)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
if o.UserClientID != 0 {
var userClient UserClient
if err := d.db.Where("id = ? and user_banker_id = ? and auth_type_banker = ?", o.UserClientID, user.ID, user.AuthType).First(&userClient).Error; err != nil {
http.Error(w, "You can not access this ressource", http.StatusForbidden)
return
}
d.db.Create(&o)
} else {
http.Error(w, "id of UserClient is missing", http.StatusNotFound)
}
case UserClient:
http.Error(w, "You're not authorize to execute this method on this ressource.", http.StatusMethodNotAllowed)
default:
http.Error(w, "Could not get logged user", http.StatusInternalServerError)
}
d.db.Create(&o)
case "DELETE":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
if id != 0 {
var o BankAccount
if err := d.db.First(&o, id).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
user := d.getLoggedUser(w, r)
switch user := user.(type) {
case UserBanker:
if id != 0 {
var o BankAccount
if err := d.db.First(&o, id).Error; err != nil {
http.Error(w, "id does not exist", http.StatusNotFound)
return
}
if o.UserClientID != 0 {
var userClient UserClient
if err := d.db.Where("id = ? and user_banker_id = ? and auth_type_banker = ?", o.UserClientID, user.ID, user.AuthType).First(&userClient).Error; err != nil {
http.Error(w, "You can not access this ressource", http.StatusForbidden)
return
}
d.db.Delete(&o)
} else {
http.Error(w, "id of UserClient is missing", http.StatusNotFound)
}
} else {
http.Error(w, "id is missing", http.StatusNotFound)
}
d.db.Delete(&o)
} else {
http.Error(w, "id is missing", http.StatusNotFound)
case UserClient:
http.Error(w, "You're not authorize to execute this method on this ressource.", http.StatusMethodNotAllowed)
default:
http.Error(w, "Could not get logged user", http.StatusInternalServerError)
}
default:
http.Error(w, "method not allowed", 400)
......@@ -308,3 +422,30 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
}
}
func (d *DataHandler) getLoggedUser(w http.ResponseWriter, r *http.Request) interface{} {
user := auth.GetLoggedUserTechnical(w, r)
for _, userRole := range user.Roles {
if userRole != "" && (userRole == os.Getenv("ADMIN_ROLE")) {
var o UserBanker
if err := d.db.Where("id = ? AND auth_type = ?", user.ID, user.AuthType).First(&o).Error; err != nil {
id, _ := strconv.ParseUint(user.ID, 10, 64)
o := UserBanker{ID: id, AuthType: user.AuthType, Name: user.Login}
d.db.Create(&o)
d.db.Where("id = ? AND auth_type = ?", user.ID, user.AuthType).First(&o)
return o
}
return o
}
var o UserClient
if err := d.db.Where("id = ? AND auth_type = ?", user.ID, user.AuthType).First(&o).Error; err != nil {
id, _ := strconv.ParseUint(user.ID, 10, 64)
o := UserClient{ID: id, AuthType: user.AuthType, Name: user.Login}
d.db.Create(&o)
d.db.Where("id = ? AND auth_type = ?", user.ID, user.AuthType).First(&o)
return o
}
return o
}
return nil
}
......@@ -5,7 +5,7 @@ import (
"os"
"forge.grandlyon.com/apoyen/sdk-go/internal/models"
"github.com/nicolaspernoud/vestibule/pkg/auth"
"forge.grandlyon.com/apoyen/sdk-go/pkg/auth"
"github.com/nicolaspernoud/vestibule/pkg/middlewares"
"github.com/nicolaspernoud/vestibule/pkg/common"
......
......@@ -11,8 +11,8 @@ import (
"regexp"
"testing"
"forge.grandlyon.com/apoyen/sdk-go/pkg/auth"
"forge.grandlyon.com/apoyen/sdk-go/pkg/tester"
"github.com/nicolaspernoud/vestibule/pkg/auth"
"github.com/nicolaspernoud/vestibule/pkg/tokens"
"forge.grandlyon.com/apoyen/sdk-go/internal/mocks"
......@@ -133,8 +133,8 @@ func UserTests(t *testing.T) {
json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
do("POST", "/api/UserClients", xsrfHeader, `{"Name":"Dupond"}`, 200, "")
do("POST", "/api/UserClients", xsrfHeader, `{"Name":"Boulangerie"}`, 200, "")
do("POST", "/api/UserClients", xsrfHeader, `{"ID":1,"AuthType":"local","Name":"Dupond"}`, 200, "")
do("POST", "/api/UserClients", xsrfHeader, `{"ID":2,"AuthType":"local","Name":"Boulangerie"}`, 200, "")
do("POST", "/api/BankAccounts", xsrfHeader, `{"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100}`, 200, "")
do("POST", "/api/BankAccounts", xsrfHeader, `{"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0}`, 200, "")
......@@ -165,14 +165,16 @@ func UserTests(t *testing.T) {
// Verify operation on client Dupond checking-account
do("GET", "/api/BankAccounts/1", xsrfHeader, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":[{"ID":1,"Debtor":1,"Amount":-100`)
// Verify operation on client Bakery checking-account
do("GET", "/api/BankAccounts/3", xsrfHeader, "", 200, `{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":[{"ID":2,"Debtor":3,"Amount":100`)
do("GET", "/api/BankAccounts/3", xsrfHeader, "", 403, `You can not access this ressource`)
// Get all accounts of the user client and only those ones
do("GET", "/api/BankAccounts/", xsrfHeader, "", 200, `[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":[{"ID":1,"Debtor":1,"Amount":-100`)
// Get client Dupond with his banks accounts and operations up to date (-100€ on checking-account)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":null},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":null}]}`)
do("GET", "/api/UserClients/2", xsrfHeader, "", 403, `You can not access this ressource`)
// Get client Bakery with his banks accounts and operations up to date (+100€ on checking-account)
do("GET", "/api/UserClients/2", xsrfHeader, "", 200, `{"ID":2,"Name":"Boulangerie","BankAccounts":[{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":null}]}`)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"AuthType":"local","Name":"Dupond","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":null},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":null}]}`)
// Try to get all the clients
do("GET", "/api/UserClients", xsrfHeader, "", 200, `[{"ID":1,"Name":"Dupond","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":null},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":null}]},{"ID":2,"Name":"Boulangerie","BankAccounts":[{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":null}]}]`)
do("GET", "/api/UserClients", xsrfHeader, "", 200, `[{"ID":1,"AuthType":"local","Name":"Dupond","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":null},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":null}]},{"ID":2,"AuthType":"local","Name":"Boulangerie","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":null}]}]`)
// Try to delete the first operation should fail
do("DELETE", "/api/Operations/1", xsrfHeader, ``, 405, "You're not authorize to execute this method on this ressource.")
......@@ -182,7 +184,7 @@ func UserTests(t *testing.T) {
do("DELETE", "/api/UserClients/2", xsrfHeader, ``, 405, "You're not authorize to execute this method on this ressource.")
}
// Do a in memory login with an known user
do("POST", "/Login", noH, `{"login": "user","password": "password"}`, 200, "")
do("POST", "/Login", noH, `{"login": "Dupond","password": "password"}`, 200, "")
// Run the tests
tests()
// Try to logout (must pass)
......@@ -205,13 +207,13 @@ func AdminTests(t *testing.T) {
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Try to create a client
do("POST", "/api/UserClients", xsrfHeader, `{"Name":"Dupond"}`, 200, "")
do("POST", "/api/UserClients", xsrfHeader, `{"ID":1,"AuthType":"local","Name":"Dupond"}`, 200, "")
// Try to create an other one
do("POST", "/api/UserClients", xsrfHeader, `{"Name":"Boulangerie"}`, 200, "")
do("POST", "/api/UserClients", xsrfHeader, `{"ID":2,"AuthType":"local","Name":"Boulangerie"}`, 200, "")
// Try to get the first client
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[]}`)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"AuthType":"local","Name":"Dupond","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[]}`)
// Try to get all the clients
do("GET", "/api/UserClients", xsrfHeader, "", 200, `[{"ID":1,"Name":"Dupond","BankAccounts":[]},{"ID":2,"Name":"Boulangerie","BankAccounts":[]}]`)
do("GET", "/api/UserClients", xsrfHeader, "", 200, `[{"ID":1,"AuthType":"local","Name":"Dupond","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[]},{"ID":2,"AuthType":"local","Name":"Boulangerie","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[]}]`)
// Add bank account to client
do("POST", "/api/BankAccounts", xsrfHeader, `{"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100}`, 200, "")
......@@ -234,9 +236,9 @@ func AdminTests(t *testing.T) {
do("POST", "/api/Operations", xsrfHeader, `{"Debtor":1,"Amount":-1789,"Creditor":3}`, 417, "Not enough money")
// Get client Dupond with his banks accounts and operations up to date (-100€ on checking-account)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":null},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":null}]}`)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"AuthType":"local","Name":"Dupond","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":null},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":null}]}`)
// Get client Bakery with his banks accounts and operations up to date (+100€ on checking-account)
do("GET", "/api/UserClients/2", xsrfHeader, "", 200, `{"ID":2,"Name":"Boulangerie","BankAccounts":[{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":null}]}`)
do("GET", "/api/UserClients/2", xsrfHeader, "", 200, `{"ID":2,"AuthType":"local","Name":"Boulangerie","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":null}]}`)
// Verify operation on client Dupond checking-account
do("GET", "/api/BankAccounts/1", xsrfHeader, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":[{"ID":1,"Debtor":1,"Amount":-100`)
......@@ -261,15 +263,57 @@ func AdminTests(t *testing.T) {
// Test that client have been updated after operations deletion and bank account deletion
// Get client Dupond with his banks accounts and operations up to date (-100€ on checking-account)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":null}]}`)
do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"AuthType":"local","Name":"Dupond","UserBankerID":3,"AuthTypeBanker":"local","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":null}]}`)
// Try to delete the saving account of Dupond
// Try to delete the bakery client
do("DELETE", "/api/UserClients/2", xsrfHeader, ``, 200, "")
// Try to get the first user again
do("GET", "/api/UserClients/2", xsrfHeader, "", 404, `id does not exist`)
// Test that bank account have been deleted after client deletion
do("GET", "/api/BankAccounts/3", xsrfHeader, "", 404, `id does not exist`)
do("GET", "/Logout", noH, "", 200, "Logout OK")
// Try to login with OAuth2 (must pass) should be logged as admin with admin role (banker)
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
response = do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token = auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader = tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Try to create a client Dupont
do("POST", "/api/UserClients", xsrfHeader, `{"ID":4,"AuthType":"local","Name":"Dupont"}`, 200, "")
// Try to get the banker's client
do("GET", "/api/UserClients/4", xsrfHeader, "", 200, `{"ID":4,"AuthType":"local","Name":"Dupont","UserBankerID":1,"AuthTypeBanker":"OAuth2","BankAccounts":[]}`)
// Try to get an other banker's client should fail with 403
do("GET", "/api/UserClients/1", xsrfHeader, "", 403, `You can not access this ressource`)
// Add bank account to client
do("POST", "/api/BankAccounts", xsrfHeader, `{"Number":"04-01","UserClientID":4,"Type":"checking-account","Amount":200,"BankOverdraft":-50}`, 200, "")
// Get account where id=1 should fail as it's not an account of the banker's client
do("GET", "/api/BankAccounts/1", xsrfHeader, "", 403, `You can not access this ressource`)
// Get all Bank account
do("GET", "/api/BankAccounts/", xsrfHeader, "", 200, `[{"ID":4,"Number":"04-01","UserClientID":4,"Type":"checking-account","Amount":200,"BankOverdraft":-50,"Operations":[]}]`)
// Try to delete the checking-account account of Dupond should fail with 403
do("DELETE", "/api/BankAccounts/1", xsrfHeader, ``, 403, `You can not access this ressource`)
// Try to delete Dupond should fail
do("DELETE", "/api/UserClients/1", xsrfHeader, ``, 403, `You can not access this ressource`)
do("GET", "/Logout", noH, "", 200, "Logout OK")
do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
response = do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token = auth.TokenData{}
json.Unmarshal([]byte(response), &token)
xsrfHeader = tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// logout and relog as local banker
// Try to get an other banker's client should fail with 403
do("GET", "/api/UserClients/4", xsrfHeader, "", 403, `You can not access this ressource`)
// Try to get an other banker's client's account should fail with 403
do("GET", "/api/BankAccounts/4", xsrfHeader, "", 403, `You can not access this ressource`)
// Try to delete the checking-account account of Dupont should fail with 403
do("DELETE", "/api/BankAccounts/4", xsrfHeader, ``, 403, `You can not access this ressource`)
// Try to delete Dupont should fail
do("DELETE", "/api/UserClients/4", xsrfHeader, ``, 403, `You can not access this ressource`)
}
// Do a in memory login with an known admin
do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
......
......@@ -26,7 +26,7 @@ const (
type User struct {
ID string `json:"id,omitempty"`
Login string `json:"login"`
AuthType string `json:authType`
AuthType string `json:"authType,omitempty"`
DisplayName string `json:"displayName,omitempty"`
Roles []string `json:"memberOf"`
IsAdmin bool `json:"isAdmin,omitempty"`
......@@ -229,3 +229,33 @@ func IsAllowed(w http.ResponseWriter, r *http.Request, allowedRoles []string) bo
}
return true
}
// GetLoggedUserTechnical return the User authenticated
func GetLoggedUserTechnical(w http.ResponseWriter, r *http.Request) User {
user := TokenData{}
checkXSRF, err := tokens.Manager.ExtractAndValidateToken(r, authTokenKey, &user, true)
// Handle CORS preflight requests
if err != nil && r.Method == "OPTIONS" {
// Handle GIO preflight requests
if strings.Contains(r.UserAgent(), "vfs") || strings.Contains(r.UserAgent(), "Microsoft-WebDAV") {
w.Header().Set("WWW-Authenticate", `Basic realm="server"`)
http.Error(w, "webdav client authentication", 401)
}
}
if err != nil {
redirectTo := os.Getenv("HOSTNAME")
_, port, perr := net.SplitHostPort(r.Host)
if perr == nil {
redirectTo += ":" + port
}
w.Header().Set("Content-Type", "text/html")
w.WriteHeader(http.StatusUnauthorized)
responseContent := fmt.Sprintf("error extracting token: %v<meta http-equiv=\"Refresh\" content=\"0; url=https://%v/#login\"/>", err.Error(), redirectTo)
fmt.Fprintf(w, responseContent)
}
// Check XSRF Token
if checkXSRF && r.Header.Get("XSRF-TOKEN") != user.XSRFToken {
http.Error(w, "XSRF protection triggered", 401)
}
return user.User
}
......@@ -51,7 +51,7 @@ func TestAddUser(t *testing.T) {
func TestMatchUser(t *testing.T) {
UsersFile = "../../configs/users.json"
existingUser := User{ID: "2", Login: "user", Roles: []string{"USERS"}, PasswordHash: "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"}
existingUser := User{ID: "2", Login: "Bakery", Roles: []string{"USERS"}, PasswordHash: "$2a$10$PgiAoLxZhgNtr7kRK/DH5ezwT./7vRkWqFNEtJD1670z3Zf60HqgG"}
veryLongString, _ := common.GenerateRandomString(10000)
specialCharString := "\""
......@@ -64,18 +64,18 @@ func TestMatchUser(t *testing.T) {
want User
wantErr bool
}{
{"user_exists", args{User{Login: "user", Password: "password"}}, existingUser, false},
{"user_exists", args{User{Login: "Bakery", Password: "password"}}, existingUser, false},
{"user_does_not_exists", args{User{Login: "notuser", Password: "password"}}, User{}, true},
{"user_does_not_exists_and_wrong_password", args{User{Login: "notuser", Password: "wrongpassword"}}, User{}, true},
{"wrong_password", args{User{Login: "user", Password: "wrongpassword"}}, User{}, true},
{"no_password", args{User{Login: "user", Password: ""}}, User{}, true},
{"wrong_password", args{User{Login: "Bakery", Password: "wrongpassword"}}, User{}, true},
{"no_password", args{User{Login: "Bakery", Password: ""}}, User{}, true},
{"empty_user", args{User{Login: "", Password: "password"}}, User{}, true},
{"empty_user_and_password", args{User{Login: "", Password: ""}}, User{}, true},
{"very_long_string_as_user", args{User{Login: veryLongString, Password: "password"}}, User{}, true},
{"very_long_string_as_password", args{User{Login: "user", Password: veryLongString}}, User{}, true},
{"very_long_string_as_password", args{User{Login: "Bakery", Password: veryLongString}}, User{}, true},
{"very_long_string_as_user_and_password", args{User{Login: veryLongString, Password: veryLongString}}, User{}, true},
{"special_char_string_as_user", args{User{Login: specialCharString, Password: "password"}}, User{}, true},
{"special_char_string_as_password", args{User{Login: "user", Password: specialCharString}}, User{}, true},
{"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},
}
for _, tt := range tests {
......
......@@ -40,7 +40,6 @@ func (m Manager) HandleInMemoryLogin(w http.ResponseWriter, r *http.Request) {
log.Logger.Printf("| %v | Login failure | %v | %v", sentUser.Login, r.RemoteAddr, log.GetCityAndCountryFromRequest(r))
return
}
user.AuthType = "local"
// Store the user in cookie
// Store only the relevant info
// Generate
...