diff --git a/.env.template b/.env.template index f2b86fe0f18ed30bb1bb2410f301e339ea194bf4..1771b0ddd7123700a539a873be9ca5d6e5eb230c 100644 --- a/.env.template +++ b/.env.template @@ -1,6 +1,7 @@ # Common settings HOSTNAME=localhost -ADMIN_ROLE +ANIMATOR_ROLE=ANIMATORS +ADMIN_ROLE=ADMINS DEBUG_MODE MOCK_OAUTH2 HTTPS_PORT diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 2fe17c006fe49b7dbaeb3fecc15bc66cf6b7e8e8..0000000000000000000000000000000000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - // Utilisez IntelliSense pour en savoir plus sur les attributs possibles. - // Pointez pour afficher la description des attributs existants. - // Pour plus d'informations, visitez : https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Debug Back-office with Mock OAuth2", - "type": "go", - "request": "launch", - "mode": "debug", - "program": "${workspaceFolder}/main.go", - "envFile": "${workspaceFolder}/.env", - "showLog": true - }, - { - "name": "Debug Back-office with Sign&Go", - "type": "go", - "request": "launch", - "mode": "debug", - "program": "${workspaceFolder}/main.go", - "env": { - "REDIRECT_URL": "https://localhost:1443/OAuth2Callback", - "CLIENT_ID": "<GET ONE FROM YOUR IDP>", - "CLIENT_SECRET": "<GET ONE FROM YOUR IDP>", - "AUTH_URL": "https://connexion-rec.grandlyon.fr/IdPOAuth2/authorize/oidc-rec", - "TOKEN_URL": "https://connexion-rec.grandlyon.fr/IdPOAuth2/token/oidc-rec", - "USERINFO_URL": "https://connexion-rec.grandlyon.fr/IdPOAuth2/userinfo/oidc-rec", - "LOGOUT_URL": "https://connexion-rec.grandlyon.fr/auth/logout.jsp", - "ADMIN_ROLE": "GGD_ORG_DG-DEES-DINSI-DAAG_TOUS", - "HOSTNAME": "ecolyobackoffice.127.0.0.1.nip.io", - "DEBUG_MODE": "true", - "HTTPS_PORT": "1443" - }, - "showLog": true - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json index 5bef7010d562b29263aca212431df54eb8652a7d..11a1978e8b65f62fa6d8ce4f9b7fba81a8f0623b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -20,6 +20,8 @@ }, "peacock.color": "#32f0ff", "cSpell.words": [ + "admininfo", + "animatorinfo", "backoffice", "ecogesture", "ecolyo", diff --git a/docker-compose.deploy.yml b/docker-compose.deploy.yml deleted file mode 100644 index cff867a3a615c96e95629b90d153003321662020..0000000000000000000000000000000000000000 --- a/docker-compose.deploy.yml +++ /dev/null @@ -1,58 +0,0 @@ -version: '3.7' -services: - database-agent: - image: mysql:8 - networks: - backoffice: - restart: always - volumes: - - /mnt/local/data/ecolyo_agent_mysql_data:/var/lib/mysql - - ./dbinit:/dbinit - environment: - MYSQL_ROOT_PASSWORD: ${DATABASE_PASSWORD} - MYSQL_DATABASE: ${DATABASE_NAME} - healthcheck: - test: mysqladmin ping -h 127.0.0.1 -u root --password=$$MYSQL_ROOT_PASSWORD - interval: 5s - timeout: 10s - retries: 60 - - backend: - image: registry.forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server:dev - networks: - backoffice: - nginx-network: - depends_on: - database-agent: - condition: service_healthy - restart: unless-stopped - volumes: - - /etc/localtime:/etc/localtime:ro - - ./configs:/app/configs - - ./letsencrypt_cache:/app/letsencrypt_cache - - ./data:/app/data - - ./${IMAGE_FOLDER}:/app/${IMAGE_FOLDER} - expose: - - ${HTTPS_PORT} - environment: - - HOSTNAME=${HOSTNAME} - - HTTPS_PORT=${HTTPS_PORT} - - ADMIN_ROLE=${ADMIN_ROLE} - - REDIRECT_URL=${REDIRECT_URL} - - CLIENT_ID=${CLIENT_ID} - - CLIENT_SECRET=${CLIENT_SECRET} - - AUTH_URL=${AUTH_URL} - - IMAGE_FOLDER=${IMAGE_FOLDER} - - MOCK_OAUTH2=${MOCK_OAUTH2} - - TOKEN_URL=${TOKEN_URL} - - USERINFO_URL=${USERINFO_URL} - - DEBUG_MODE=${DEBUG_MODE} - - DATABASE_USER=${DATABASE_USER} - - DATABASE_NAME=${DATABASE_NAME} - - DATABASE_PASSWORD=${DATABASE_PASSWORD} - - DATABASE_HOST=database-agent - -networks: - backoffice: - nginx-network: - external: true \ No newline at end of file diff --git a/internal/auth/auth.go b/internal/auth/auth.go index f4326b6ccdc877ce941a16d4fc864e522ef73ed1..2670382e45bcb5ab9aad6b68a1e57b798888e20b 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -7,9 +7,7 @@ import ( "fmt" "net" "net/http" - "os" "strings" - "time" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/common" "forge.grandlyon.com/web-et-numerique/factory/llle_project/backoffice-server/internal/tokens" @@ -24,6 +22,7 @@ const ( ) var ( + AnimatorRole = common.StringValueFromEnv("ANIMATOR_ROLE", "ANIMATORS") // AdminRole represents the role reserved for admins AdminRole = common.StringValueFromEnv("ADMIN_ROLE", "ADMINS") hostname = common.StringValueFromEnv("HOSTNAME", "ecolyobackoffice.127.0.0.1.nip.io") @@ -53,12 +52,12 @@ type TokenData struct { XSRFToken string `json:"xsrftoken,omitempty"` } -func AdminAuthMiddleware(next http.Handler) http.Handler { - return ValidateAuthMiddleware(next, []string{os.Getenv("ADMIN_ROLE")}, true) +func AnimatorAuthMiddleware(next http.Handler) http.Handler { + return ValidateAuthMiddleware(next, []string{AdminRole, AnimatorRole}, true) } -func CommonAuthMiddleware(next http.Handler) http.Handler { - return ValidateAuthMiddleware(next, []string{"*"}, false) +func AdminAuthMiddleware(next http.Handler) http.Handler { + return ValidateAuthMiddleware(next, []string{AdminRole}, true) } // ValidateAuthMiddleware validates that the token is valid and that the user has the correct roles @@ -169,45 +168,6 @@ func checkUserHasRole(user TokenData, allowedRoles []string) error { return fmt.Errorf("no user role among %v is in allowed roles (%v)", user.Roles, allowedRoles) } -//GetShareToken gets a share token for a given ressource -func GetShareToken(w http.ResponseWriter, r *http.Request) { - user, err := GetTokenData(r) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - - if r.Method != http.MethodPost { - http.Error(w, "method not allowed", http.StatusMethodNotAllowed) - return - } - var wantedToken struct { - Sharedfor string `json:"sharedfor"` - URL string `json:"url"` - Lifespan int `json:"lifespan"` - ReadOnly bool `json:"readonly,omitempty"` - } - err = json.NewDecoder(r.Body).Decode(&wantedToken) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - if wantedToken.URL == "" { - http.Error(w, "url cannot be empty", http.StatusBadRequest) - return - } - user.Login = user.Login + "_share_for_" + wantedToken.Sharedfor - user.URL = wantedToken.URL - user.ReadOnly = wantedToken.ReadOnly - user.SharingUserLogin = wantedToken.Sharedfor - token, err := tokens.CreateToken(user, time.Now().Add(time.Hour*time.Duration(24*wantedToken.Lifespan))) - if err != nil { - http.Error(w, err.Error(), http.StatusBadRequest) - return - } - fmt.Fprint(w, token) -} - // GetTokenData gets an user from a request func GetTokenData(r *http.Request) (TokenData, error) { user, ok := r.Context().Value(ContextData).(TokenData) @@ -216,13 +176,3 @@ func GetTokenData(r *http.Request) (TokenData, error) { } return user, nil } - -// isWebdav works out if an user agent is a webdav user agent -func isWebdav(ua string) bool { - for _, a := range []string{"vfs", "Microsoft-WebDAV", "Konqueror", "LibreOffice", "Rei.Fs.WebDAV"} { - if strings.Contains(ua, a) { - return true - } - } - return false -} diff --git a/internal/auth/oauth2.go b/internal/auth/oauth2.go index 84c03558c6ce0e371805b950a8ed97ebd51ef7a4..423df8eac680bc3d4b740993ceb38eadfb004312 100644 --- a/internal/auth/oauth2.go +++ b/internal/auth/oauth2.go @@ -127,7 +127,7 @@ func (m Manager) HandleOAuth2Callback() http.Handler { } // Check if user has the correct role - err = checkUserHasRole(TokenData{User: user}, []string{AdminRole}) + err = checkUserHasRole(TokenData{User: user}, []string{AdminRole, AnimatorRole}) if err != nil { // Log the connexion attempt diff --git a/internal/mocks/mocks.go b/internal/mocks/mocks.go index f25b70c3241016120cb4b0cd02f9299e0cdc7163..724552d295444b5bbc6b24e095144deb69d79088 100644 --- a/internal/mocks/mocks.go +++ b/internal/mocks/mocks.go @@ -36,17 +36,17 @@ func CreateMockOAuth2() *http.ServeMux { w.Header().Set("Content-Type", "application/x-www-form-urlencoded") w.Write([]byte("access_token=mocktoken&scope=user&token_type=bearer")) }) - // Returns userinfo back to the user - mux.HandleFunc("/userinfo", func(w http.ResponseWriter, r *http.Request) { + // Returns userinfo back to the user (with an animator user) + mux.HandleFunc("/animatorinfo", func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") w.Write([]byte(`{ - "displayName": "Us ER", + "displayName": "Anim Ator", "memberOf": [ - "CN=USERS", + "CN=ANIMATORS", "CN=OTHER_GROUP" ], "id": "1000", - "login": "USER" + "login": "ANIMATOR" }`)) }) // Returns userinfo back to the user (with an admin user) diff --git a/internal/models/customPopup.go b/internal/models/customPopup.go index 71b32ab2bed81e0c62463a3078733f9770efd303..6e3dd2732ead6106a651fa1d2061967ac7d69b37 100644 --- a/internal/models/customPopup.go +++ b/internal/models/customPopup.go @@ -50,7 +50,7 @@ func (dh *DataHandler) GetCustomPopup(w http.ResponseWriter, r *http.Request) { // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param customPopup body CustomPopup true "CustomPopup to create/update with new content" -// @Router /api/admin/customPopup [put] +// @Router /api/animator/customPopup [put] func (dh *DataHandler) SaveCustomPopup(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) diff --git a/internal/models/mailSubject.go b/internal/models/mailSubject.go index e9cb8dc60689d58a67b99c00725b61ea3e750a95..c3005a2f841a27b0c0166caddbd13e3f9eb4277d 100644 --- a/internal/models/mailSubject.go +++ b/internal/models/mailSubject.go @@ -23,7 +23,7 @@ type MailSubject struct { // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the mailSubject" // @Param month path int true "Month of the mailSubject" -// @Router /api/admin/mailSubject/{year}/{month} [get] +// @Router /api/animator/mailSubject/{year}/{month} [get] func (dh *DataHandler) GetSingleMailSubject(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { @@ -54,7 +54,7 @@ func (dh *DataHandler) GetSingleMailSubject(w http.ResponseWriter, r *http.Reque // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param mailSubject body MailSubject true "MailSubject to create/update with new content" -// @Router /api/admin/mailSubject [put] +// @Router /api/animator/mailSubject [put] func (dh *DataHandler) SaveMailSubject(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) @@ -111,7 +111,7 @@ func (dh *DataHandler) SaveMailSubject(w http.ResponseWriter, r *http.Request) { // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the mailSubject" // @Param month path int true "Month of the mailSubject" -// @Router /api/admin/mailSubject/{year}/{month} [delete] +// @Router /api/animator/mailSubject/{year}/{month} [delete] func (dh *DataHandler) DeleteMailSubject(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { diff --git a/internal/models/monthlyInfo.go b/internal/models/monthlyInfo.go index 32a12ab76aab51be361dff53d7f6d54aed3e8db1..971c9c5f1817b2390b20f350f4b93a2d912607b5 100644 --- a/internal/models/monthlyInfo.go +++ b/internal/models/monthlyInfo.go @@ -21,7 +21,7 @@ type MonthlyInfo struct { // @Tags monthlyInfo // @Produce json // @Success 200 {array} MonthlyInfo -// @Router /api/admin/monthlyInfo [get] +// @Router /api/animator/monthlyInfo [get] func (dh *DataHandler) GetAllMonthlyInfo(w http.ResponseWriter, r *http.Request) { var monthlyInfo []MonthlyInfo dh.sqlClient.Find(&monthlyInfo) @@ -40,7 +40,7 @@ func (dh *DataHandler) GetAllMonthlyInfo(w http.ResponseWriter, r *http.Request) // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the monthlyInfo" // @Param month path int true "Month of the monthlyInfo" -// @Router /api/admin/monthlyInfo/{year}/{month} [get] +// @Router /api/animator/monthlyInfo/{year}/{month} [get] func (dh *DataHandler) GetSingleMonthlyInfo(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { @@ -71,7 +71,7 @@ func (dh *DataHandler) GetSingleMonthlyInfo(w http.ResponseWriter, r *http.Reque // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param monthlyInfo body MonthlyInfo true "MonthlyInfo to create/update with new content" -// @Router /api/admin/monthlyInfo [put] +// @Router /api/animator/monthlyInfo [put] func (dh *DataHandler) SaveMonthlyInfo(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) @@ -136,7 +136,7 @@ func (dh *DataHandler) SaveMonthlyInfo(w http.ResponseWriter, r *http.Request) { // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the monthlyInfo" // @Param month path int true "Month of the monthlyInfo" -// @Router /api/admin/monthlyInfo/{year}/{month} [delete] +// @Router /api/animator/monthlyInfo/{year}/{month} [delete] func (dh *DataHandler) DeleteMonthlyInfo(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { diff --git a/internal/models/monthlyNews.go b/internal/models/monthlyNews.go index f352177509b1b1ab22e8e528813377b6962a671c..9bdfdb8dccc79d8d987a558dca771e9463af2be6 100644 --- a/internal/models/monthlyNews.go +++ b/internal/models/monthlyNews.go @@ -21,7 +21,7 @@ type MonthlyNews struct { // @Tags monthlyNews // @Produce json // @Success 200 {array} MonthlyNews -// @Router /api/admin/monthlyNews [get] +// @Router /api/animator/monthlyNews [get] func (dh *DataHandler) GetAllMonthlyNews(w http.ResponseWriter, r *http.Request) { var monthlyNews []MonthlyNews dh.sqlClient.Find(&monthlyNews) @@ -40,7 +40,7 @@ func (dh *DataHandler) GetAllMonthlyNews(w http.ResponseWriter, r *http.Request) // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the monthlyNews" // @Param month path int true "Month of the monthlyNews" -// @Router /api/admin/monthlyNews/{year}/{month} [get] +// @Router /api/animator/monthlyNews/{year}/{month} [get] func (dh *DataHandler) GetSingleMonthlyNews(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { @@ -71,7 +71,7 @@ func (dh *DataHandler) GetSingleMonthlyNews(w http.ResponseWriter, r *http.Reque // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param monthlyNews body MonthlyNews true "MonthlyNews to create/update with new content" -// @Router /api/admin/monthlyNews [put] +// @Router /api/animator/monthlyNews [put] func (dh *DataHandler) SaveMonthlyNews(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) @@ -136,7 +136,7 @@ func (dh *DataHandler) SaveMonthlyNews(w http.ResponseWriter, r *http.Request) { // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the monthlyNews" // @Param month path int true "Month of the monthlyNews" -// @Router /api/admin/monthlyNews/{year}/{month} [delete] +// @Router /api/animator/monthlyNews/{year}/{month} [delete] func (dh *DataHandler) DeleteMonthlyNews(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { diff --git a/internal/models/partnersInfo.go b/internal/models/partnersInfo.go index 0a70becfabab3b46177bbe7863ebf9792569fc7f..cdea10aec1770bbba1051ccb1bfd934d8560a007 100644 --- a/internal/models/partnersInfo.go +++ b/internal/models/partnersInfo.go @@ -49,7 +49,7 @@ func (dh *DataHandler) GetPartnersInfo(w http.ResponseWriter, r *http.Request) { // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param partnersInfo body PartnersInfo true "PartnersInfo to create/update with new content" -// @Router /api/admin/partnersInfo [put] +// @Router /api/animator/partnersInfo [put] func (dh *DataHandler) SavePartnersInfo(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) diff --git a/internal/models/poll.go b/internal/models/poll.go index 3f89d853b1ad607fa92e2807d5565d88b7e756b4..76683a20660ebd17f3b2ae1259062c5aefd636b4 100644 --- a/internal/models/poll.go +++ b/internal/models/poll.go @@ -21,7 +21,7 @@ type Poll struct { // @Tags poll // @Produce json // @Success 200 {array} Poll -// @Router /api/admin/poll [get] +// @Router /api/animator/poll [get] func (dh *DataHandler) GetAllPolls(w http.ResponseWriter, r *http.Request) { var polls []Poll err := dh.sqlClient.Find(&polls).Error @@ -44,7 +44,7 @@ func (dh *DataHandler) GetAllPolls(w http.ResponseWriter, r *http.Request) { // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the poll" // @Param month path int true "Month of the poll" -// @Router /api/admin/poll/{year}/{month} [get] +// @Router /api/animator/poll/{year}/{month} [get] func (dh *DataHandler) GetSinglePoll(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { @@ -75,7 +75,7 @@ func (dh *DataHandler) GetSinglePoll(w http.ResponseWriter, r *http.Request) { // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param poll body Poll true "Poll to update with new content" -// @Router /api/admin/poll [put] +// @Router /api/animator/poll [put] func (dh *DataHandler) SavePoll(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body is empty", http.StatusBadRequest) @@ -133,7 +133,7 @@ func (dh *DataHandler) SavePoll(w http.ResponseWriter, r *http.Request) { // @Failure 404 {string} string "Not found" // @Param year path int true "Year of the poll" // @Param month path int true "Month of the poll" -// @Router /api/admin/poll/{year}/{month} [delete] +// @Router /api/animator/poll/{year}/{month} [delete] func (dh *DataHandler) DeletePoll(w http.ResponseWriter, r *http.Request) { year, month, err := common.YearMonthFromRequest(r) if err != nil { diff --git a/internal/models/price.go b/internal/models/price.go index bfec29b853a928ae9ae444bea541c0bbce9273a8..5503bf185fe55ec3d83bfad7709ba529fb7b9a3c 100644 --- a/internal/models/price.go +++ b/internal/models/price.go @@ -72,7 +72,7 @@ func (dh *DataHandler) GetPricesByFluid(w http.ResponseWriter, r *http.Request) // @Failure 400 {string} string "Bad Request" // @Failure 500 {string} string "Internal server error" // @Param price body object true "Price to create/update with new content" -// @Router /api/admin/price [put] +// @Router /api/animator/price [put] func (dh *DataHandler) SavePrice(w http.ResponseWriter, r *http.Request) { if r.Body == http.NoBody { http.Error(w, "request body price is empty", http.StatusBadRequest) diff --git a/internal/rootmux/rootmux.go b/internal/rootmux/rootmux.go index caf8916f6cf58d8cd4f932556c157a5cb4e402af..04e185c7d855b939f47234e24a144650da8a3d59 100644 --- a/internal/rootmux/rootmux.go +++ b/internal/rootmux/rootmux.go @@ -51,8 +51,8 @@ func CreateRootMux() RootMux { r.Get("/prices/{fluidtype}", dh.GetPricesByFluid) }) - r.Route("/api/admin", func(r chi.Router) { - r.Use(auth.AdminAuthMiddleware) + r.Route("/api/animator", func(r chi.Router) { + r.Use(auth.AnimatorAuthMiddleware) r.Get("/mailSubject/{year}/{month}", dh.GetSingleMailSubject) r.Put("/mailSubject", dh.SaveMailSubject) r.Delete("/mailSubject/{year}/{month}", dh.DeleteMailSubject) @@ -76,6 +76,10 @@ func CreateRootMux() RootMux { r.Put("/customPopup", dh.SaveCustomPopup) r.Get("/imageNames", file.GetEcogestureImages) r.Put("/prices", dh.SavePrice) + }) + + r.Route("/api/admin", func(r chi.Router) { + r.Use(auth.AdminAuthMiddleware) r.Get("/consent", dh.SearchConsent) }) diff --git a/internal/rootmux/rootmux_test.go b/internal/rootmux/rootmux_test.go index 4b0c1344bedbf05fc51dbe8c96599518c24e778f..9d86529c154af8225060079782a35c89b82b3433 100644 --- a/internal/rootmux/rootmux_test.go +++ b/internal/rootmux/rootmux_test.go @@ -45,11 +45,12 @@ func TestMain(m *testing.M) { // Set the constants with environment variables os.Setenv("HOSTNAME", "localhost") + os.Setenv("ANIMATOR_ROLE", "ANIMATORS") os.Setenv("ADMIN_ROLE", "ADMINS") os.Setenv("CLIENT_ID", "foo") os.Setenv("CLIENT_SECRET", "bar") os.Setenv("TOKEN_URL", oAuth2Server.URL+"/token") - os.Setenv("USERINFO_URL", oAuth2Server.URL+"/userinfo") + os.Setenv("USERINFO_URL", oAuth2Server.URL+"/animatorinfo") os.Setenv("LOGOUT_URL", oAuth2Server.URL+"/logout") os.Setenv("SGE_API_TOKEN", "sgeApiToken") @@ -82,17 +83,18 @@ func TestMain(m *testing.M) { } func TestAll(t *testing.T) { + // SGE API tests + sgeTests(t) // Set up testers os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth-wrong-state") // Set the server to access failing OAuth2 endpoints oauth2Tests(t) os.Setenv("AUTH_URL", oAuth2Server.URL+"/auth") // Set the server to access the correct OAuth2Endpoint unloggedTests(t) + os.Setenv("USERINFO_URL", oAuth2Server.URL+"/animatorinfo") + animatorTests(t) os.Setenv("USERINFO_URL", oAuth2Server.URL+"/admininfo") adminTests(t) - - // SGE API tests - sgeTests(t) } /* @@ -119,7 +121,7 @@ func unloggedTests(t *testing.T) { defer ts.Close() // Close the tester // Try to create a monthlyNews (must fail) - do("PUT", "/api/admin/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, "error extracting token") + do("PUT", "/api/animator/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, "error extracting token") // Try to get a monthlyReport (must fail because no parameters are given) do("GET", "/api/common/monthlyReport", noH, "", http.StatusBadRequest, "") // Try to get a monthlyReport (must pass empty) @@ -130,10 +132,10 @@ func unloggedTests(t *testing.T) { /* * -ADMIN TESTS (this tests are to check that an administrator can edit a newsletter's content) +ANIMATOR TESTS (these tests are to check that an animator user can edit a newsletter's content but can't access SGE consents) * */ -func adminTests(t *testing.T) { +func animatorTests(t *testing.T) { // Create the tester ts, do, _ := createTester(t) defer ts.Close() // Close the tester @@ -144,83 +146,85 @@ func adminTests(t *testing.T) { json.Unmarshal([]byte(response), &token) xsrfHeader := map[string]string{"XSRF-TOKEN": token.XSRFToken} // Try to create a monthlyNews without the XSRF-TOKEN (must fail) - do("PUT", "/api/admin/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, "XSRF protection triggered") + do("PUT", "/api/animator/monthlyNews", noH, monthlyNewsStr, http.StatusUnauthorized, "XSRF protection triggered") // Try to create a monthlyNews without body (must fail) - do("PUT", "/api/admin/monthlyNews", xsrfHeader, "", http.StatusBadRequest, "request body is empty") + do("PUT", "/api/animator/monthlyNews", xsrfHeader, "", http.StatusBadRequest, "request body is empty") // Try to get a monthlyNews before it is created (must fail because not found) - do("GET", "/api/admin/monthlyNews/2021/1", xsrfHeader, "", http.StatusNotFound, "") + do("GET", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a monthlyNews (must pass) - do("PUT", "/api/admin/monthlyNews", xsrfHeader, monthlyNewsStr, http.StatusCreated, `{"year":2021,"month":1,"title":"Les nouveautés du service","content":"Nouvelles fonctionnalités"`) + do("PUT", "/api/animator/monthlyNews", xsrfHeader, monthlyNewsStr, http.StatusCreated, `{"year":2021,"month":1,"title":"Les nouveautés du service","content":"Nouvelles fonctionnalités"`) // Try to update a monthlyNews (must pass) - do("PUT", "/api/admin/monthlyNews", xsrfHeader, monthlyNewsStr, http.StatusOK, `{"year":2021,"month":1,"title":"Les nouveautés du service","content":"Nouvelles fonctionnalités"`) + do("PUT", "/api/animator/monthlyNews", xsrfHeader, monthlyNewsStr, http.StatusOK, `{"year":2021,"month":1,"title":"Les nouveautés du service","content":"Nouvelles fonctionnalités"`) // Try to get the monthlyNews created (must pass) - do("GET", "/api/admin/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, `{"year":2021,"month":1,"title":"Les nouveautés du service","content":"Nouvelles fonctionnalités"`) + do("GET", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, `{"year":2021,"month":1,"title":"Les nouveautés du service","content":"Nouvelles fonctionnalités"`) // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Votre bilan de décembre 2020","info":"","image":"","newsTitle":"Les nouveautés du service","newsContent":"Nouvelles fonctionnalités","question":"","link":""}`) // Try to create a poll without the XSRF-TOKEN (must fail) - do("PUT", "/api/admin/poll", noH, newPollStr, http.StatusUnauthorized, "XSRF protection triggered") + do("PUT", "/api/animator/poll", noH, newPollStr, http.StatusUnauthorized, "XSRF protection triggered") // Try to create a poll without body (must fail) - do("PUT", "/api/admin/poll", xsrfHeader, "", http.StatusBadRequest, "request body is empty") + do("PUT", "/api/animator/poll", xsrfHeader, "", http.StatusBadRequest, "request body is empty") // Try to get a poll before it is created (must fail because not found') - do("GET", "/api/admin/poll/2021/1", xsrfHeader, "", http.StatusNotFound, "") + do("GET", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a poll (must pass) - do("PUT", "/api/admin/poll", xsrfHeader, newPollStr, http.StatusCreated, newPollStr) + do("PUT", "/api/animator/poll", xsrfHeader, newPollStr, http.StatusCreated, newPollStr) // Try to update a poll (must pass) - do("PUT", "/api/admin/poll", xsrfHeader, newPollStr, http.StatusOK, newPollStr) + do("PUT", "/api/animator/poll", xsrfHeader, newPollStr, http.StatusOK, newPollStr) // Try to get the poll created (must pass) - do("GET", "/api/admin/poll/2021/1", xsrfHeader, "", http.StatusOK, newPollStr) + do("GET", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusOK, newPollStr) // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Votre bilan de décembre 2020","info":"","image":"","newsTitle":"Les nouveautés du service","newsContent":"Nouvelles fonctionnalités","question":"pollQuestion","link":"pollLink"}`) // Try to create a monthlyInfo without the XSRF-TOKEN (must fail) - do("PUT", "/api/admin/monthlyInfo", noH, monthlyInfoStr, http.StatusUnauthorized, "XSRF protection triggered") + do("PUT", "/api/animator/monthlyInfo", noH, monthlyInfoStr, http.StatusUnauthorized, "XSRF protection triggered") // Try to create a monthlyInfo without body (must fail) - do("PUT", "/api/admin/monthlyInfo", xsrfHeader, "", http.StatusBadRequest, "request body is empty") + do("PUT", "/api/animator/monthlyInfo", xsrfHeader, "", http.StatusBadRequest, "request body is empty") // Try to get a monthlyInfo before it is created (must fail because not found) - do("GET", "/api/admin/monthlyInfo/2021/1", xsrfHeader, "", http.StatusNotFound, "") + do("GET", "/api/animator/monthlyInfo/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a monthlyInfo (must pass) - do("PUT", "/api/admin/monthlyInfo", xsrfHeader, monthlyInfoStr, http.StatusCreated, monthlyInfoStr) + do("PUT", "/api/animator/monthlyInfo", xsrfHeader, monthlyInfoStr, http.StatusCreated, monthlyInfoStr) // Try to update a monthlyInfo (must pass) - do("PUT", "/api/admin/monthlyInfo", xsrfHeader, monthlyInfoStr, http.StatusOK, monthlyInfoStr) + do("PUT", "/api/animator/monthlyInfo", xsrfHeader, monthlyInfoStr, http.StatusOK, monthlyInfoStr) // Try to get the monthlyInfo created (must pass) - do("GET", "/api/admin/monthlyInfo/2021/1", xsrfHeader, "", http.StatusOK, monthlyInfoStr) + do("GET", "/api/animator/monthlyInfo/2021/1", xsrfHeader, "", http.StatusOK, monthlyInfoStr) // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Votre bilan de décembre 2020","info":"Informations du mois","image":"imagebase64","newsTitle":"Les nouveautés du service","newsContent":"Nouvelles fonctionnalités","question":"pollQuestion","link":"pollLink"`) // Try to update the partnersInfo (must pass) - do("PUT", "/api/admin/partnersInfo", xsrfHeader, partnersInfoStr, http.StatusOK, partnersInfoStr) + do("PUT", "/api/animator/partnersInfo", xsrfHeader, partnersInfoStr, http.StatusOK, partnersInfoStr) // Try to get the partnersInfo created (must pass) do("GET", "/api/common/partnersInfo", xsrfHeader, "", http.StatusOK, partnersInfoStr) // Try to update the customPopup (must pass) - do("PUT", "/api/admin/customPopup", xsrfHeader, customPopupStr, http.StatusOK, customPopupStr) + do("PUT", "/api/animator/customPopup", xsrfHeader, customPopupStr, http.StatusOK, customPopupStr) // Try to get the partnersInfo created (must pass) do("GET", "/api/common/customPopup", xsrfHeader, "", http.StatusOK, customPopupStr) // Try to delete the monthlyNews created (must pass) - do("DELETE", "/api/admin/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") + do("DELETE", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") // Try to get a monthlyNews after it is deleted (must fail because not found) - do("GET", "/api/admin/monthlyNews/2021/1", xsrfHeader, "", http.StatusNotFound, "") + do("GET", "/api/animator/monthlyNews/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to create a mail subject (must pass) - do("PUT", "/api/admin/mailSubject", xsrfHeader, mailSubjectStr, http.StatusCreated, mailSubjectStr) + do("PUT", "/api/animator/mailSubject", xsrfHeader, mailSubjectStr, http.StatusCreated, mailSubjectStr) // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Newsletter","info":"Informations du mois","image":"imagebase64","newsTitle":"","newsContent":"","question":"pollQuestion","link":"pollLink"`) // Try to delete the poll created (must pass) - do("DELETE", "/api/admin/poll/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") + do("DELETE", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") // Try to get a poll after it is deleted (must fail because not found) - do("GET", "/api/admin/poll/2021/1", xsrfHeader, "", http.StatusNotFound, "") + do("GET", "/api/animator/poll/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Newsletter","info":"Informations du mois","image":"imagebase64","newsTitle":"","newsContent":"","question":"","link":""`) // Try to delete the mail subject created (must pass) - do("DELETE", "/api/admin/mailSubject/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") + do("DELETE", "/api/animator/mailSubject/2021/1", xsrfHeader, "", http.StatusOK, "successful delete") // Try to get a mail subject after it is deleted (must fail because not found) - do("GET", "/api/admin/mailSubject/2021/1", xsrfHeader, "", http.StatusNotFound, "") + do("GET", "/api/animator/mailSubject/2021/1", xsrfHeader, "", http.StatusNotFound, "") // Try to get the monthlyReport (must pass) do("GET", "/api/common/monthlyReport?year=2021&month=1", noH, "", http.StatusOK, `{"year":2021,"month":1,"subject":"[Ecolyo] Votre bilan de décembre 2020","info":"Informations du mois","image":"imagebase64","newsTitle":"","newsContent":"","question":"","link":""`) + // Try to get SGE consents (must fail) + do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusForbidden, "no user role among [ANIMATORS OTHER_GROUP] is in allowed roles ([ADMINS])") } // Try to login (must pass) do("GET", "/OAuth2Login", noH, "", http.StatusOK, "") @@ -229,12 +233,33 @@ func adminTests(t *testing.T) { // Try to logout (must pass) do("GET", "/Logout", noH, "", http.StatusOK, "") // Try to get a monthlyNews again (must fail) - do("GET", "/api/admin/monthlyNews", noH, "", http.StatusUnauthorized, "error extracting token") + do("GET", "/api/animator/monthlyNews", noH, "", http.StatusUnauthorized, "error extracting token") // Try to get a poll again (must fail) - do("GET", "/api/admin/poll", noH, "", http.StatusUnauthorized, "error extracting token") + do("GET", "/api/animator/poll", noH, "", http.StatusUnauthorized, "error extracting token") } +func adminTests(t *testing.T) { + // Create the tester + ts, do, _ := createTester(t) + defer ts.Close() // Close the tester + + // Try to login (must pass) + do("GET", "/OAuth2Login", noH, "", http.StatusOK, "") + // Get the XSRF Token + response := do("GET", "/api/common/WhoAmI", noH, "", http.StatusOK, "") + token := auth.TokenData{} + json.Unmarshal([]byte(response), &token) + xsrfHeader := map[string]string{"XSRF-TOKEN": token.XSRFToken} + + // Try to get SGE consents (must pass) + do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":2,"rows":[{"ID":2`) + // Try to logout (must pass) + do("GET", "/Logout", noH, "", http.StatusOK, "") + // Try to get SGE consents again (must fail) + do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusUnauthorized, "error extracting token") +} + func sgeTests(t *testing.T) { // Create the tester ts, do, _ := createTester(t) @@ -260,25 +285,6 @@ func sgeTests(t *testing.T) { do("GET", "/api/sge/consent/1", sgeApiHeader, "", http.StatusOK, `{"ID":1`) // Try to get a consent that doesn't exist (must fail not found) do("GET", "/api/sge/consent/3", sgeApiHeader, "", http.StatusNotFound, `consent not found`) - - // Try to login (must pass) - do("GET", "/OAuth2Login", noH, "", http.StatusOK, "") - - response := do("GET", "/api/common/WhoAmI", noH, "", http.StatusOK, "") - token := auth.TokenData{} - json.Unmarshal([]byte(response), &token) - xsrfHeader := map[string]string{"XSRF-TOKEN": token.XSRFToken} - - // Try to get first 50 consents (must pass) - do("GET", "/api/admin/consent?limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":2,"rows":[{"ID":2`) - // Try to search for both consent (must pass) - do("GET", "/api/admin/consent?search=123456&limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":2,"rows":[{"ID":2`) - // Try to search for first point id (must pass) - do("GET", "/api/admin/consent?search=12345671234567&limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":1,"rows":[{"ID":1`) - // Try to search for second point id (must pass) - do("GET", "/api/admin/consent?search=01234560123456&limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":1,"rows":[{"ID":2`) - // Try to search for unknown point id (must pass empty) - do("GET", "/api/admin/consent?search=000000&limit=50&page=0", xsrfHeader, "", http.StatusOK, `{"totalRows":0,"rows":[]`) // Try to delete a consent (must pass) do("DELETE", "/api/sge/consent/1", sgeApiHeader, "", http.StatusOK, "") }