mirror of
https://github.com/alangrainger/immich-public-proxy.git
synced 2025-01-15 04:26:45 +01:00
Expire asset decryption tokens
This commit is contained in:
parent
f445844766
commit
f78a02bb8d
4 changed files with 29 additions and 10 deletions
4
.github/workflows/ci.yaml
vendored
4
.github/workflows/ci.yaml
vendored
|
@ -53,7 +53,7 @@ jobs:
|
|||
build-args: |
|
||||
PACKAGE_VERSION=${{ env.PACKAGE_VERSION }}
|
||||
tags: |
|
||||
${{ github.repository }}:latest
|
||||
${{ github.repository }}:${{ env.PACKAGE_VERSION }}
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
${{ github.repository }}:latest
|
||||
ghcr.io/${{ github.repository }}:${{ env.PACKAGE_VERSION }}
|
||||
ghcr.io/${{ github.repository }}:latest
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "immich-public-proxy",
|
||||
"version": "1.3.5",
|
||||
"version": "1.3.6",
|
||||
"scripts": {
|
||||
"dev": "ts-node src/index.ts",
|
||||
"build": "npx tsc",
|
||||
|
|
|
@ -187,7 +187,7 @@ class Immich {
|
|||
photoUrl (key: string, id: string, size?: ImageSize, password?: string) {
|
||||
const params = { key, size }
|
||||
if (password) {
|
||||
Object.assign(params, encrypt(password))
|
||||
Object.assign(params, this.encryptPassword(password))
|
||||
}
|
||||
return this.buildUrl(`/photo/${key}/${id}`, params)
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ class Immich {
|
|||
* Return the video data URL for a video
|
||||
*/
|
||||
videoUrl (key: string, id: string, password?: string) {
|
||||
const params = password ? encrypt(password) : {}
|
||||
const params = password ? this.encryptPassword(password) : {}
|
||||
return this.buildUrl(`/video/${key}/${id}`, params)
|
||||
}
|
||||
|
||||
|
@ -215,6 +215,18 @@ class Immich {
|
|||
isKey (key: string) {
|
||||
return !!key.match(/^[\w-]+$/)
|
||||
}
|
||||
|
||||
/**
|
||||
* When loading assets from a password-protected link, make the decryption key valid for a
|
||||
* short time. If the visitor loads the share link again, it will renew that expiry time.
|
||||
* This prevents people from sharing the image links and bypassing password protection.
|
||||
*/
|
||||
encryptPassword (password: string) {
|
||||
return encrypt(JSON.stringify({
|
||||
password,
|
||||
expires: dayjs().add(1, 'hour').format()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const immich = new Immich()
|
||||
|
|
|
@ -37,13 +37,20 @@ app.get('/:type(photo|video)/:key/:id', async (req, res) => {
|
|||
res.set('Cache-Control', 'public, max-age=' + process.env.CACHE_AGE)
|
||||
// Check for valid key and ID
|
||||
if (immich.isKey(req.params.key) && immich.isId(req.params.id)) {
|
||||
// Decrypt the password, if one was provided
|
||||
let password
|
||||
// Validate the password payload, if one was provided
|
||||
if (req.query?.cr && req.query?.iv) {
|
||||
password = decrypt({
|
||||
iv: toString(req.query.iv),
|
||||
cr: toString(req.query.cr)
|
||||
})
|
||||
try {
|
||||
const payload = JSON.parse(decrypt({
|
||||
iv: toString(req.query.iv),
|
||||
cr: toString(req.query.cr)
|
||||
}))
|
||||
if (payload?.expires && dayjs(payload.expires) > dayjs()) {
|
||||
password = payload.password
|
||||
} else {
|
||||
log(`Attempted to load assets from ${req.params.key} with an expired decryption token`)
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
// Check if the key is a valid share link
|
||||
const sharedLink = (await immich.getShareByKey(req.params.key, password))?.link
|
||||
|
|
Loading…
Reference in a new issue