Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSO - Correction des erreurs de connexion #3763

Merged
merged 9 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions frontend/cypress/e2e/external_monitorfish.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
context('External MonitorFish', () => {
it('Should redirect to /', () => {
// Given
cy.intercept('/bff/v1/authorization/current', { statusCode: 401 }).as('getIsSuperUser')
cy.intercept('/bff/v1/authorization/current', {
body: {
isSuperUser: false
},
statusCode: 200
}).as('getIsSuperUser')
cy.visit('/ext#@-824534.42,6082993.21,8.70')
cy.wait('@getIsSuperUser')

Expand All @@ -10,7 +15,12 @@ context('External MonitorFish', () => {

it('Should have some features removed When not logged as super user', () => {
// Given
cy.intercept('/bff/v1/authorization/current', { statusCode: 401 }).as('getIsSuperUser')
cy.intercept('/bff/v1/authorization/current', {
body: {
isSuperUser: false
},
statusCode: 200
}).as('getIsSuperUser')
cy.visit('/#@-824534.42,6082993.21,8.70')
cy.wait('@getIsSuperUser')
cy.wait(200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ context('Offline management', () => {
path:
'/bff/v1/vessels/find?vesselId=1&internalReferenceNumber=FAK000999999&externalReferenceNumber=DONTSINK' +
'&IRCS=CALLME&vesselIdentifier=INTERNAL_REFERENCE_NUMBER&trackDepth=TWELVE_HOURS&afterDateTime=&beforeDateTime=',
times: 1
times: 2
},
{ statusCode: 400 }
).as('openVesselStubbed')
Expand Down Expand Up @@ -117,7 +117,7 @@ context('Offline management', () => {
{
method: 'GET',
path: '/bff/v1/vessels/logbook/find?internalReferenceNumber=FAK000999999&voyageRequest=LAST&tripNumber=',
times: 1
times: 2
},
{ statusCode: 400 }
).as('getLogbookStubbed')
Expand All @@ -135,8 +135,8 @@ context('Offline management', () => {
cy.intercept(
{
method: 'GET',
pathname: '/bff/v1/vessels/reportings',
times: 3
pathname: '/bff/v1/vessels/reporting',
times: 2
},
{ statusCode: 400 }
).as('getReportingsStubbed')
Expand Down
7 changes: 6 additions & 1 deletion frontend/cypress/e2e/nav_monitorfish.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
context('Light MonitorFish', () => {
it('Should have some features removed When not logged as super user', () => {
// Given
cy.intercept('/bff/v1/authorization/current', { statusCode: 401 }).as('getIsSuperUser')
cy.intercept('/bff/v1/authorization/current', {
body: {
isSuperUser: false
},
statusCode: 200
}).as('getIsSuperUser')
cy.visit('/light#@-824534.42,6082993.21,8.70')
cy.wait('@getIsSuperUser')
cy.wait(200)
Expand Down
11 changes: 10 additions & 1 deletion frontend/cypress/e2e/side_window/utils.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { SideWindowMenuLabel } from '../../../src/domain/entities/sideWindow/constants'

export const openSideWindowAsUser = () => {
cy.intercept('/bff/v1/authorization/current', { statusCode: 401 }).as('getIsSuperUser')
cy.intercept('/bff/v1/authorization/current', {
body: {
isSuperUser: false
},
statusCode: 200
}).as('getIsSuperUser')

cy.viewport(1920, 1080)
cy.visit('/side_window')
cy.wait(500)
if (document.querySelector('[data-cy="first-loader"]')) {
cy.getDataCy('first-loader').should('not.be.visible')
}
cy.clickButton(SideWindowMenuLabel.PRIOR_NOTIFICATION_LIST)
}

export const openSideWindowAsSuperUser = () => {
Expand All @@ -16,4 +24,5 @@ export const openSideWindowAsSuperUser = () => {
if (document.querySelector('[data-cy="first-loader"]')) {
cy.getDataCy('first-loader').should('not.be.visible')
}
cy.clickButton(SideWindowMenuLabel.PRIOR_NOTIFICATION_LIST)
}
2 changes: 1 addition & 1 deletion frontend/cypress/support/e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ Cypress.on('uncaught:exception', err => {

// Run before each spec
beforeEach(() => {
// We use a Cypress session to inject inject a Local Storage key
// We use a Cypress session to inject a Local Storage key
// so that we can detect when the browser app is running in Cypress.
// https://docs.cypress.io/faq/questions/using-cypress-faq#How-do-I-preserve-cookies--localStorage-in-between-my-tests
cy.session('cypress', () => {
Expand Down
16 changes: 13 additions & 3 deletions frontend/src/api/api.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
// https://redux-toolkit.js.org/rtk-query/usage/cache-behavior
// https://redux-toolkit.js.org/rtk-query/usage/automated-refetching#cache-tags

import { isUnauthorizedOrForbidden } from '@api/utils'
import { FrontendApiError } from '@libs/FrontendApiError'
import { createApi, fetchBaseQuery, retry } from '@reduxjs/toolkit/query/react'
import { setMeasurement, startSpan } from '@sentry/react'
import { normalizeRtkBaseQuery } from '@utils/normalizeRtkBaseQuery'
import { sha256 } from '@utils/sha256'
import ky, { HTTPError } from 'ky'

import { RTK_MAX_RETRIES, RtkCacheTagType } from './constants'
import { getOIDCConfig } from '../auth/getOIDCConfig'
import { getOIDCUser } from '../auth/getOIDCUser'
import { redirectToLoginIfUnauthorized } from '../auth/utils'
import { normalizeRtkBaseQuery } from '../utils/normalizeRtkBaseQuery'

import type { BackendApi } from './BackendApi.types'
import type { CustomResponseError, RTKBaseQueryArgs } from './types'
Expand Down Expand Up @@ -243,7 +244,14 @@ export const monitorfishApiKy = ky.extend({
}
],
beforeRetry: [
async ({ request }) => {
async ({ error, request }) => {
if (error) {
// Retry is not necessary when request is unauthorized
if (isUnauthorizedOrForbidden((error as HTTPError).response?.status)) {
return ky.stop
}
}

const user = getOIDCUser()
const token = user?.access_token

Expand All @@ -257,8 +265,10 @@ export const monitorfishApiKy = ky.extend({
request.headers.set(CORRELATION_HEADER, hashedToken)
}
}

return undefined
}
]
},
retry: RTK_MAX_RETRIES + 1
retry: RTK_MAX_RETRIES
})
5 changes: 3 additions & 2 deletions frontend/src/api/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FIVE_MINUTES, ONE_MINUTE, THIRTY_SECONDS } from '../constants'
import type { RefetchConfigOptions } from '@reduxjs/toolkit'
import type { StartQueryActionCreatorOptions, SubscriptionOptions } from '@reduxjs/toolkit/query'

export const RTK_MAX_RETRIES = 2
export const RTK_MAX_RETRIES = 1

export const RTK_THIRTY_SECONDS_POLLING_QUERY_OPTIONS: SubscriptionOptions & Partial<RefetchConfigOptions> = {
pollingInterval: THIRTY_SECONDS,
Expand Down Expand Up @@ -32,7 +32,8 @@ export enum HttpStatusCode {
CREATED = 201,
ACCEPTED = 202,
NOT_FOUND = 404,
UNAUTHORIZED = 401
UNAUTHORIZED = 401,
FORBIDDEN = 403
}

export enum RtkCacheTagType {
Expand Down
10 changes: 10 additions & 0 deletions frontend/src/api/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { HttpStatusCode } from '@api/constants'
import { isString } from 'lodash'

export function isUnauthorizedOrForbidden(httpStatus: number | string | undefined) {
if (!httpStatus || isString(httpStatus)) {
return false
}

return [HttpStatusCode.FORBIDDEN, HttpStatusCode.UNAUTHORIZED].includes(httpStatus)
}
5 changes: 1 addition & 4 deletions frontend/src/auth/getOIDCConfig.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import { isCypress } from '@utils/isCypress'
import { WebStorageStateStore } from 'oidc-client-ts'

const IS_CYPRESS = isCypress()

export function getOIDCConfig() {
const IS_OIDC_ENABLED = import.meta.env.FRONTEND_OIDC_ENABLED === 'true'
const OIDC_REDIRECT_URI = import.meta.env.FRONTEND_OIDC_REDIRECT_URI
Expand Down Expand Up @@ -30,7 +27,7 @@ export function getOIDCConfig() {

return {
// eslint-disable-next-line @typescript-eslint/naming-convention
IS_OIDC_ENABLED: IS_CYPRESS ? false : IS_OIDC_ENABLED,
IS_OIDC_ENABLED,
oidcConfig
}
}
26 changes: 20 additions & 6 deletions frontend/src/auth/hooks/useGetUserAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
import { useTracking } from '@hooks/useTracking'
import { setUser } from '@sentry/react'
import { isCypress } from '@utils/isCypress'
import { useCallback, useEffect, useMemo } from 'react'
import { type AuthContextProps, useAuth } from 'react-oidc-context'

import { useGetCurrentUserAuthorizationQueryOverride } from './useGetCurrentUserAuthorizationQueryOverride'

import type { UserAccountContextType } from '../../context/UserAccountContext'

const IS_CYPRESS = isCypress() || true

/**
* When using Cypress, we stub `useAuth()`
*/
export function useGetUserAccount(): UserAccountContextType {
// `| undefined` because it's undefined if the OIDC is disabled which is the case for Cypress tests
const auth = useAuth() as AuthContextProps | undefined
const { trackUserId } = useTracking()
const { data: user } = useGetCurrentUserAuthorizationQueryOverride({ skip: !auth?.isAuthenticated })
const { data: user } = useGetCurrentUserAuthorizationQueryOverride({ skip: !IS_CYPRESS && !auth?.isAuthenticated })
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

J'aimerais bien avoir une autre méthode que ça pour stubber cette requête, mais je n'arrive pas à utiliser cy.stub pour useAuth.
@ivangabriele je suis preneur d'une idée si ut en as!


useEffect(() => {
if (auth?.user?.profile?.email) {
Expand All @@ -31,15 +37,23 @@ export function useGetUserAccount(): UserAccountContextType {
auth.signoutRedirect({ id_token_hint: idTokenHint ?? '' })
}, [auth])

const userAccount = useMemo(
() => ({
const userAccount = useMemo(() => {
if (IS_CYPRESS) {
return {
email: 'dummy@cypress.test',
isAuthenticated: true,
isSuperUser: user?.isSuperUser ?? false,
logout
}
}

return {
email: auth?.user?.profile?.email,
isAuthenticated: auth?.isAuthenticated ?? false,
isSuperUser: user?.isSuperUser ?? false,
logout
}),
[logout, user, auth?.isAuthenticated, auth?.user?.profile?.email]
)
}
}, [logout, user, auth?.isAuthenticated, auth?.user?.profile?.email])

useEffect(
() =>
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/auth/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export type UserAuthorization = {
isLogged: boolean | undefined
isSuperUser: boolean | undefined
mustReload: boolean | undefined
}
4 changes: 2 additions & 2 deletions frontend/src/auth/utils.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { HttpStatusCode } from '@api/constants'
import { isUnauthorizedOrForbidden } from '@api/utils'

import { paths } from '../paths'
import { router } from '../router'
Expand All @@ -9,7 +9,7 @@ import type { CustomResponseError } from '@api/types'
* Redirect to Login page if any HTTP request in Unauthorized
*/
export function redirectToLoginIfUnauthorized(error: CustomResponseError) {
if (!error.path.includes(paths.backendForFrontend) || error.status !== HttpStatusCode.UNAUTHORIZED) {
if (!error.path.includes(paths.backendForFrontend) || !isUnauthorizedOrForbidden(error.status)) {
return
}

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/utils/isCypress.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Detects whether the browser app is running in Cypress.
*
* @description
* We use a Cypress session to inject inject a Local Storage key
* We use a Cypress session to inject a Local Storage key
* so that we can detect when the browser app is running in Cypress.
*
* @see https://docs.cypress.io/faq/questions/using-cypress-faq#How-do-I-preserve-cookies--localStorage-in-between-my-tests
Expand Down
Loading