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, "")
 }