Skip to content

Commit 6f1d381

Browse files
committed
fix: secure blobs
Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
1 parent 2859952 commit 6f1d381

File tree

9 files changed

+83
-15
lines changed

9 files changed

+83
-15
lines changed

dev/docker-compose.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,7 @@ services:
519519
- REGION=cockroach
520520
- QUEUE_CONFIG=${QUEUE_CONFIG}
521521
- OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4318/v1/traces
522+
- SECURE=true
522523
restart: unless-stopped
523524
hulylake:
524525
image: hardcoreeng/hulylake

foundations/core/packages/storage-client/src/client/datalake.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core'
1717
import { FileStorage, FileStorageUploadOptions } from '../types'
1818
import { uploadMultipart, uploadXhr } from '../upload'
1919

20+
const getPathname = (url: string): string => {
21+
const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost'
22+
return new URL(url, base).pathname
23+
}
24+
2025
/** @public */
2126
export class DatalakeStorage implements FileStorage {
2227
constructor (private readonly baseUrl: string) {}
@@ -26,6 +31,11 @@ export class DatalakeStorage implements FileStorage {
2631
return concatLink(this.baseUrl, path)
2732
}
2833

34+
getCookiePath (workspace: string): string {
35+
const url = concatLink(this.baseUrl, `/blob/${workspace}`)
36+
return getPathname(url)
37+
}
38+
2939
async getFileMeta (token: string, workspace: string, file: string): Promise<Record<string, any>> {
3040
const url = concatLink(this.baseUrl, `/meta/${encodeURIComponent(workspace)}/${encodeURIComponent(file)}`)
3141
try {

foundations/core/packages/storage-client/src/client/front.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core'
1717
import { FileStorage, FileStorageUploadOptions } from '../types'
1818
import { uploadXhr } from '../upload'
1919

20+
const getPathname = (url: string): string => {
21+
const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost'
22+
return new URL(url, base).pathname
23+
}
24+
2025
/** @public */
2126
export class FrontStorage implements FileStorage {
2227
constructor (private readonly baseUrl: string) {}
@@ -26,6 +31,11 @@ export class FrontStorage implements FileStorage {
2631
return concatLink(this.baseUrl, path)
2732
}
2833

34+
getCookiePath (workspace: string): string {
35+
const url = concatLink(this.baseUrl, `/${workspace}`)
36+
return getPathname(url)
37+
}
38+
2939
async getFileMeta (token: string, workspace: string, file: string): Promise<Record<string, any>> {
3040
return {}
3141
}

foundations/core/packages/storage-client/src/client/hulylake.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@ import { concatLink } from '@hcengineering/core'
1717
import { FileStorage, FileStorageUploadOptions } from '../types'
1818
import { uploadXhr } from '../upload'
1919

20+
const getPathname = (url: string): string => {
21+
const base = window?.location?.href !== undefined ? window.location.href : 'http://localhost'
22+
return new URL(url, base).pathname
23+
}
24+
2025
/** @public */
2126
export class HulylakeStorage implements FileStorage {
2227
constructor (private readonly baseUrl: string) {}
@@ -26,6 +31,11 @@ export class HulylakeStorage implements FileStorage {
2631
return concatLink(this.baseUrl, path)
2732
}
2833

34+
getCookiePath (workspace: string): string {
35+
const url = concatLink(this.baseUrl, `/api/${workspace}`)
36+
return getPathname(url)
37+
}
38+
2939
async getFileMeta (token: string, workspace: string, file: string): Promise<Record<string, any>> {
3040
return {}
3141
}

foundations/core/packages/storage-client/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export interface FileStorageUploadOptions {
2929
/** @public */
3030
export interface FileStorage {
3131
getFileUrl: (workspace: string, file: string, filename?: string) => string
32+
getCookiePath: (workspace: string) => string
3233
getFileMeta: (token: string, workspace: string, file: string) => Promise<Record<string, any>>
3334
uploadFile: (
3435
token: string,

packages/presentation/src/utils.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -891,15 +891,19 @@ export function isSpaceClass (_class: Ref<Class<Doc>>): boolean {
891891
}
892892

893893
export function setPresentationCookie (token: string, workspaceUuid: WorkspaceUuid): void {
894-
function setToken (path: string): void {
895-
const res =
896-
encodeURIComponent(plugin.metadata.Token.replaceAll(':', '-')) +
897-
'=' +
898-
encodeURIComponent(token) +
899-
`; path=${path}`
900-
document.cookie = res
901-
}
902-
setToken('/files/' + workspaceUuid)
894+
const cookieName = encodeURIComponent(plugin.metadata.Token.replaceAll(':', '-'))
895+
const cookieValue = encodeURIComponent(token)
896+
897+
const storage = getMetadata(plugin.metadata.FileStorage)
898+
if (storage !== undefined) {
899+
let path = `/files/${workspaceUuid}`
900+
try {
901+
path = storage.getCookiePath(workspaceUuid)
902+
} catch {}
903+
904+
const normalized = path.startsWith('/') ? path : `/${path}`
905+
document.cookie = `${cookieName}=${cookieValue}; path=${normalized}`
906+
}
903907
}
904908

905909
export const upgradeDownloadProgress = writable(-1)

services/datalake/pod-datalake/src/config.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface Config {
3535
DbUrl: string
3636
Buckets: BucketConfig[]
3737
CleanupInterval: number
38+
Secure: boolean
3839
Readonly: boolean
3940
Cache: CacheConfig
4041
}
@@ -86,6 +87,7 @@ const config: Config = (() => {
8687
AccountsUrl: process.env.ACCOUNTS_URL,
8788
DbUrl: process.env.DB_URL,
8889
Buckets: parseBucketsConfig(process.env.BUCKETS),
90+
Secure: process.env.SECURE === 'true',
8991
Readonly: process.env.READONLY === 'true',
9092
Cache: {
9193
enabled: process.env.CACHE_ENABLED !== 'false',

services/datalake/pod-datalake/src/middleware.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ export const keepAlive = (options: KeepAliveOptions): RequestHandler => {
3838
}
3939
}
4040

41+
export const withOptionalAuth = (secure: boolean): RequestHandler => {
42+
return secure ? withAuthorization : (req: Request, res: Response, next: NextFunction) => { next() }
43+
}
44+
4145
export const withAdminAuthorization = (req: RequestWithAuth, res: Response, next: NextFunction): void => {
4246
try {
4347
const token = extractToken(req.headers)

services/datalake/pod-datalake/src/server.ts

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ import {
3636
withAuthorization,
3737
withBlob,
3838
withWorkspace,
39-
withReadonly
39+
withReadonly,
40+
withOptionalAuth
4041
} from './middleware'
4142
import {
4243
handleBlobDelete,
@@ -176,13 +177,33 @@ export async function createServer (
176177

177178
app.get('/blob/:workspace', withAdminAuthorization, withWorkspace, wrapRequest(ctx, 'listBlobs', handleBlobList))
178179

179-
app.head('/blob/:workspace/:name', withBlob, wrapRequest(ctx, 'headBlob', handleBlobHead))
180+
app.head(
181+
'/blob/:workspace/:name',
182+
withOptionalAuth(config.Secure),
183+
withBlob,
184+
wrapRequest(ctx, 'headBlob', handleBlobHead)
185+
)
180186

181-
app.head('/blob/:workspace/:name/:filename', withBlob, wrapRequest(ctx, 'headBlob', handleBlobHead))
187+
app.head(
188+
'/blob/:workspace/:name/:filename',
189+
withOptionalAuth(config.Secure),
190+
withBlob,
191+
wrapRequest(ctx, 'headBlob', handleBlobHead)
192+
)
182193

183-
app.get('/blob/:workspace/:name', withBlob, wrapRequest(ctx, 'getBlob', handleBlobGet))
194+
app.get(
195+
'/blob/:workspace/:name',
196+
withOptionalAuth(config.Secure),
197+
withBlob,
198+
wrapRequest(ctx, 'getBlob', handleBlobGet)
199+
)
184200

185-
app.get('/blob/:workspace/:name/:filename', withBlob, wrapRequest(ctx, 'getBlob', handleBlobGet))
201+
app.get(
202+
'/blob/:workspace/:name/:filename',
203+
withOptionalAuth(config.Secure),
204+
withBlob,
205+
wrapRequest(ctx, 'getBlob', handleBlobGet)
206+
)
186207

187208
app.delete('/blob/:workspace/:name', withAuthorization, withBlob, wrapRequest(ctx, 'deleteBlob', handleBlobDelete))
188209

@@ -206,7 +227,12 @@ export async function createServer (
206227

207228
// Blob meta
208229

209-
app.get('/meta/:workspace/:name', withBlob, wrapRequest(ctx, 'getMeta', handleMetaGet))
230+
app.get(
231+
'/meta/:workspace/:name',
232+
withOptionalAuth(config.Secure),
233+
withBlob,
234+
wrapRequest(ctx, 'getMeta', handleMetaGet)
235+
)
210236

211237
app.put('/meta/:workspace/:name', withAuthorization, withBlob, wrapRequest(ctx, 'putMeta', handleMetaPut))
212238

0 commit comments

Comments
 (0)