From d3d26e8db3b8c70590298d8013d09ceb0c78eac9 Mon Sep 17 00:00:00 2001 From: Nicolas Pernoud <github@ninico.fr> Date: Fri, 18 Jun 2021 16:27:27 +0200 Subject: [PATCH] feat: fully functionning mandatement --- TODO.md | 2 + go.mod | 5 +- go.sum | 17 ++++- internal/mandate/mandate.go | 38 ++++++++++ internal/mandate/mandate_test.go | 30 ++++++++ mailtemplate.html | 8 +- pkg/franceconnect/franceconnect.go | 91 +++++++++++++++++++++++ pkg/matcher/mailtemplate.html | 8 +- pkg/matcher/matcher.go | 56 +++++++++++++- pkg/oidcserver/oidcserver.go | 115 +++++++---------------------- web/components/auth/auth.js | 1 + web/components/matcher/matcher.js | 2 +- web/main.js | 14 +++- 13 files changed, 283 insertions(+), 104 deletions(-) create mode 100644 internal/mandate/mandate.go create mode 100644 internal/mandate/mandate_test.go create mode 100644 pkg/franceconnect/franceconnect.go diff --git a/TODO.md b/TODO.md index 6682a5d..3718a82 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,5 @@ [ ] Remove references to localhost and ports [ ] Tests [ ] Remove useless fmt.Print +[ ] Obviously : TODOs +[ ] Css and JS messages diff --git a/go.mod b/go.mod index d328a29..54cd2c8 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,9 @@ go 1.16 require ( github.com/davecgh/go-spew v1.1.1 // indirect + github.com/mattn/go-sqlite3 v1.14.7 // indirect github.com/oschwald/maxminddb-golang v1.8.0 - golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 // indirect + golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71 // indirect + gorm.io/driver/sqlite v1.1.4 + gorm.io/gorm v1.21.10 ) diff --git a/go.sum b/go.sum index e838a1e..2272e66 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= +github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/mattn/go-sqlite3 v1.14.5/go.mod h1:WVKg1VTActs4Qso6iwGbiFih2UIHo0ENGwNd0Lj+XmI= +github.com/mattn/go-sqlite3 v1.14.7 h1:fxWBnXkxfM6sRiuH3bqJ4CfzZojMOLVc0UTsTglEghA= +github.com/mattn/go-sqlite3 v1.14.7/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/oschwald/maxminddb-golang v1.8.0 h1:Uh/DSnGoxsyp/KYbY1AuP0tYEwfs0sCph9p/UMXK/Hk= github.com/oschwald/maxminddb-golang v1.8.0/go.mod h1:RXZtst0N6+FY/3qCNmZMBApR19cdQj43/NM9VkrNAis= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -9,8 +17,13 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= golang.org/x/sys v0.0.0-20191224085550-c709ea063b76/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210608053332-aa57babbf139 h1:C+AwYEtBp/VQwoLntUmQ/yx3MS9vmZaKNdw5eOpoQe8= -golang.org/x/sys v0.0.0-20210608053332-aa57babbf139/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71 h1:X/2sJAybVknnUnV7AD2HdT6rm2p5BP6eH2j+igduWgk= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.1.4 h1:PDzwYE+sI6De2+mxAneV9Xs11+ZyKV6oxD3wDGkaNvM= +gorm.io/driver/sqlite v1.1.4/go.mod h1:mJCeTFr7+crvS+TRnWc5Z3UvwxUN1BGBLMrf5LA9DYw= +gorm.io/gorm v1.20.7/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw= +gorm.io/gorm v1.21.10 h1:kBGiBsaqOQ+8f6S2U6mvGFz6aWWyCeIiuaFcaBozp4M= +gorm.io/gorm v1.21.10/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= diff --git a/internal/mandate/mandate.go b/internal/mandate/mandate.go new file mode 100644 index 0000000..3a93b30 --- /dev/null +++ b/internal/mandate/mandate.go @@ -0,0 +1,38 @@ +package mandate + +import ( + "gorm.io/driver/sqlite" + "gorm.io/gorm" +) + +type Mandate struct { + gorm.Model + Sirent string + MandateeSub string + MandaterSub string + // TODO : Expiration date +} + +var db *gorm.DB + +func init() { + var err error + db, err = gorm.Open(sqlite.Open("glcpro.db"), &gorm.Config{}) + if err != nil { + panic("failed to connect database") + } + // Migrate the schema + db.AutoMigrate(&Mandate{}) +} + +func Exists(sirent string, mandateeSub string) bool { + var mandate Mandate + if err := db.Where("sirent = ? AND mandatee_sub = ?", sirent, mandateeSub).First(&mandate).Error; err != nil { + return false + } + return true +} + +func Create(sirent string, mandateeSub string, mandaterSub string) { + db.Create(&Mandate{Sirent: sirent, MandateeSub: mandateeSub, MandaterSub: mandaterSub}) +} diff --git a/internal/mandate/mandate_test.go b/internal/mandate/mandate_test.go new file mode 100644 index 0000000..e5c1015 --- /dev/null +++ b/internal/mandate/mandate_test.go @@ -0,0 +1,30 @@ +package mandate + +import ( + "testing" +) + +func TestExists(t *testing.T) { + Create("001", "101", "201") + type args struct { + sirent string + mandateeSub string + } + tests := []struct { + name string + args args + want bool + }{ + {"exists", args{sirent: "001", mandateeSub: "101"}, true}, + {"bad_mandatee", args{sirent: "001", mandateeSub: "102"}, false}, + {"bad_sirent", args{sirent: "002", mandateeSub: "101"}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + if got := Exists(tt.args.sirent, tt.args.mandateeSub); got != tt.want { + t.Errorf("Exists() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/mailtemplate.html b/mailtemplate.html index 35abb6a..abf7d3b 100644 --- a/mailtemplate.html +++ b/mailtemplate.html @@ -7,8 +7,12 @@ l'entreprise ou de l'établissement portant le numéro SIREN/SIRET {{.Sirent}}. </p> - <p>Pour valider cette demande, veuillez cliquer sur ce lien :</p> - <a href="localhost:8080/api/matcher/validate&code={{.Token}}">Valider</a> + <p> + Pour valider cette demande, veuillez cliquer sur ce lien : + <a href="http://localhost:8080/api/matcher/validate?code={{.Token}}" + >Valider.</a + > + </p> <p> Pour refuser cette demande, il suffit de ne rien faire et de supprimer ce mail. diff --git a/pkg/franceconnect/franceconnect.go b/pkg/franceconnect/franceconnect.go new file mode 100644 index 0000000..bd07b87 --- /dev/null +++ b/pkg/franceconnect/franceconnect.go @@ -0,0 +1,91 @@ +package franceconnect + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "strconv" + "strings" +) + +type Identity struct { + GivenName string `json:"given_name"` + FamilyName string `json:"family_name"` + Birthdate string `json:"birthdate"` + Gender string `json:"gender"` + Birthplace string `json:"birthplace"` + Birthcountry string `json:"birthcountry"` + PreferredUsername string `json:"preferred_username"` + Sub string `json:"sub"` +} + +func RedirectToFranceConnect(w http.ResponseWriter, r *http.Request) { + // TODO : France Connect parameters as env variables + fcAuth := "https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize" + + fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e" + params := url.Values{} + // TODO : State and nonce + params.Add("state", "A_RANDOM_STATE") + params.Add("nonce", "A_RANDOM_NONCE") + params.Add("scope", "openid identite_pivot") + // TODO : Redirect from request + params.Add("redirect_uri", "http://localhost:8080"+"/callback") + params.Add("client_id", fcClientID) + params.Add("response_type", "code") + http.Redirect(w, r, fcAuth+"?"+params.Encode(), http.StatusFound) +} + +func HandleCallBack(w http.ResponseWriter, r *http.Request) (Identity, error) { + // TODO : fcToken and client secret from env + fcToken := "https://fcp.integ01.dev-franceconnect.fr/api/v1/token" + fcUserInfo := "https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo?schema=openid" + fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e" + fcClientSecret := "2791a731e6a59f56b6b4dd0d08c9b1f593b5f3658b9fd731cb24248e2669af4b" + // TODO : Check the state + fcCode := r.URL.Query().Get("code") + data := url.Values{} + data.Set("grant_type", "authorization_code") + data.Set("redirect_uri", "http://localhost:8080/callback") + data.Set("client_id", fcClientID) + data.Set("client_secret", fcClientSecret) + data.Set("code", fcCode) + + client := &http.Client{} + req, err := http.NewRequest(http.MethodPost, fcToken, strings.NewReader(data.Encode())) + if err != nil { + return Identity{}, fmt.Errorf("could not perform the request to France Connect token url") + } + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) + + resp, err := client.Do(req) + if err != nil { + return Identity{}, fmt.Errorf("could not perform the request to France Connect token url") + } + // Get the access_token information + rBody, _ := ioutil.ReadAll(resp.Body) + fmt.Printf("rBody: %v\n", string(rBody)) + accessToken := regexp.MustCompile(`.*"access_token":"([^"]*)`).FindStringSubmatch(string(rBody))[1] + + // Call the user info endpoint + req, err = http.NewRequest(http.MethodGet, fcUserInfo, nil) + if err != nil { + return Identity{}, fmt.Errorf("could not perform the request to France Connect userinfo url") + } + req.Header.Add("Authorization", "Bearer "+accessToken) + resp, err = client.Do(req) + if err != nil { + return Identity{}, fmt.Errorf("could not perform the request to France Connect userinfo url") + } + rBodyFC, _ := ioutil.ReadAll(resp.Body) + i := Identity{} + err = json.Unmarshal(rBodyFC, &i) + if err != nil { + return Identity{}, fmt.Errorf("unmarshall the France Connect user info") + } + return i, nil +} diff --git a/pkg/matcher/mailtemplate.html b/pkg/matcher/mailtemplate.html index 35abb6a..abf7d3b 100644 --- a/pkg/matcher/mailtemplate.html +++ b/pkg/matcher/mailtemplate.html @@ -7,8 +7,12 @@ l'entreprise ou de l'établissement portant le numéro SIREN/SIRET {{.Sirent}}. </p> - <p>Pour valider cette demande, veuillez cliquer sur ce lien :</p> - <a href="localhost:8080/api/matcher/validate&code={{.Token}}">Valider</a> + <p> + Pour valider cette demande, veuillez cliquer sur ce lien : + <a href="http://localhost:8080/api/matcher/validate?code={{.Token}}" + >Valider.</a + > + </p> <p> Pour refuser cette demande, il suffit de ne rien faire et de supprimer ce mail. diff --git a/pkg/matcher/matcher.go b/pkg/matcher/matcher.go index 657e767..5c07c24 100644 --- a/pkg/matcher/matcher.go +++ b/pkg/matcher/matcher.go @@ -10,7 +10,10 @@ import ( "regexp" "time" + "forge.grandlyon.com/npernoud/glcpro/internal/mandate" + "forge.grandlyon.com/npernoud/glcpro/pkg/apientreprise" "forge.grandlyon.com/npernoud/glcpro/pkg/common" + "forge.grandlyon.com/npernoud/glcpro/pkg/franceconnect" "forge.grandlyon.com/npernoud/glcpro/pkg/oidcserver" "forge.grandlyon.com/npernoud/glcpro/pkg/tokens" ) @@ -68,16 +71,61 @@ func CreateMatcherServer() *http.ServeMux { mux.HandleFunc("/validate", func(w http.ResponseWriter, r *http.Request) { // Unpack the token to work out who the mandate is for + md := MandateDemand{} + tokens.ExtractAndValidateToken(r, "", &md, false) - // Perform an France Connect authentication + // Store the demand in a cookie + tokens.CreateCookie(md, "localhost", "MandateDemand", 60*time.Second, w) - // Check that the France Connect user can create a mandate : he is the CEO of the company or has a transitive mandate + // Set a cookie to redirect to the needed callback route from the front end generic callback + cookie := http.Cookie{Name: "callbackRoute", Domain: "localhost", 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 + franceconnect.RedirectToFranceConnect(w, r) + + }) + + mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { + // Get the mandater identity from + i, err := franceconnect.HandleCallBack(w, r) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Get the redirect url from the cookie + md := MandateDemand{} + _, err = tokens.ExtractAndValidateToken(r, "MandateDemand", &md, false) + if err != nil { + http.Error(w, "could not get the mandate demand data from cookie", http.StatusInternalServerError) + return + } - // Create the mandate + // Check that the France Connect user can create a mandate : he is the CEO of the company or has a transitive mandate + // Get the data from the API Entreprise + e, _, err := apientreprise.GetData(md.Sirent) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } - // TODO : Inform the original asket that is demand has been validated/rejected + // TODO : Iterate on all MandatairesSociaux and clean the big if, refactor with oidcserver + if len(e.MandatairesSociaux) < 1 { + http.Error(w, "there is no Mandataires Sociaux for this SIREN/T", http.StatusOK) + return + } + if e.MandatairesSociaux[0].Nom == i.FamilyName || e.MandatairesSociaux[0].Prenom == i.GivenName || e.MandatairesSociaux[0].DateNaissance == i.Birthdate { + // Create the mandate + mandate.Create(md.Sirent, md.AskerSub, i.Sub) + w.Write([]byte("the mandate was successfully created ")) + 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 }) + return mux } diff --git a/pkg/oidcserver/oidcserver.go b/pkg/oidcserver/oidcserver.go index 9a07bc5..28ae9df 100644 --- a/pkg/oidcserver/oidcserver.go +++ b/pkg/oidcserver/oidcserver.go @@ -3,17 +3,15 @@ package oidcserver import ( "encoding/base64" "encoding/json" - "fmt" - "io/ioutil" "net/http" "net/url" - "regexp" - "strconv" "strings" "time" + "forge.grandlyon.com/npernoud/glcpro/internal/mandate" "forge.grandlyon.com/npernoud/glcpro/pkg/apientreprise" "forge.grandlyon.com/npernoud/glcpro/pkg/common" + "forge.grandlyon.com/npernoud/glcpro/pkg/franceconnect" "forge.grandlyon.com/npernoud/glcpro/pkg/tokens" ) @@ -21,25 +19,14 @@ type ClientRequestData struct { RedirectURI string State string Sirent string - Id FCIdentity + Id franceconnect.Identity } type GLCId struct { - Id FCIdentity `json:"id_token"` + Id franceconnect.Identity `json:"id_token"` E apientreprise.Entreprise `json:"entreprise"` } -type FCIdentity struct { - GivenName string `json:"given_name"` - FamilyName string `json:"family_name"` - Birthdate string `json:"birthdate"` - Gender string `json:"gender"` - Birthplace string `json:"birthplace"` - Birthcountry string `json:"birthcountry"` - PreferredUsername string `json:"preferred_username"` - Sub string `json:"sub"` -} - // CreateOIDCServer creates a Open ID Connect Server as proxy to France Connect func CreateOIDCServer() *http.ServeMux { rd := ClientRequestData{} @@ -79,80 +66,28 @@ func CreateOIDCServer() *http.ServeMux { rd.Sirent = query.Get("sirent") tokens.CreateCookie(rd, "localhost", "ClientRequestData", 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: "localhost", Path: "/", Value: "/api/oidc/callback", MaxAge: 60, Secure: false, HttpOnly: false, SameSite: http.SameSiteLaxMode} + http.SetCookie(w, &cookie) + // Redirect to France Connect with the callback as parameter - // TODO : France Connect parameters as env variables - fcAuth := "https://fcp.integ01.dev-franceconnect.fr/api/v1/authorize" - - fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e" - params := url.Values{} - // TODO : State and nonce - params.Add("state", "A_RANDOM_STATE") - params.Add("nonce", "A_RANDOM_NONCE") - params.Add("scope", "openid identite_pivot") - // TODO : Redirect from request - params.Add("redirect_uri", "http://localhost:8080"+"/callback") - params.Add("client_id", fcClientID) - params.Add("response_type", "code") - http.Redirect(w, r, fcAuth+"?"+params.Encode(), http.StatusFound) + franceconnect.RedirectToFranceConnect(w, r) // And that's all the reste will be handed by France Connect callback : get the France Connect identity, do the API Entreprise call, get the original parameters from the client and redirect to the original client }) // Handles the France Connect Callback, get the user info from the token, and end the original response mux.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) { - // TODO : fcToken and client secret from env - fcToken := "https://fcp.integ01.dev-franceconnect.fr/api/v1/token" - fcUserInfo := "https://fcp.integ01.dev-franceconnect.fr/api/v1/userinfo?schema=openid" - fcClientID := "211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6e" - fcClientSecret := "2791a731e6a59f56b6b4dd0d08c9b1f593b5f3658b9fd731cb24248e2669af4b" - // TODO : Check the state - fcCode := r.URL.Query().Get("code") - data := url.Values{} - data.Set("grant_type", "authorization_code") - data.Set("redirect_uri", "http://localhost:8080/callback") - data.Set("client_id", fcClientID) - data.Set("client_secret", fcClientSecret) - data.Set("code", fcCode) - - client := &http.Client{} - req, err := http.NewRequest(http.MethodPost, fcToken, strings.NewReader(data.Encode())) - if err != nil { - http.Error(w, "could not perform the request to France Connect token url", http.StatusInternalServerError) - return - } - req.Header.Add("Content-Type", "application/x-www-form-urlencoded") - req.Header.Add("Content-Length", strconv.Itoa(len(data.Encode()))) - resp, err := client.Do(req) + i, err := franceconnect.HandleCallBack(w, r) if err != nil { - http.Error(w, "could not perform the request to France Connect token url", http.StatusInternalServerError) - return - } - // Get the access_token information - rBody, _ := ioutil.ReadAll(resp.Body) - fmt.Printf("rBody: %v\n", string(rBody)) - accessToken := regexp.MustCompile(`.*"access_token":"([^"]*)`).FindStringSubmatch(string(rBody))[1] - - // Call the user info endpoint - req, err = http.NewRequest(http.MethodGet, fcUserInfo, nil) - if err != nil { - http.Error(w, "could not perform the request to France Connect userinfo url", http.StatusInternalServerError) - return - } - req.Header.Add("Authorization", "Bearer "+accessToken) - resp, err = client.Do(req) - if err != nil { - http.Error(w, "could not perform the request to France Connect userinfo url", http.StatusInternalServerError) + http.Error(w, err.Error(), http.StatusInternalServerError) return } - rBodyFC, _ := ioutil.ReadAll(resp.Body) - i := FCIdentity{} - json.Unmarshal(rBodyFC, &i) // Get the redirect url from the cookie rd := ClientRequestData{} _, err = tokens.ExtractAndValidateToken(r, "ClientRequestData", &rd, false) - fmt.Printf("Request Data:%v\n", rd) if err != nil { http.Error(w, "could not get the initial client request data from cookie", http.StatusInternalServerError) return @@ -165,19 +100,19 @@ func CreateOIDCServer() *http.ServeMux { return } - // Match the fetched company to the user company - // TODO : Check if there is a mandate - // TODO : Iterate on all MandatairesSociaux and clean the big if - if len(e.MandatairesSociaux) < 1 { - http.Error(w, "there is no Mandataires Sociaux for this SIREN/T", http.StatusOK) - return - } - if e.MandatairesSociaux[0].Nom != i.FamilyName || e.MandatairesSociaux[0].Prenom != i.GivenName || e.MandatairesSociaux[0].DateNaissance != i.Birthdate { // If not redirect to the matcher to ask for a mandate - // Add the asker identity to the request data - rd.Id = i - tokens.CreateCookie(rd, "localhost", "ClientRequestData", 60*time.Second, w) - http.Redirect(w, req, "/matcher", http.StatusFound) - return + if !mandate.Exists(rd.Sirent, i.Sub) { // If there is a mandate, continue + if len(e.MandatairesSociaux) < 1 { + http.Error(w, "there is no Mandataires Sociaux for this SIREN/T", http.StatusOK) + return + } + // TODO : Iterate on all MandatairesSociaux and clean the big if + if e.MandatairesSociaux[0].Nom != i.FamilyName || e.MandatairesSociaux[0].Prenom != i.GivenName || e.MandatairesSociaux[0].DateNaissance != i.Birthdate { // If not redirect to the matcher to ask for a mandate + // Add the asker identity to the request data + rd.Id = i + tokens.CreateCookie(rd, "localhost", "ClientRequestData", 60*time.Second, w) + http.Redirect(w, r, "/matcher", http.StatusFound) + return + } } // Redirect to the initial caller with an code that it actually the data bundled in an opaque token @@ -190,7 +125,7 @@ func CreateOIDCServer() *http.ServeMux { http.Error(w, "could not create an authorisation code", http.StatusInternalServerError) return } - http.Redirect(w, req, rd.RedirectURI+"?code="+url.QueryEscape(code), http.StatusFound) + http.Redirect(w, r, rd.RedirectURI+"?code="+url.QueryEscape(code), http.StatusFound) }) // Returns access token back to the user diff --git a/web/components/auth/auth.js b/web/components/auth/auth.js index 02e5197..4dd86dc 100644 --- a/web/components/auth/auth.js +++ b/web/components/auth/auth.js @@ -15,6 +15,7 @@ class Auth { <p>Pour essayer avec une autre entreprise, prendre par exemple la Fnac : 350127460</p> <input type="text" id="auth-sirent" value="000000001"></input> <button id="auth-do">OK</button> + <Pour tester un mandatement, utiliser la valeur "000000001", mais avec une connexion France Connect avec le compte "sans_nom_dusage".</p> `; // TODO : Check SIREN/T is in input and looks like a SIREN/T document.getElementById(`auth-do`).addEventListener("click", () => { diff --git a/web/components/matcher/matcher.js b/web/components/matcher/matcher.js index 2616f70..360b0a7 100644 --- a/web/components/matcher/matcher.js +++ b/web/components/matcher/matcher.js @@ -17,7 +17,7 @@ class Matcher { <p> Voulez vous qu'une demande de mandatement soit envoyée au dirigeant ? </p> - <input type="text" id="matcher-email" value="npernoud@grandlyon.com"></input> + <input type="text" id="matcher-email" value="nicolas@alpha.grandlyon.com"></input> <button id="matcher-do">OK</button> <button id="matcher-cancel">Annuler</button> `; diff --git a/web/main.js b/web/main.js index b42fa22..527d4df 100644 --- a/web/main.js +++ b/web/main.js @@ -22,8 +22,18 @@ async function navigate() { }); break; case "/callback": - //Redirect to server callback - location.pathname = "/api/oidc/callback"; + // Redirect to server callback according to cookie + try { + const callbackRoute = document.cookie + .split("; ") + .find((row) => row.startsWith("callbackRoute=")) + .split("=")[1]; + if (callbackRoute != "" && callbackRoute != null) { + location.pathname = callbackRoute; + } + } catch (e) { + console.error("no callback route was gotten from the cookie"); + } break; default: location.pathname = "auth"; -- GitLab