diff --git a/src/features/party/PartiesProvider.tsx b/src/features/party/PartiesProvider.tsx index 36a2d085f9..1338f65cbf 100644 --- a/src/features/party/PartiesProvider.tsx +++ b/src/features/party/PartiesProvider.tsx @@ -9,7 +9,7 @@ import { delayedContext } from 'src/core/contexts/delayedContext'; import { createQueryContext } from 'src/core/contexts/queryContext'; import { DisplayError } from 'src/core/errorHandling/DisplayError'; import { Loader } from 'src/core/loading/Loader'; -import { instanceQueries, useInstanceDataQueryArgs } from 'src/features/instance/InstanceContext'; +import { instanceQueries, useInstanceDataQueryArgs, useLaxInstanceId } from 'src/features/instance/InstanceContext'; import { NoValidPartiesError } from 'src/features/instantiate/containers/NoValidPartiesError'; import { flattenParties } from 'src/features/party/partyUtils'; import { useShouldFetchProfile } from 'src/features/profile/ProfileProvider'; @@ -117,6 +117,7 @@ const { Provider: RealSelectedPartyProvider, useCtx: useSelectedPartyCtx } = cre */ const SelectedPartyProvider = ({ children }: PropsWithChildren) => { const validParties = useValidParties(); + const instanceId = useLaxInstanceId(); const [sentToMutation, setSentToMutation] = useState(undefined); const { mutateAsync, data: dataFromMutation, error: errorFromMutation } = useSetSelectedPartyMutation(); const { data: partyFromQuery, isLoading, error: errorFromQuery } = useSelectedPartyQuery(true); @@ -131,7 +132,7 @@ const SelectedPartyProvider = ({ children }: PropsWithChildren) => { return ; } - if (!validParties?.length) { + if (!instanceId && !validParties?.length) { return ; } diff --git a/test/e2e/integration/signering-brukerstyrt/existing-instance-access.ts b/test/e2e/integration/signering-brukerstyrt/existing-instance-access.ts new file mode 100644 index 0000000000..616424478b --- /dev/null +++ b/test/e2e/integration/signering-brukerstyrt/existing-instance-access.ts @@ -0,0 +1,64 @@ +import { AppFrontend } from 'test/e2e/pageobjects/app-frontend'; +import { Tenor } from 'test/e2e/support/users'; + +const appFrontend = new AppFrontend(); + +describe('Existing instance access', () => { + it('allows access when the user has no party allowed to instantiate', () => { + let userIsReopeningInstance = false; + + // Regression setup for https://github.com/Altinn/altinn-studio/issues/19576: + // PartyProvider used to require at least one party allowed to instantiate before rendering anything, including + // an existing instance. Consequently, a user with access to an existing instance saw NoValidPartiesError (403) + // solely because they could not create a new instance. The test user can normally instantiate this app, so + // both the buggy and fixed frontend would pass without this mock. Returning no allowed parties when reopening + // the instance recreates the failing condition and ensures the test detects that unconditional check if it is + // reintroduced. The user's access to the existing instance itself remains real and is verified below. + cy.intercept('GET', '**/api/v1/parties?allowedtoinstantiatefilter=true', (req) => { + const isReopeningInstance = userIsReopeningInstance; + if (isReopeningInstance) { + req.alias = 'partiesAllowedToInstantiate'; + } + req.continue((res) => { + res.send(isReopeningInstance ? [] : res.body); + }); + }); + + cy.startAppInstance(appFrontend.apps.signeringBrukerstyrt, { + cyUser: null, + tenorUser: Tenor.users.humanAndrefiolin, + authenticationLevel: '2', + }); + + cy.findByRole('heading', { name: 'Hvem vil du sende inn for?' }).should('be.visible'); + cy.findByRole('button', { + name: new RegExp(`org\\.nr\\. ${Tenor.orgs.sivilisertAvansertIsbjoernSA.orgNr}`, 'i'), + }).click(); + + const companyName = 'Testselskap AS'; + cy.findByRole('textbox', { name: /navn/i }).type(companyName); + cy.waitUntilSaved(); + + cy.url().then((instanceUrl) => { + const instanceHash = new URL(instanceUrl).hash; + const instanceId = instanceHash.match(/\d+\/[\da-f-]{36}/i)?.[0]; + expect(instanceId, 'instance ID').to.exist; + + cy.intercept('GET', `**/instances/${instanceId}`).as('existingInstance'); + userIsReopeningInstance = true; + cy.startAppInstance(appFrontend.apps.signeringBrukerstyrt, { + cyUser: null, + tenorUser: Tenor.users.humanAndrefiolin, + authenticationLevel: '2', + urlSuffix: instanceHash, + }); + + // Access to an existing instance must depend on the instance request, not whether the user can instantiate + // the app. The latter is only relevant when entering the app without an instance. + cy.wait('@partiesAllowedToInstantiate').its('response.body').should('deep.equal', []); + cy.wait('@existingInstance').its('response.statusCode').should('eq', 200); + cy.findByRole('textbox', { name: /navn/i }).should('have.value', companyName); + cy.get(appFrontend.instanceErrorCode).should('not.exist'); + }); + }); +});