From 57057780d8dd5388dcfadff0ea02162d6d532f29 Mon Sep 17 00:00:00 2001 From: pikiou Date: Fri, 11 Mar 2022 10:11:54 +0100 Subject: [PATCH] =?UTF-8?q?Add=20roles=20support=20server=20and=20client?= =?UTF-8?q?=20side=C2=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/gsheets/expressAccessors.ts | 21 +++++++++++++++++---- src/server/gsheets/volunteers.ts | 11 +++++++---- src/server/ssr.tsx | 4 ++-- src/services/auth.ts | 6 ++++-- src/services/volunteers.ts | 3 ++- src/store/auth.ts | 6 ++++++ src/store/index.ts | 13 ++++++++----- src/store/volunteerLogin.ts | 2 +- 8 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/server/gsheets/expressAccessors.ts b/src/server/gsheets/expressAccessors.ts index 5d498dd..56c7fc2 100644 --- a/src/server/gsheets/expressAccessors.ts +++ b/src/server/gsheets/expressAccessors.ts @@ -57,7 +57,12 @@ export default class ExpressAccessors< // custom can be async get( - custom?: (list: Element[], body: Request["body"], id: number) => Promise | Ret + custom?: ( + list: Element[], + body: Request["body"], + id: number, + roles: string[] + ) => Promise | Ret ) { return async (request: Request, response: Response, _next: NextFunction): Promise => { try { @@ -69,7 +74,8 @@ export default class ExpressAccessors< toCaller = list.find((e: Element) => e.id === id) } else { const memberId = response?.locals?.jwt?.id || -1 - toCaller = await custom(list, request.body, memberId) + const roles: string[] = response?.locals?.jwt?.roles || [] + toCaller = await custom(list, request.body, memberId, roles) if (toCaller?.jwt && toCaller?.id) { response.cookie("jwt", toCaller.jwt, { maxAge: 365 * 24 * 60 * 60 }) response.cookie("id", toCaller.id, { maxAge: 365 * 24 * 60 * 60 }) @@ -101,7 +107,8 @@ export default class ExpressAccessors< custom?: ( list: Element[], body: RequestBody, - id: number + id: number, + roles: string[] ) => Promise> | CustomSetReturn ) { return async (request: Request, response: Response, _next: NextFunction): Promise => { @@ -112,8 +119,14 @@ export default class ExpressAccessors< response.status(200) } else { const memberId = response?.locals?.jwt?.id || -1 + const roles: string[] = response?.locals?.jwt?.roles || [] const list = (await sheet.getList()) || [] - const { toDatabase, toCaller } = await custom(list, request.body, memberId) + const { toDatabase, toCaller } = await custom( + list, + request.body, + memberId, + roles + ) if (toDatabase !== undefined) { await sheet.set(toDatabase) } diff --git a/src/server/gsheets/volunteers.ts b/src/server/gsheets/volunteers.ts index 691e0c7..2fcbe34 100644 --- a/src/server/gsheets/volunteers.ts +++ b/src/server/gsheets/volunteers.ts @@ -51,8 +51,9 @@ export const volunteerLogin = expressAccessor.get(async (list, b const jwt = await getJwt(volunteer.id, volunteer.roles) return { - id: volunteer.id, jwt, + id: volunteer.id, + roles: volunteer.roles, } }) @@ -142,10 +143,12 @@ export const volunteerNotifsSet = expressAccessor.set(async (list, body, id) => } }) -export const volunteerTeamWishesSet = expressAccessor.set(async (list, body, id) => { +export const volunteerTeamWishesSet = expressAccessor.set(async (list, body, id, roles) => { const requestedId = +body[0] || id - if (requestedId !== id && requestedId !== 0) { - throw Error(`On ne peut acceder qu'à ses propres envies d'équipes`) + if (requestedId !== id && requestedId !== 0 && !roles.includes("repartiteur")) { + throw Error( + `À moins d'être répartiteur de bénévole dans les équipes, on ne peut acceder qu'à ses propres envies d'équipes` + ) } const wishes = body[1] as VolunteerTeamWishes const volunteer = list.find((v) => v.id === requestedId) diff --git a/src/server/ssr.tsx b/src/server/ssr.tsx index c0395c1..5498f9a 100644 --- a/src/server/ssr.tsx +++ b/src/server/ssr.tsx @@ -15,8 +15,8 @@ import routes from "../routes" import { getCookieJWT } from "../services/auth" export default async (req: Request, res: Response, next: NextFunction): Promise => { - const { jwt, id } = getCookieJWT(req.headers.cookie) - const { store } = createStore({ url: req.url, jwt, id }) + const { jwt, id, roles } = getCookieJWT(req.headers.cookie) + const { store } = createStore({ url: req.url, jwt, id, roles }) // The method for loading data from server-side const loadBranchData = (): Promise => { diff --git a/src/services/auth.ts b/src/services/auth.ts index ff74daf..b85e0e0 100644 --- a/src/services/auth.ts +++ b/src/services/auth.ts @@ -7,10 +7,11 @@ export const axiosConfig: AxiosRequestConfig = { headers: {}, } -export function setJWT(token: string, id: number): void { +export function setJWT(token: string, id: number, roles: string[]): void { axiosConfig.headers.Authorization = `Bearer ${token}` Cookies.set("jwt", token, { expires: 3650 }) Cookies.set("id", `${id}`, { expires: 3650 }) + Cookies.set("roles", roles.join(","), { expires: 3650 }) } export function unsetJWT(): void { @@ -18,6 +19,7 @@ export function unsetJWT(): void { Cookies.remove("jwt") Cookies.remove("id") + Cookies.remove("roles") } export function getCookieJWT(cookie = ""): VolunteerLogin { @@ -28,5 +30,5 @@ export function getCookieJWT(cookie = ""): VolunteerLogin { res[k.trim()] = v return res }, {}) - return { jwt: cookies.jwt, id: +cookies.id } + return { jwt: cookies.jwt, id: +cookies.id, roles: cookies.roles?.split(",") || [] } } diff --git a/src/services/volunteers.ts b/src/services/volunteers.ts index 97fe0d5..846ede9 100644 --- a/src/services/volunteers.ts +++ b/src/services/volunteers.ts @@ -107,8 +107,9 @@ export const passwordMinLength = 4 export type VolunteerWithoutId = Omit export interface VolunteerLogin { - id: number jwt: string + id: number + roles: string[] } export interface VolunteerForgot { diff --git a/src/store/auth.ts b/src/store/auth.ts index e6d3535..0d1dfb0 100644 --- a/src/store/auth.ts +++ b/src/store/auth.ts @@ -4,12 +4,14 @@ import { AppState } from "." // Define a type for the slice state interface AuthState { id: number + roles: string[] jwt: string } // Define the initial state using that type const initialState: AuthState = { id: 0, + roles: [], jwt: "", } @@ -19,11 +21,13 @@ export const auth = createSlice({ reducers: { setCurrentUser: (state, action: PayloadAction) => { state.id = action.payload.id + state.roles = action.payload.roles state.jwt = action.payload.jwt }, logoutUser: (state) => { // Unused, just reload page :/ state.id = 0 + state.roles = [] state.jwt = "" }, }, @@ -35,6 +39,8 @@ export const selectAuthData = (state: AppState): AuthState => state.auth export const selectUserJwtToken = createSelector(selectAuthData, (authData) => authData.jwt) +export const selectUserRoles = createSelector(selectAuthData, (authData) => authData.roles) + export const isUserConnected = createSelector(selectUserJwtToken, (token) => !!token) export default auth.reducer diff --git a/src/store/index.ts b/src/store/index.ts index 2944046..132480b 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -13,11 +13,12 @@ interface Arg { url?: string jwt?: string id?: number + roles?: string[] } // Use inferred return type for making correctly Redux types // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -const createStore = ({ initialState, url, jwt, id }: Arg = {}) => { +const createStore = ({ initialState, url, jwt, id, roles }: Arg = {}) => { const history = __SERVER__ ? createMemoryHistory({ initialEntries: [url || "/"] }) : createBrowserHistory() @@ -32,8 +33,8 @@ const createStore = ({ initialState, url, jwt, id }: Arg = {}) => { devTools: __DEV__, }) - if (jwt && id) { - store.dispatch(setCurrentUser({ jwt, id })) + if (jwt && id && roles) { + store.dispatch(setCurrentUser({ jwt, id, roles })) } else { store.dispatch(logoutUser()) } @@ -42,12 +43,14 @@ const createStore = ({ initialState, url, jwt, id }: Arg = {}) => { } const id = +(Cookies.get("id") || 0) +const roles = Cookies.get("roles")?.split(",") || [] const jwt = Cookies.get("jwt") -if (id && jwt) { +if (id && jwt && roles) { Cookies.set("id", `${id}`, { expires: 3650 }) + Cookies.set("roles", roles.join(","), { expires: 3650 }) Cookies.set("jwt", jwt, { expires: 3650 }) } -const { store } = createStore({ id, jwt }) +const { store } = createStore({ jwt, id, roles }) export type AppState = ReturnType diff --git a/src/store/volunteerLogin.ts b/src/store/volunteerLogin.ts index 27dbf62..785f52d 100644 --- a/src/store/volunteerLogin.ts +++ b/src/store/volunteerLogin.ts @@ -39,7 +39,7 @@ export const fetchVolunteerLogin = elementFetch { - setJWT(login.jwt, login.id) + setJWT(login.jwt, login.id, login.roles) // eslint-disable-next-line no-restricted-globals location?.reload() }