Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions backend/.render-build-trigger
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# This file is used to force Docker service rebuilds in PR previews
# Modify LAST_UPDATE to trigger rebuilds

LAST_UPDATE=2026-09-30T12:43:29Z
LAST_UPDATE=2026-10-14T15:18:46Z

# Instructions:
# 1. To force rebuild of Docker services in a PR, update LAST_UPDATE
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-09-30T12:43:29Z
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-10-14T15:18:46Z
# 2. Commit and push changes to trigger Docker rebuilds
4 changes: 2 additions & 2 deletions collect/.render-build-trigger
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# This file is used to force Docker service rebuilds in PR previews
# Modify LAST_UPDATE to trigger rebuilds

LAST_UPDATE=2026-09-30T12:43:29Z
LAST_UPDATE=2026-10-14T15:18:46Z

# Instructions:
# 1. To force rebuild of Docker services in a PR, update LAST_UPDATE
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-09-30T12:43:29Z
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-10-14T15:18:46Z
# 2. Commit and push changes to trigger Docker rebuilds
4 changes: 2 additions & 2 deletions frontend/.render-build-trigger
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# This file is used to force Docker service rebuilds in PR previews
# Modify LAST_UPDATE to trigger rebuilds

LAST_UPDATE=2026-09-30T12:43:29Z
LAST_UPDATE=2026-10-14T15:18:46Z

# Instructions:
# 1. To force rebuild of Docker services in a PR, update LAST_UPDATE
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-09-30T12:43:29Z
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-10-14T15:18:46Z
# 2. Commit and push changes to trigger Docker rebuilds
154 changes: 81 additions & 73 deletions frontend/src/components/account/impersonation/ImpersonationPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,90 +94,98 @@ async function disableConsent() {
)
"
/>
<NeSkeleton
v-if="state.status === 'pending' || asyncStatus === 'loading'"
:lines="3"
class="max-w-2xl"
/>
<!-- get consent error notification -->
<NeInlineNotification
v-else-if="state.status === 'error'"
kind="error"
:title="$t('account.impersonation.cannot_retrieve_impersonation_consent')"
:description="state.error.message"
/>
<template v-else>
<!-- delete consent error notification -->
<template v-if="!loginStore.isImpersonating">
<NeSkeleton
v-if="state.status === 'pending' || asyncStatus === 'loading'"
:lines="3"
class="max-w-2xl"
/>
<!-- get consent error notification -->
<NeInlineNotification
v-if="deleteConsentError?.message"
v-else-if="state.status === 'error'"
kind="error"
:title="t('account.impersonation.cannot_disable_impersonation_consent')"
:description="deleteConsentError.message"
:title="$t('account.impersonation.cannot_retrieve_impersonation_consent')"
:description="state.error.message"
/>
<template v-if="state.data.consent">
<!-- consent is enabled -->
<div class="flex flex-col items-start gap-6">
<div class="flex items-center gap-2">
<FontAwesomeIcon
:icon="faCircleCheck"
class="text-icon-enabled size-4"
aria-hidden="true"
/>
<span>
{{
t('account.impersonation.impersonation_consent_is_enabled_until_time', {
time: consentExpirationDate,
})
}}
</span>
</div>
<!-- revoke consent button -->
<NeButton
kind="tertiary"
size="lg"
:disabled="
deleteConsentLoading || loginStore.isOwnerAccount || loginStore.isImpersonating
"
:loading="deleteConsentLoading"
@click.prevent="disableConsent"
class="-ml-2.5"
>
{{ $t('account.impersonation.revoke_impersonation_consent') }}
</NeButton>
</div>
</template>
<template v-else>
<!-- consent is disabled -->
<div class="flex flex-col items-start gap-6">
<div class="flex items-center gap-2">
<FontAwesomeIcon
:icon="faCircleInfo"
class="size-4 text-indigo-500 dark:text-indigo-300"
aria-hidden="true"
/>
<span>
{{ t('account.impersonation.impersonation_consent_is_disabled') }}
</span>
<!-- delete consent error notification -->
<NeInlineNotification
v-if="deleteConsentError?.message"
kind="error"
:title="t('account.impersonation.cannot_disable_impersonation_consent')"
:description="deleteConsentError.message"
/>
<template v-if="state.data.consent">
<!-- consent is enabled -->
<div class="flex flex-col items-start gap-6">
<div class="flex items-center gap-2">
<FontAwesomeIcon
:icon="faCircleCheck"
class="text-icon-enabled size-4"
aria-hidden="true"
/>
<span>
{{
t('account.impersonation.impersonation_consent_is_enabled_until_time', {
time: consentExpirationDate,
})
}}
</span>
</div>
<!-- revoke consent button -->
<NeButton
kind="tertiary"
size="lg"
:disabled="
deleteConsentLoading || loginStore.isOwnerAccount || loginStore.isImpersonating
"
:loading="deleteConsentLoading"
@click.prevent="disableConsent"
class="-ml-2.5"
>
{{ $t('account.impersonation.revoke_impersonation_consent') }}
</NeButton>
</div>
<!-- enable consent button -->
<NeButton
kind="secondary"
size="lg"
:disabled="
deleteConsentLoading || loginStore.isOwnerAccount || loginStore.isImpersonating
"
@click.prevent="isShownEnableConsentModal = true"
>
{{ $t('account.impersonation.consent_to_impersonation') }}
</NeButton>
</div>
</template>
<template v-else>
<!-- consent is disabled -->
<div class="flex flex-col items-start gap-6">
<div class="flex items-center gap-2">
<FontAwesomeIcon
:icon="faCircleInfo"
class="size-4 text-indigo-500 dark:text-indigo-300"
aria-hidden="true"
/>
<span>
{{ t('account.impersonation.impersonation_consent_is_disabled') }}
</span>
</div>
<!-- enable consent button -->
<NeButton
kind="secondary"
size="lg"
:disabled="
deleteConsentLoading || loginStore.isOwnerAccount || loginStore.isImpersonating
"
@click.prevent="isShownEnableConsentModal = true"
>
{{ $t('account.impersonation.consent_to_impersonation') }}
</NeButton>
</div>
</template>
</template>
</template>
</div>
<NeHeading tag="h4" class="mt-10 mb-5">
{{ $t('account.impersonation.sessions') }}
</NeHeading>
<SessionsTable />
<NeInlineNotification
v-if="loginStore.isImpersonating"
kind="info"
:title="$t('account.impersonation.sessions_not_available')"
:description="$t('account.impersonation.sessions_not_available_impersonating_description')"
/>
<SessionsTable v-else />
<EnableImpersonationConsentModal
:visible="isShownEnableConsentModal"
@close="isShownEnableConsentModal = false"
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/i18n/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,8 @@
"sessions": "Sessions",
"sessions_description": "Overview of all sessions during which you were impersonated by a user with the Nethesis staff roles (Staff, Owner).",
"cannot_retrieve_impersonation_sessions": "Cannot retrieve impersonation sessions",
"sessions_not_available": "Sessions not available",
"sessions_not_available_impersonating_description": "Impersonation sessions are not available while impersonating another user.",
"session_start": "Start",
"session_end": "End",
"duration": "Duration",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/i18n/it/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,8 @@
"sessions": "Sessioni",
"sessions_description": "Elenco delle sessioni durante le quali sei stato impersonato da un utente con i ruoli dello staff Nethesis (Staff, Owner).",
"cannot_retrieve_impersonation_sessions": "Impossibile recuperare le sessioni di impersonificazione",
"sessions_not_available": "Sessioni non disponibili",
"sessions_not_available_impersonating_description": "Le sessioni di impersonificazione non sono disponibili mentre impersoni un altro utente.",
"session_start": "Inizio",
"session_end": "Fine",
"duration": "Durata",
Expand Down
4 changes: 3 additions & 1 deletion frontend/src/lib/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,9 @@ export const configureAxios = () => {
console.warn('[interceptor]', 'Unrecoverable 401, re-authenticating')
loginStore.forceReauth()
} else if (error.response?.status == 403) {
router.push({ name: 'forbidden' })
if (error.response?.data?.data?.reason !== 'impersonation_active') {
router.push({ name: 'forbidden' })
}
} else if (!isValidationErrorCode(error.response?.data?.code)) {
// show error notification if it's not a validation error
notificationsStore.createNotificationFromAxiosError(error)
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/queries/impersonationConsent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const useImpersonationConsent = defineQuery(() => {

const { state, asyncStatus, ...rest } = useQuery({
key: () => [IMPERSONATION_CONSENT_KEY],
enabled: () => !!loginStore.jwtToken,
enabled: () => !!loginStore.jwtToken && !loginStore.isImpersonating,
query: () => getConsent(),
})

Expand Down
5 changes: 4 additions & 1 deletion frontend/src/queries/impersonationSessionAudit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export const useImpersonationSessionAudit = defineQuery(() => {
pageSize: pageSize.value,
},
],
enabled: () => !!loginStore.jwtToken && !!sessionAuditStore.session?.session_id,
enabled: () =>
!!loginStore.jwtToken &&
!loginStore.isImpersonating &&
!!sessionAuditStore.session?.session_id,
query: () =>
getSessionAudit(sessionAuditStore.session?.session_id || '', pageNum.value, pageSize.value),
})
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/queries/impersonationSessions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const useImpersonationSessions = defineQuery(() => {
pageSize: pageSize.value,
},
],
enabled: () => !!loginStore.jwtToken,
enabled: () => !!loginStore.jwtToken && !loginStore.isImpersonating,
query: () => getSessions(pageNum.value, pageSize.value),
})

Expand Down
4 changes: 2 additions & 2 deletions proxy/.render-build-trigger
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
# This file is used to force Docker service rebuilds in PR previews
# Modify LAST_UPDATE to trigger rebuilds

LAST_UPDATE=2026-09-30T12:43:29Z
LAST_UPDATE=2026-10-14T15:18:46Z

# Instructions:
# 1. To force rebuild of Docker services in a PR, update LAST_UPDATE
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-09-30T12:43:29Z
# 2. Run: perl -i -pe "s/LAST_UPDATE=2026-10-14T15:18:46Z
# 2. Commit and push changes to trigger Docker rebuilds
2 changes: 1 addition & 1 deletion services/mimir/.render-build-trigger
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# This file is used to force Docker service rebuilds in PR previews
# Modify LAST_UPDATE to trigger rebuilds

LAST_UPDATE=2026-09-30T12:43:29Z
LAST_UPDATE=2026-10-14T15:18:46Z

# Instructions:
# 1. To force rebuild of Docker services in a PR, update LAST_UPDATE
Expand Down
Loading