Skip to content
Snippets Groups Projects
Commit dfcc30b4 authored by Alexis Poyen's avatar Alexis Poyen
Browse files

WIP : api call with authent and roles checking

parent a5b1c184
No related branches found
No related tags found
No related merge requests found
Pipeline #4592 failed
# SDK - GO # SDK - GO
Simple Developpement Kit for GO with Gorm ORM for backend and Bulmajs for frontend. Simple Developpement Kit for GO with Gorm ORM for backend and Bulma for frontend.
\ No newline at end of file \ No newline at end of file
...@@ -2,12 +2,16 @@ package models ...@@ -2,12 +2,16 @@ package models
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"os"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
"github.com/nicolaspernoud/vestibule/pkg/auth"
// Needed for sqlite // Needed for sqlite
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
...@@ -100,9 +104,14 @@ type Operation struct { ...@@ -100,9 +104,14 @@ type Operation struct {
// HandleClients expose the UserClients API // HandleClients expose the UserClients API
func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) { func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) {
fmt.Println(r.URL.Path)
id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/UserClients/")) id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/UserClients/"))
switch method := r.Method; method { switch method := r.Method; method {
case "GET": case "GET":
if !auth.IsAllowed(w, r, []string{"*"}) {
return
}
if id != 0 { if id != 0 {
var o UserClient var o UserClient
if err := d.db.Preload("BankAccounts").First(&o, id).Error; err != nil { if err := d.db.Preload("BankAccounts").First(&o, id).Error; err != nil {
...@@ -116,6 +125,10 @@ func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) { ...@@ -116,6 +125,10 @@ func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(o) json.NewEncoder(w).Encode(o)
} }
case "POST": case "POST":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
var o UserClient var o UserClient
err := json.NewDecoder(r.Body).Decode(&o) err := json.NewDecoder(r.Body).Decode(&o)
if err != nil { if err != nil {
...@@ -123,6 +136,10 @@ func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) { ...@@ -123,6 +136,10 @@ func (d *DataHandler) HandleClients(w http.ResponseWriter, r *http.Request) {
} }
d.db.Create(&o) d.db.Create(&o)
case "DELETE": case "DELETE":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
if id != 0 { if id != 0 {
var o UserClient var o UserClient
if err := d.db.First(&o, id).Error; err != nil { if err := d.db.First(&o, id).Error; err != nil {
...@@ -145,6 +162,10 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request) ...@@ -145,6 +162,10 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request)
id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/BankAccounts/")) id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/BankAccounts/"))
switch method := r.Method; method { switch method := r.Method; method {
case "GET": case "GET":
if !auth.IsAllowed(w, r, []string{"*"}) {
return
}
if id != 0 { if id != 0 {
var o BankAccount var o BankAccount
if err := d.db.Preload("Operations").First(&o, id).Error; err != nil { if err := d.db.Preload("Operations").First(&o, id).Error; err != nil {
...@@ -158,6 +179,10 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request) ...@@ -158,6 +179,10 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request)
json.NewEncoder(w).Encode(o) json.NewEncoder(w).Encode(o)
} }
case "POST": case "POST":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
var o BankAccount var o BankAccount
err := json.NewDecoder(r.Body).Decode(&o) err := json.NewDecoder(r.Body).Decode(&o)
if err != nil { if err != nil {
...@@ -165,6 +190,10 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request) ...@@ -165,6 +190,10 @@ func (d *DataHandler) HandleBankAccounts(w http.ResponseWriter, r *http.Request)
} }
d.db.Create(&o) d.db.Create(&o)
case "DELETE": case "DELETE":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
if id != 0 { if id != 0 {
var o BankAccount var o BankAccount
if err := d.db.First(&o, id).Error; err != nil { if err := d.db.First(&o, id).Error; err != nil {
...@@ -184,6 +213,11 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) { ...@@ -184,6 +213,11 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/Operations/")) id, _ := strconv.Atoi(strings.TrimPrefix(r.URL.Path, "/api/Operations/"))
switch method := r.Method; method { switch method := r.Method; method {
case "GET": case "GET":
if !auth.IsAllowed(w, r, []string{"*"}) {
return
}
if id != 0 { if id != 0 {
var o Operation var o Operation
if err := d.db.First(&o, id).Error; err != nil { if err := d.db.First(&o, id).Error; err != nil {
...@@ -197,6 +231,10 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) { ...@@ -197,6 +231,10 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(o) json.NewEncoder(w).Encode(o)
} }
case "POST": case "POST":
if !auth.IsAllowed(w, r, []string{"*"}) {
return
}
var o Operation var o Operation
err := json.NewDecoder(r.Body).Decode(&o) err := json.NewDecoder(r.Body).Decode(&o)
if err != nil { if err != nil {
...@@ -232,6 +270,10 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) { ...@@ -232,6 +270,10 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) {
} }
} }
case "DELETE": case "DELETE":
if !auth.IsAllowed(w, r, []string{os.Getenv("ADMIN_ROLE")}) {
return
}
if id != 0 { if id != 0 {
var o Operation var o Operation
if err := d.db.First(&o, id).Error; err != nil { if err := d.db.First(&o, id).Error; err != nil {
...@@ -265,6 +307,6 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) { ...@@ -265,6 +307,6 @@ func (d *DataHandler) HandleOperations(w http.ResponseWriter, r *http.Request) {
http.Error(w, "id is missing", http.StatusNotFound) http.Error(w, "id is missing", http.StatusNotFound)
} }
default: default:
http.Error(w, "method not allowed", 400) http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
} }
} }
...@@ -26,72 +26,67 @@ func TestBankAPI(t *testing.T) { ...@@ -26,72 +26,67 @@ func TestBankAPI(t *testing.T) {
do := tester.CreateServerTester(t, port, "sdk-go.io", nil) do := tester.CreateServerTester(t, port, "sdk-go.io", nil)
noH := tester.Header{Key: "", Value: ""} noH := tester.Header{Key: "", Value: ""}
// Try to create a client // Without authent API calls must fails with 403 user could not be got from context
do("POST", "/api/UserClients", noH, `{"Name":"Dupond"}`, 200, "")
// Try to create an other one
do("POST", "/api/UserClients", noH, `{"Name":"Boulangerie"}`, 200, "")
// Try to get the first client // Try to get the first client
do("GET", "/api/UserClients/1", noH, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[]}`) do("GET", "/api/UserClients/1", noH, "", 401, `error extracting token`)
// // Try to get all the clients
do("GET", "/api/UserClients", noH, "", 200, `[{"ID":1,"Name":"Dupond","BankAccounts":[]},{"ID":2,"Name":"Boulangerie","BankAccounts":[]}]`) // // Add bank account to client
// do("POST", "/api/BankAccounts", noH, `{"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100}`, 200, "")
// Add bank account to client // // Add an other bank account to client
do("POST", "/api/BankAccounts", noH, `{"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100}`, 200, "") // do("POST", "/api/BankAccounts", noH, `{"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0}`, 200, "")
// Add an other bank account to client // // Get account where id=1
do("POST", "/api/BankAccounts", noH, `{"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0}`, 200, "") // do("GET", "/api/BankAccounts/1", noH, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":[]}`)
// Get account where id=1 // // Get all Bank account
do("GET", "/api/BankAccounts/1", noH, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":[]}`) // do("GET", "/api/BankAccounts/", noH, "", 200, `[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":[]},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":[]}]`)
// Get all Bank account // // Add a bank account to Bakery
do("GET", "/api/BankAccounts/", noH, "", 200, `[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":[]},{"ID":2,"Number":"01-02","UserClientID":1,"Type":"saving-account","Amount":1287,"BankOverdraft":0,"Operations":[]}]`) // do("POST", "/api/BankAccounts", noH, `{"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4745,"BankOverdraft":-500}`, 200, "")
// Add a bank account to Bakery
do("POST", "/api/BankAccounts", noH, `{"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4745,"BankOverdraft":-500}`, 200, "") // // Add operation between client and Bakery
// do("POST", "/api/Operations", noH, `{"Debtor":1,"Amount":-100,"Creditor":3}`, 200, "")
// Add operation between client and Bakery // // Get operation where id=1
do("POST", "/api/Operations", noH, `{"Debtor":1,"Amount":-100,"Creditor":3}`, 200, "") // do("GET", "/api/Operations/1", noH, "", 200, `{"ID":1,"Debtor":1,"Amount":-100`)
// Get operation where id=1 // // Get Operations for creditor (opposite operation should have been registered)
do("GET", "/api/Operations/1", noH, "", 200, `{"ID":1,"Debtor":1,"Amount":-100`) // do("GET", "/api/Operations/2", noH, "", 200, `{"ID":2,"Debtor":3,"Amount":100`)
// Get Operations for creditor (opposite operation should have been registered) // // Add invalid operation between client and Bakery must be refused with 417 (Expectation failed)
do("GET", "/api/Operations/2", noH, "", 200, `{"ID":2,"Debtor":3,"Amount":100`) // do("POST", "/api/Operations", noH, `{"Debtor":1,"Amount":-1789,"Creditor":3}`, 417, "Not enough money")
// Add invalid operation between client and Bakery must be refused with 417 (Expectation failed)
do("POST", "/api/Operations", noH, `{"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", noH, "", 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}]}`)
// Get client Dupond with his banks accounts and operations up to date (-100€ on checking-account) // // Get client Bakery with his banks accounts and operations up to date (+100€ on checking-account)
do("GET", "/api/UserClients/1", noH, "", 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", noH, "", 200, `{"ID":2,"Name":"Boulangerie","BankAccounts":[{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":null}]}`)
// Get client Bakery with his banks accounts and operations up to date (+100€ on checking-account)
do("GET", "/api/UserClients/2", noH, "", 200, `{"ID":2,"Name":"Boulangerie","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", noH, "", 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 Dupond checking-account // // Verify operation on client Bakery checking-account
do("GET", "/api/BankAccounts/1", noH, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":358,"BankOverdraft":-100,"Operations":[{"ID":1,"Debtor":1,"Amount":-100`) // do("GET", "/api/BankAccounts/3", noH, "", 200, `{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":[{"ID":2,"Debtor":3,"Amount":100`)
// Verify operation on client Bakery checking-account
do("GET", "/api/BankAccounts/3", noH, "", 200, `{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4845,"BankOverdraft":-500,"Operations":[{"ID":2,"Debtor":3,"Amount":100`) // // Try to delete the first operation
// do("DELETE", "/api/Operations/1", noH, ``, 200, "")
// Try to delete the first operation // // Try to get the first user again
do("DELETE", "/api/Operations/1", noH, ``, 200, "") // do("GET", "/api/Operations/1", noH, "", 404, `id does not exist`)
// Try to get the first user again // // The operation id=2 (opposite operation) should also have been deleted
do("GET", "/api/Operations/1", noH, "", 404, `id does not exist`) // do("GET", "/api/Operations/2", noH, "", 404, `id does not exist`)
// The operation id=2 (opposite operation) should also have been deleted
do("GET", "/api/Operations/2", noH, "", 404, `id does not exist`) // // Test that bank account have been updated after operations deletion
// do("GET", "/api/BankAccounts/1", noH, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":[]}`)
// Test that bank account have been updated after operations deletion // do("GET", "/api/BankAccounts/3", noH, "", 200, `{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4745,"BankOverdraft":-500,"Operations":[]}`)
do("GET", "/api/BankAccounts/1", noH, "", 200, `{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":[]}`)
do("GET", "/api/BankAccounts/3", noH, "", 200, `{"ID":3,"Number":"02-01","UserClientID":2,"Type":"checking-account","Amount":4745,"BankOverdraft":-500,"Operations":[]}`) // // Try to delete the saving account of Dupond
// do("DELETE", "/api/BankAccounts/2", noH, ``, 200, "")
// Try to delete the saving account of Dupond // // Try to get the first user again
do("DELETE", "/api/BankAccounts/2", noH, ``, 200, "") // do("GET", "/api/BankAccounts/2", noH, "", 404, `id does not exist`)
// Try to get the first user again
do("GET", "/api/BankAccounts/2", noH, "", 404, `id does not exist`) // // 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)
// Test that client have been updated after operations deletion and bank account deletion // do("GET", "/api/UserClients/1", noH, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[{"ID":1,"Number":"01-01","UserClientID":1,"Type":"checking-account","Amount":458,"BankOverdraft":-100,"Operations":null}]}`)
// Get client Dupond with his banks accounts and operations up to date (-100€ on checking-account)
do("GET", "/api/UserClients/1", noH, "", 200, `{"ID":1,"Name":"Dupond","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
// do("DELETE", "/api/UserClients/2", noH, ``, 200, "")
// Try to delete the saving account of Dupond // // Try to get the first user again
do("DELETE", "/api/UserClients/2", noH, ``, 200, "") // do("GET", "/api/UserClients/2", noH, "", 404, `id does not exist`)
// Try to get the first user again
do("GET", "/api/UserClients/2", noH, "", 404, `id does not exist`) // // Test that bank account have been deleted after client deletion
// do("GET", "/api/BankAccounts/3", noH, "", 404, `id does not exist`)
// Test that bank account have been deleted after client deletion
do("GET", "/api/BankAccounts/3", noH, "", 404, `id does not exist`)
// remove db test // remove db test
os.RemoveAll("./data") os.RemoveAll("./data")
......
...@@ -89,22 +89,24 @@ func createUnLoggedTests(t *testing.T) func(wg *sync.WaitGroup) { ...@@ -89,22 +89,24 @@ func createUnLoggedTests(t *testing.T) func(wg *sync.WaitGroup) {
return func(wg *sync.WaitGroup) { return func(wg *sync.WaitGroup) {
defer ts.Close() // Close the tester defer ts.Close() // Close the tester
defer wg.Done() defer wg.Done()
// Try to get the apps (must fail) // Without authent API calls must fails with 403 user could not be got from context
do("GET", "/api/admin/users/", noH, "", 401, "error extracting token") // Try to get the first client
// Try to create an user (must fail) do("GET", "/api/UserClients/1", noH, "", 401, `error extracting token`)
do("POST", "/api/admin/users/", noH, newUser, 401, "error extracting token")
// Try to delete an user (must fail) // // Try to create an user (must fail)
do("DELETE", "/api/admin/users/0", noH, "", 401, "error extracting token") // do("POST", "/api/admin/users/", noH, newUser, 401, "error extracting token")
// Try to access the main page (must pass) // // Try to delete an user (must fail)
do("GET", "/", noH, "", 200, "<!DOCTYPE html>") // do("DELETE", "/api/admin/users/0", noH, "", 401, "error extracting token")
// Try to get the user informations (must fail) // // Try to access the main page (must pass)
do("GET", "/api/common/WhoAmI", noH, "", 401, "error extracting token") // do("GET", "/", noH, "", 200, "<!DOCTYPE html>")
// Do a in memory login with an unknown user // // Try to get the user informations (must fail)
do("POST", "/Login", noH, `{"login": "unknownuser","password": "password"}`, http.StatusForbidden, `user not found`) // do("GET", "/api/common/WhoAmI", noH, "", 401, "error extracting token")
// Do a in memory login with a known user but bad password // // Do a in memory login with an unknown user
do("POST", "/Login", noH, `{"login": "admin","password": "badpassword"}`, http.StatusForbidden, `user not found`) // do("POST", "/Login", noH, `{"login": "unknownuser","password": "password"}`, http.StatusForbidden, `user not found`)
// Try to get a share token (must fail) // // Do a in memory login with a known user but bad password
do("POST", "/api/common/Share", noH, `{"sharedfor":"guest","url":"userdav.vestibule.io/mydata/test.txt","lifespan":1,"readonly":true}`, 401, "error extracting token") // do("POST", "/Login", noH, `{"login": "admin","password": "badpassword"}`, http.StatusForbidden, `user not found`)
// // Try to get a share token (must fail)
// do("POST", "/api/common/Share", noH, `{"sharedfor":"guest","url":"userdav.vestibule.io/mydata/test.txt","lifespan":1,"readonly":true}`, 401, "error extracting token")
} }
} }
...@@ -122,17 +124,8 @@ func createUserTests(t *testing.T) func(wg *sync.WaitGroup) { ...@@ -122,17 +124,8 @@ func createUserTests(t *testing.T) func(wg *sync.WaitGroup) {
response := do("GET", "/api/common/WhoAmI", noH, "", 200, "") response := do("GET", "/api/common/WhoAmI", noH, "", 200, "")
token := auth.TokenData{} token := auth.TokenData{}
json.Unmarshal([]byte(response), &token) json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken} // xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
// Try to get the users (must fail)
do("GET", "/api/admin/users/", xsrfHeader, "", 403, "no user role among")
// Try to create an user (must fail)
do("POST", "/api/admin/users/", xsrfHeader, newUser, 403, "no user role among")
// Try to delete an user (must fail)
do("DELETE", "/api/admin/users/0", xsrfHeader, "", 403, "no user role among")
// Try to access the main page (must pass)
do("GET", "/", xsrfHeader, "", 200, "<!DOCTYPE html>")
// Try to get the user informations (must pass)
do("GET", "/api/common/WhoAmI", xsrfHeader, "", 200, `{"id":`)
} }
// Try to login with OAuth2 (must pass) // Try to login with OAuth2 (must pass)
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>") do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
...@@ -164,13 +157,15 @@ func createAdminTests(t *testing.T) func(wg *sync.WaitGroup) { ...@@ -164,13 +157,15 @@ func createAdminTests(t *testing.T) func(wg *sync.WaitGroup) {
token := auth.TokenData{} token := auth.TokenData{}
json.Unmarshal([]byte(response), &token) json.Unmarshal([]byte(response), &token)
xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken} xsrfHeader := tester.Header{Key: "XSRF-TOKEN", Value: token.XSRFToken}
do("GET", "/api/admin/users/", xsrfHeader, "", 200, `[{"id":"1",`)
// Try to create an user (must pass) // Try to create a client
do("POST", "/api/admin/users/", xsrfHeader, newUser, 200, `[{"id":"1",`) do("POST", "/api/UserClients", xsrfHeader, `{"Name":"Dupond"}`, 200, "")
// Try to delete an user (must pass) // Try to create an other one
do("DELETE", "/api/admin/users/3", xsrfHeader, "", 200, `[{"id":"1",`) do("POST", "/api/UserClients", xsrfHeader, `{"Name":"Boulangerie"}`, 200, "")
// Try to get the user informations (must pass) // Try to get the first client
do("GET", "/api/common/WhoAmI", xsrfHeader, "", 200, `{"id":`) do("GET", "/api/UserClients/1", xsrfHeader, "", 200, `{"ID":1,"Name":"Dupond","BankAccounts":[]}`)
// Try to get all the clients
do("GET", "/api/UserClients", xsrfHeader, "", 200, `[{"ID":1,"Name":"Dupond","BankAccounts":[]},{"ID":2,"Name":"Boulangerie","BankAccounts":[]}]`)
} }
// Try to login (must pass) // Try to login (must pass)
do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>") do("GET", "/OAuth2Login", noH, "", 200, "<!DOCTYPE html>")
...@@ -179,6 +174,7 @@ func createAdminTests(t *testing.T) func(wg *sync.WaitGroup) { ...@@ -179,6 +174,7 @@ func createAdminTests(t *testing.T) func(wg *sync.WaitGroup) {
// Try to logout (must pass) // Try to logout (must pass)
do("GET", "/Logout", noH, "", 200, "Logout OK") do("GET", "/Logout", noH, "", 200, "Logout OK")
// Do a in memory login with an known admin // Do a in memory login with an known admin
resetData()
do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "") do("POST", "/Login", noH, `{"login": "admin","password": "password"}`, 200, "")
tests() tests()
// Try to logout (must pass) // Try to logout (must pass)
...@@ -186,10 +182,13 @@ func createAdminTests(t *testing.T) func(wg *sync.WaitGroup) { ...@@ -186,10 +182,13 @@ func createAdminTests(t *testing.T) func(wg *sync.WaitGroup) {
} }
} }
func createTester(t *testing.T) (*httptest.Server, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string) { func resetData() {
os.Remove("./data/test.db") os.Remove("./data/test.db")
os.Mkdir("data", os.ModePerm) os.Mkdir("data", os.ModePerm)
os.Create("./data/test.db") os.Create("./data/test.db")
}
func createTester(t *testing.T) (*httptest.Server, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string, func(method string, url string, header tester.Header, payload string, expectedStatus int, expectedBody string) string) {
// Create the server // Create the server
mux := CreateRootMux(1443, "../../web") mux := CreateRootMux(1443, "../../web")
ts := httptest.NewServer(mux.Mux) ts := httptest.NewServer(mux.Mux)
...@@ -199,7 +198,6 @@ func createTester(t *testing.T) (*httptest.Server, func(method string, url strin ...@@ -199,7 +198,6 @@ func createTester(t *testing.T) (*httptest.Server, func(method string, url strin
mux.Manager.Hostname = "http://" + os.Getenv("HOSTNAME") + ":" + port mux.Manager.Hostname = "http://" + os.Getenv("HOSTNAME") + ":" + port
// Create the cookie jar // Create the cookie jar
jar, _ := cookiejar.New(nil) jar, _ := cookiejar.New(nil)
os.RemoveAll("./data")
// wrap the testing function // wrap the testing function
return ts, tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), jar), tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), nil) return ts, tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), jar), tester.CreateServerTester(t, port, os.Getenv("HOSTNAME"), nil)
} }
...@@ -190,3 +190,41 @@ func GetTokenData(r *http.Request) (TokenData, error) { ...@@ -190,3 +190,41 @@ func GetTokenData(r *http.Request) (TokenData, error) {
} }
return user, nil return user, nil
} }
// IsAllowed let to know if a user is in a list of group or not
func IsAllowed(w http.ResponseWriter, r *http.Request, allowedRoles []string) bool {
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)
}
return false
}
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)
return false
}
// Check XSRF Token
if checkXSRF && r.Header.Get("XSRF-TOKEN") != user.XSRFToken {
http.Error(w, "XSRF protection triggered", 401)
return false
}
err = checkUserHasRole(user, allowedRoles)
if err != nil {
http.Error(w, err.Error(), 403)
return false
}
return true
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment