Skip to content
Snippets Groups Projects
Commit 320d93c3 authored by Nicolas Pernoud's avatar Nicolas Pernoud
Browse files

feat: completed tests on rootmux

parent 36439b43
Branches master
No related tags found
No related merge requests found
......@@ -15,7 +15,12 @@ func CreateMock() *http.ServeMux {
redir := strings.Replace(query.Get("redirect_uri"), "/callback", "/api/oidc/callback", 1) + "?state=" + query.Get("state") + "&code=mock_code"
http.Redirect(w, r, redir, http.StatusFound)
})
// Returns authorization code back to the user for matcher use case
mux.HandleFunc("/auth2", func(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
redir := strings.Replace(query.Get("redirect_uri"), "/callback", "/api/matcher/callback", 1) + "?state=" + query.Get("state") + "&code=mock_code"
http.Redirect(w, r, redir, http.StatusFound)
})
// Returns access token back to the user
mux.HandleFunc("/token", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
......@@ -26,6 +31,10 @@ func CreateMock() *http.ServeMux {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"given_name":"Angela Claire Louise","family_name":"DUBOIS","birthdate":"1962-08-24","gender":"female","birthplace":"75107","birthcountry":"99100","preferred_username":"","sub":"b6048e95bb134ec5b1d1e1fa69f287172e91722b9354d637a1bcf2ebb0fd2ef5v1"}`))
})
mux.HandleFunc("/userinfo2", func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"given_name":"Paul Louis","family_name":"DUPONT","birthdate":"1962-08-24","gender":"male","birthplace":"75107","birthcountry":"99100","preferred_username":"","sub":"dcc2d409424c519ae0599c8585b585711020bd4035b91633e9eabaa9b7542721v1"}`))
})
// Logout
mux.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "Logout OK")
......
......@@ -65,7 +65,7 @@ func CreateMatcherServer() *http.ServeMux {
}
// Send a mail to the CEO with the aforementionned token
err = sendMailToMandater(string(email), rd.Id.GivenName+" "+rd.Id.FamilyName, rd.Sirent, token, r.Host)
err = sendMailToMandater(string(email), rd.Id.GivenName+" "+rd.Id.FamilyName, rd.Sirent, token, common.GetDomain(r))
if err != nil {
http.Error(w, "could not send email", http.StatusBadRequest)
return
......@@ -80,10 +80,10 @@ func CreateMatcherServer() *http.ServeMux {
tokens.ExtractAndValidateToken(r, "", &md, false)
// Store the demand in a cookie
tokens.CreateCookie(md, r.Host, "MandateDemand", 60*time.Second, w)
tokens.CreateCookie(md, common.GetDomain(r), "MandateDemand", 60*time.Second, w)
// Set a cookie to redirect to the needed callback route from the front end generic callback
cookie := http.Cookie{Name: "callbackRoute", Domain: r.Host, Path: "/", Value: "/api/matcher/callback", MaxAge: 60, Secure: false, HttpOnly: false, SameSite: http.SameSiteLaxMode}
cookie := http.Cookie{Name: "callbackRoute", Domain: common.GetDomain(r), Path: "/", Value: "/api/matcher/callback", MaxAge: 60, Secure: false, HttpOnly: false, SameSite: http.SameSiteLaxMode}
http.SetCookie(w, &cookie)
// Perform an France Connect authentication ... and wait for the callback
......@@ -126,7 +126,6 @@ func CreateMatcherServer() *http.ServeMux {
return
}
w.Write([]byte("the mandate could not be created, you do not seem to be the company CEO"))
return
// TODO : Inform the original asker that is demand has been validated/rejected
})
......@@ -170,3 +169,11 @@ func sendMailToMandater(email string, mandate string, sirent string, token strin
// Sending email.
return mailSender.Send(to, body.Bytes())
}
// SetTestMailSender allows overriding mail sender for test purposes
func SetTestModeAndReturnRecorder() *email.EmailRecorder {
mailTemplate = "../../mailtemplate.html"
sender, recorder := email.NewMockSender()
mailSender = sender
return recorder
}
......@@ -3,14 +3,10 @@ package matcher
import (
"strings"
"testing"
"forge.grandlyon.com/npernoud/glcpro/pkg/email"
)
func Test_sendMailToMandater(t *testing.T) {
mailTemplate = "../../mailtemplate.html"
sender, recorder := email.NewMockSender()
mailSender = sender
recorder := SetTestModeAndReturnRecorder()
type args struct {
email string
mandate string
......
......@@ -2,6 +2,7 @@ package rootmux
import (
"encoding/base64"
"fmt"
"net/http/cookiejar"
"net/http/httptest"
"net/url"
......@@ -13,17 +14,21 @@ import (
"forge.grandlyon.com/npernoud/glcpro/internal/apientreprise"
"forge.grandlyon.com/npernoud/glcpro/internal/franceconnect"
"forge.grandlyon.com/npernoud/glcpro/internal/matcher"
"forge.grandlyon.com/npernoud/glcpro/pkg/tester"
"forge.grandlyon.com/npernoud/glcpro/pkg/tokens"
)
var (
noH map[string]string
noH map[string]string
fcServer *httptest.Server
clientRedirectURI = "http://client.org"
tokenMustContent = `"id_token":{"given_name":"Angela`
)
func TestMain(m *testing.M) {
// Create the france connect mock server
fcServer := httptest.NewServer(franceconnect.CreateMock())
fcServer = httptest.NewServer(franceconnect.CreateMock())
defer fcServer.Close()
// Setup to use the france connect mock server
os.Setenv("FC_AUTH", fcServer.URL+"/auth")
......@@ -37,6 +42,8 @@ func TestMain(m *testing.M) {
apientreprise.Init("../../configs/apicache.json")
code := m.Run()
// Remove the database
os.Remove("./glcpro.db")
os.Exit(code)
}
......@@ -55,18 +62,22 @@ func createTester(t *testing.T) (*httptest.Server, tester.DoFn, tester.DoFn) {
// Test the use case 1 : "Cas d'usage 1 : Accès à une démarche depuis un portail de service public pour un dirigeant"
func TestUseCase1(t *testing.T) {
clientRedirectURI := "http://client.org"
// Create the tester
_, do, _ := createTester(t)
// (We arrive from a client with a query, and the front end add the requested SIRENT to the query), we should be redirected to france connect, login, and be back with an authorisation code
r := do("GET", "/api/oidc/auth?scope=openid%20profile&client_id=A_RANDOM_ID&redirect_uri="+clientRedirectURI+"&response_type=code&state=A_RANDOM_STATE&sirent=000000001", noH, "", 302, "")
// We are redirected to France Connect
r = redirectURIFromBody(r)
fmt.Printf("Redirected to France Connect : %v\n", r)
r = do("GET", r, noH, "", 302, "")
// We are redirected to GLC Pro (France Connect callback)
r = redirectURIFromBody(r)
fmt.Printf("Redirected to GLC Pro callback : %v\n", r)
r = do("GET", r, noH, "", 302, "")
// We are redirected to the client
r = redirectURIFromBody(r)
fmt.Printf("Redirected to Client : %v\n", r)
if !strings.Contains(r, clientRedirectURI) {
t.Errorf("no redirection to the client")
}
......@@ -75,9 +86,87 @@ func TestUseCase1(t *testing.T) {
tk := do("POST", "/api/oidc/token", noH, "client_id=A_RANDOM_ID&client_secret=A_RANDOM_SECRET&grant_type=authorization_code&code="+code, 200, "")
tk = regexp.MustCompile(`id_token=(.*)&scope.*`).FindStringSubmatch(tk)[1]
token, _ := base64.StdEncoding.DecodeString(tk)
if !strings.Contains(string(token), "Angela Claire Louise") || !strings.Contains(string(token), "THE TEST COMPANY") {
if !strings.Contains(string(token), tokenMustContent) || !strings.Contains(string(token), "THE TEST COMPANY") {
t.Errorf("id token is not complete")
}
fmt.Printf("Token : %v\n", string(token))
}
// Test the use case 2 : "Cas d'usage 2 : Demande d'habilitation par un dirigeant"
func TestUseCase2(t *testing.T) {
// Create the tester
_, do, _ := createTester(t)
// Create the mock mail server
recorder := matcher.SetTestModeAndReturnRecorder()
/////////////////////////////////////
// THE MANDATEE MAKES THE DEMAND //
/////////////////////////////////////
// (We arrive from a client with a query, and the front end add the requested SIRENT to the query), we should be redirected to france connect, login, .. and do NOT get a code but be redirect on the mandate demand page
// Configure FC Mock to give the ID of Paul Louis Dupont
os.Setenv("FC_USER_INFO", fcServer.URL+"/userinfo2")
franceconnect.Init()
r := do("GET", "/api/oidc/auth?scope=openid%20profile&client_id=A_RANDOM_ID&redirect_uri="+clientRedirectURI+"&response_type=code&state=A_RANDOM_STATE&sirent=000000001", noH, "", 302, "")
// We are redirected to France Connect
r = redirectURIFromBody(r)
fmt.Printf("Redirected to France Connect : %v\n", r)
r = do("GET", r, noH, "", 302, "")
// We are redirected to GLC Pro (France Connect callback)
r = redirectURIFromBody(r)
fmt.Printf("Redirected to GLC Pro callback : %v\n", r)
r = do("GET", r, noH, "", 302, "")
// We are redirected to the matcher
r = redirectURIFromBody(r)
fmt.Printf("Redirected to GLC Pro matcher : %v\n", r)
// We are redirected to the matcher
if !strings.Contains(r, "/matcher") {
t.Errorf("no redirection to the matcher")
}
// Let's send a mail to the company CEO
do("POST", "/api/matcher/demand", noH, "angela@testcompany.com", 200, "")
fmt.Printf("Sent mail : %v\n", recorder.Msg())
if !strings.Contains(recorder.Msg(), "Demande de mandatement") {
t.Errorf("received body is not what is expected")
}
////////////////////////////////////////////
// THE COMPANY CEO VALIDATES THE DEMAND //
////////////////////////////////////////////
// Configure FC Mock to give the ID of Angela Claire Louise DUBOIS, configure the FC Auth to callback to the matcher (what is normaly made by the js front client)
os.Setenv("FC_USER_INFO", fcServer.URL+"/userinfo")
os.Setenv("FC_AUTH", fcServer.URL+"/auth2")
franceconnect.Init()
// Create a new tester with a new cookie jar (because we are someone else, on a different computer)
_, do, _ = createTester(t)
// Extract the link from the mail
r = do("GET", "/api/matcher/validate?code="+regexp.MustCompile(`code=(.*)"`).FindStringSubmatch(recorder.Msg())[1], noH, "", 302, "")
// We are redirected to France Connect
r = redirectURIFromBody(r)
fmt.Printf("Redirected to France Connect : %v\n", r)
r = do("GET", r, noH, "", 302, "")
fmt.Printf("Body 5 : %v\n", r)
r = redirectURIFromBody(r)
r = do("GET", r, noH, "", 302, "")
fmt.Printf("Body 6 : %v\n", r)
r = redirectURIFromBody(r)
if r != "/mandatecreated" {
t.Errorf("CEO was not redirected to the mandate created information")
}
///////////////////////////////////////////////////
// THE MANDATEE USE THE NEWLY OBTAINED MANDATE //
///////////////////////////////////////////////////
// It's actually the use case 1, only with France Connect mock giving the identity of Paul Louis Dubois
os.Setenv("FC_USER_INFO", fcServer.URL+"/userinfo2")
os.Setenv("FC_AUTH", fcServer.URL+"/auth")
franceconnect.Init()
tokenMustContent = `"id_token":{"given_name":"Paul`
TestUseCase1(t)
}
func redirectURIFromBody(body string) string {
......
......@@ -34,7 +34,7 @@ type webSecurityWriter struct {
}
func (s webSecurityWriter) WriteHeader(code int) {
if s.wroteHeader == false {
if !s.wroteHeader {
s.w.Header().Set("Strict-Transport-Security", "max-age=63072000")
var inline string
if s.allowEvalInlineScript {
......@@ -57,6 +57,7 @@ func (s webSecurityWriter) WriteHeader(code int) {
s.w.Header().Set("X-XSS-Protection", "1; mode=block")
s.w.Header().Set("Referrer-Policy", "strict-origin")
s.w.Header().Set("X-Content-Type-Options", "nosniff")
//lint:ignore SA4005 we need to assign true so that when the WriteHeader method will be used again, we won't rewrite security headers
s.wroteHeader = true
}
s.w.WriteHeader(code)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment