Skip to content

Commit

Permalink
UBERF-9537: Fix Invalid navigate to guest not authorised (#8121)
Browse files Browse the repository at this point in the history
Signed-off-by: Andrey Sobolev <[email protected]>
  • Loading branch information
haiodo committed Mar 3, 2025
1 parent c5c0ab9 commit a6b8d6c
Show file tree
Hide file tree
Showing 13 changed files with 140 additions and 61 deletions.
9 changes: 8 additions & 1 deletion packages/presentation/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,14 @@ export function isCollabAttr (hierarchy: Hierarchy, key: KeyedAttribute): boolea
*/
export function decodeTokenPayload (token: string): any {
try {
return JSON.parse(atob(token.split('.')[1]))
if (token === '') {
return {}
}
const tsplit = token.split('.')
if (tsplit.length < 2) {
return {}
}
return JSON.parse(atob(tsplit[1]))
} catch (err: any) {
console.error(err)
return {}
Expand Down
4 changes: 4 additions & 0 deletions packages/ui/src/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,10 @@ export const setTreeCollapsed = (_id: any, collapsed: boolean, prefix?: string):
collapsed ? localStorage.setItem(key, COLLAPSED) : localStorage.removeItem(key)
}

export function isSameSegments (a: PlatformLocation, b: PlatformLocation, len = 2): boolean {
return a.path.slice(0, len).every((segment, index) => segment === b.path[index])
}

export function restoreLocation (loc: PlatformLocation, app: Plugin): void {
const last = localStorage.getItem(`${locationStorageKeyId}_${app}`)

Expand Down
11 changes: 4 additions & 7 deletions plugins/guest-resources/src/components/Guest.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,22 @@
Popup,
PopupAlignment,
ResolvedLocation,
Separator,
TooltipInstance,
areLocationsEqual,
closePanel,
getCurrentLocation,
getLocation,
navigate,
showPanel,
defineSeparators,
deviceOptionsStore as deviceInfo,
getCurrentLocation,
setResolvedLocation,
deviceOptionsStore as deviceInfo
showPanel
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import { ListSelectionProvider, parseLinkId, restrictionStore, updateFocus } from '@hcengineering/view-resources'
import workbench, { Application, NavigatorModel, SpecialNavModel, ViewConfiguration } from '@hcengineering/workbench'
import { SpaceView, buildNavModel } from '@hcengineering/workbench-resources'
import { workbenchGuestSeparators } from '..'
import guest from '../plugin'
import { checkAccess } from '../utils'
import { workbenchGuestSeparators } from '..'
const excludedApps = getMetadata(workbench.metadata.ExcludedApplications) ?? []
$deviceInfo.navigator.visible = false
Expand Down
15 changes: 13 additions & 2 deletions plugins/guest-resources/src/components/GuestApp.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,24 @@
import { getMetadata } from '@hcengineering/platform'
import { Label, Loading, Notifications, location } from '@hcengineering/ui'
import { upgradeDownloadProgress } from '@hcengineering/presentation'
import { connect, versionError } from '../connect'
import { connect, versionError, invalidError } from '../connect'
import { guestId } from '@hcengineering/guest'
import workbench from '@hcengineering/workbench'
import Guest from './Guest.svelte'
import plugin from '../plugin'
</script>

{#if $location.path[0] === guestId}
{#await connect(getMetadata(workbench.metadata.PlatformTitle) ?? 'Platform')}
<Loading />
{:then client}
{#if $versionError}
{#if $invalidError}
{invalidError}
<div class="version-wrapper">
<h1><Label label={plugin.string.LinkWasRevoked} /></h1>
</div>
{:else if $versionError}
<div class="version-wrapper">
<div class="antiPopup version-popup">
<h1><Label label={workbench.string.ServerUnderMaintenance} /></h1>
Expand All @@ -52,14 +58,19 @@
<style lang="scss">
.version-wrapper {
height: 100%;
width: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.please-update {
margin-bottom: 1rem;
}
.version-popup {
display: flex;
align-items: center;
justify-content: center;
padding: 2rem;
flex-grow: 1;
}
</style>
14 changes: 6 additions & 8 deletions plugins/guest-resources/src/connect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import { logOut } from '@hcengineering/workbench'
import { writable, get } from 'svelte/store'

export const versionError = writable<string | undefined>(undefined)

export const invalidError = writable<boolean>(false)
const versionStorageKey = 'last_server_version'

let _token: string | undefined
Expand All @@ -37,9 +39,7 @@ export async function connect (title: string): Promise<Client | undefined> {
const token = loc.query?.token
const wsUrl = loc.path[1]
if (wsUrl === undefined || token == null) {
navigate({
path: [loginId]
})
invalidError.set(true)
return
}

Expand All @@ -51,7 +51,7 @@ export async function connect (title: string): Promise<Client | undefined> {
)
// something went wrong with selecting workspace with the selected token
await logOut()
navigate({ path: [loginId] })
invalidError.set(true)
return
}

Expand Down Expand Up @@ -118,10 +118,7 @@ export async function connect (title: string): Promise<Client | undefined> {
},
onUnauthorized: () => {
void logOut().then(() => {
navigate({
path: [loginId],
query: {}
})
invalidError.set(true)
})
},
// We need to refresh all active live queries and clear old queries.
Expand Down Expand Up @@ -224,6 +221,7 @@ export async function connect (title: string): Promise<Client | undefined> {
}
}

invalidError.set(false)
versionError.set(undefined)
// Update window title
document.title = [wsUrl, title].filter((it) => it).join(' - ')
Expand Down
21 changes: 1 addition & 20 deletions plugins/login-resources/src/components/SelectWorkspace.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -132,8 +132,6 @@
throw err
}
}
$: isAdmin = isAdminUser()
let search: string = ''
</script>

Expand All @@ -151,7 +149,7 @@
<div class="status">
<StatusControl {status} />
</div>
{#if isAdmin}
{#if workspaces.length > 10}
<div class="ml-2 mr-2 mb-2 flex-grow">
<SearchEdit bind:value={search} width={'100%'} />
</div>
Expand Down Expand Up @@ -188,24 +186,7 @@
{/if}
</span>
<span class="text-xs flex-row-center flex-center">
{#if isAdmin}
{workspace.url}
{#if workspace.region !== undefined}
at ({workspace.region})
{/if}
{/if}
<div class="text-sm">
{#if isAdmin}
{#if workspace.backupInfo != null}
{@const sz = workspace.backupInfo.dataSize + workspace.backupInfo.blobsSize}
{@const szGb = Math.round((sz * 100) / 1024) / 100}
{#if szGb > 0}
- {Math.round((sz * 100) / 1024) / 100}Gb -
{:else}
- {Math.round(sz)}Mb -
{/if}
{/if}
{/if}
({lastUsageDays} days)
</div>
</span>
Expand Down
31 changes: 27 additions & 4 deletions plugins/login-resources/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import platform, {
import presentation from '@hcengineering/presentation'
import {
getCurrentLocation,
isSameSegments,
locationStorageKeyId,
locationToUrl,
navigate,
Expand Down Expand Up @@ -297,6 +298,21 @@ export async function getAccount (doNavigate: boolean = true): Promise<LoginInfo
if (err instanceof PlatformError) {
await handleStatusError('Get account error', err.status)

if (err.status.code === platform.status.Unauthorized) {
setMetadata(presentation.metadata.Token, null)
setMetadata(presentation.metadata.Workspace, null)
setMetadata(presentation.metadata.WorkspaceId, null)
setMetadataLocalStorage(login.metadata.LastToken, null)
setMetadataLocalStorage(login.metadata.LoginEndpoint, null)
setMetadataLocalStorage(login.metadata.LoginEmail, null)

const loc = getCurrentLocation()
loc.path[1] = 'login'
loc.path.length = 2
navigate(loc)
return
}

return null
} else {
Analytics.handleError(err)
Expand Down Expand Up @@ -454,11 +470,18 @@ export function navigateToWorkspace (
// Json parse error could be ignored
}
}
const last = localStorage.getItem(`${locationStorageKeyId}_${workspaceUrl}`)
if (last !== null) {
navigate(JSON.parse(last), replace)
const newLoc: Location = { path: [workbenchId, workspaceUrl] }
let last: Location | undefined
try {
last = JSON.parse(localStorage.getItem(`${locationStorageKeyId}_${workspace}`) ?? '')
} catch (err: any) {
// Ignore
}
if (last != null && isSameSegments(last, newLoc, 2)) {
// If last location in our workspace path, use it.
navigate(last, replace)
} else {
navigate({ path: [workbenchId, workspaceUrl] }, replace)
navigate(newLoc, replace)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
closePopup,
fetchMetadataLocalStorage,
getCurrentLocation,
isSameSegments,
locationStorageKeyId,
locationToUrl,
navigate,
Expand Down Expand Up @@ -58,11 +59,19 @@
e.preventDefault()
closePopup()
closePopup()
if (wsUrl !== getCurrentLocation().path[1]) {
const last = localStorage.getItem(`${locationStorageKeyId}_${wsUrl}`)
if (last !== null) {
navigate(JSON.parse(last))
} else navigate({ path: [workbenchId, wsUrl] })
const current = getCurrentLocation()
if (wsUrl !== current.path[1]) {
let last: Location | undefined
try {
last = JSON.parse(localStorage.getItem(`${locationStorageKeyId}_${wsUrl}`) ?? '')
} catch (err: any) {
// Ignore
}
if (last != null && isSameSegments(last, current, 2)) {
navigate(last)
} else {
navigate({ path: [workbenchId, wsUrl] })
}
}
}
}
Expand Down Expand Up @@ -148,7 +157,7 @@
{/if}
</div>
<div class="p-2 ml-2 mb-4 select-text flex-col bordered">
{decodeTokenPayload(getMetadata(presentation.metadata.Token) ?? '').workspace}
{decodeTokenPayload(getMetadata(presentation.metadata.Token) ?? '').workspace ?? ''}
</div>
{/if}
<div class="ap-scroll">
Expand Down
12 changes: 8 additions & 4 deletions plugins/workbench-resources/src/components/Workbench.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@
showPopup,
TooltipInstance,
workbenchSeparators,
resizeObserver
resizeObserver,
isSameSegments
} from '@hcengineering/ui'
import view from '@hcengineering/view'
import {
Expand Down Expand Up @@ -429,9 +430,12 @@
const last = localStorage.getItem(`${locationStorageKeyId}_${loc.path[1]}`)
if (last != null) {
const lastValue = JSON.parse(last)
navigateDone = navigate(lastValue)
if (navigateDone) {
return
if (isSameSegments(lastValue, loc, 2)) {
navigateDone = navigate(lastValue)
if (navigateDone) {
return
}
}
}
if (app === undefined && !navigateDone) {
Expand Down
40 changes: 40 additions & 0 deletions server/account/src/__tests__/token.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//
// Copyright © 2020, 2021 Anticrm Platform Contributors.
// Copyright © 2021 Hardcore Engineering Inc.
//
// Licensed under the Eclipse Public License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. You may
// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//
// See the License for the specific language governing permissions and
// limitations under the License.
//

import { generateToken } from '@hcengineering/server-token'

export function decodeTokenPayload (token: string): any {
try {
return JSON.parse(atob(token.split('.')[1]))
} catch (err: any) {
console.error(err)
return {}
}
}
describe('generate-tokens', () => {
it('should generate tokens', async () => {
const extra: Record<string, any> = { confirmed: 'true' }
const token = generateToken('[email protected]', { name: '' }, extra, 'secret')
console.log(token)
const decodedPayload = decodeTokenPayload(token)
expect(decodedPayload).toEqual({
confirmed: 'true',
email: '[email protected]',
workspace: ''
})
expect(decodedPayload.admin).not.toBe('true')
})
})
5 changes: 3 additions & 2 deletions server/token/src/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ const getSecret = (): string => {
export function generateToken (
accountUuid: PersonUuid,
workspaceUuid?: WorkspaceUuid,
extra?: Record<string, string>
extra?: Record<string, string>,
secret?: string
): string {
return encode(
{ ...(extra !== undefined ? { extra } : {}), account: accountUuid, workspace: workspaceUuid },
getSecret()
secret ?? getSecret()
)
}

Expand Down
4 changes: 3 additions & 1 deletion tests/sanity/tests/model/tracker/issues-page.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,9 @@ export class IssuesPage extends CommonTrackerPage {
appleseedJohnButton = (): Locator => this.page.locator('button.menu-item:has-text("Appleseed John")')
estimationEditor = (): Locator => this.page.locator('#estimation-editor')
dueDateButton = (): Locator => this.page.locator('button:has-text("Due date")')
specificDay = (day: string): Locator => this.page.locator(`.date-popup-container div.day >> text=${day}`).first()
specificDay = (day: string): Locator =>
this.page.locator(`.date-popup-container div.day:not(.wrongMonth) >> text=${day}`).first()

inputTextPlaceholder = (): Locator => this.page.getByPlaceholder('Type text...')
confirmInput = (): Locator => this.page.locator('.selectPopup button')

Expand Down
Loading

0 comments on commit a6b8d6c

Please sign in to comment.