diff --git a/api/routes/authenticate_media.go b/api/routes/authenticate_media.go
index 7b707c9e..0659ca07 100644
--- a/api/routes/authenticate_media.go
+++ b/api/routes/authenticate_media.go
@@ -2,6 +2,7 @@ package routes
import (
"database/sql"
+ "fmt"
"net/http"
"github.com/viktorstrate/photoview/api/graphql/auth"
@@ -39,7 +40,12 @@ func authenticateMedia(media *models.Media, db *sql.DB, r *http.Request) (succes
// Validate share token password, if set
if shareToken.Password != nil {
- tokenPassword := r.Header.Get("TokenPassword")
+ tokenPasswordCookie, err := r.Cookie(fmt.Sprintf("share-token-pw-%s", shareToken.Value))
+ if err != nil {
+ return false, "unauthorized", http.StatusForbidden, nil
+ }
+ // tokenPassword := r.Header.Get("TokenPassword")
+ tokenPassword := tokenPasswordCookie.Value
if err := bcrypt.CompareHashAndPassword([]byte(*shareToken.Password), []byte(tokenPassword)); err != nil {
if err == bcrypt.ErrMismatchedHashAndPassword {
diff --git a/api/routes/videos.go b/api/routes/videos.go
index 240cfc1e..2cb27cda 100644
--- a/api/routes/videos.go
+++ b/api/routes/videos.go
@@ -37,7 +37,6 @@ func RegisterVideoRoutes(db *sql.DB, router *mux.Router) {
w.Write([]byte("internal server error"))
}
- // TODO: Make sure user is authorized to access video
if success, response, status, err := authenticateMedia(media, db, r); !success {
if err != nil {
log.Printf("WARN: error authenticating video: %s\n", err)
diff --git a/ui/src/Pages/SharePage/MediaSharePage.js b/ui/src/Pages/SharePage/MediaSharePage.js
index 197fb250..cad90b63 100644
--- a/ui/src/Pages/SharePage/MediaSharePage.js
+++ b/ui/src/Pages/SharePage/MediaSharePage.js
@@ -2,7 +2,10 @@ import React, { useContext, useEffect } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import Layout from '../../Layout'
-import ProtectedImage from '../../components/photoGallery/ProtectedImage'
+import {
+ ProtectedImage,
+ ProtectedVideo,
+} from '../../components/photoGallery/ProtectedMedia'
import { SidebarContext } from '../../components/sidebar/Sidebar'
import MediaSidebar from '../../components/sidebar/MediaSidebar'
@@ -12,7 +15,7 @@ const DisplayPhoto = styled(ProtectedImage)`
object-fit: contain;
`
-const DisplayVideo = styled.video`
+const DisplayVideo = styled(ProtectedVideo)`
width: 100%;
max-height: calc(80vh);
`
@@ -29,16 +32,7 @@ const MediaView = ({ media }) => {
}
if (media.type == 'video') {
- return (
-
-
-
- )
+ return
}
throw new Error(`Unsupported media type: ${media.type}`)
diff --git a/ui/src/Pages/SharePage/SharePage.js b/ui/src/Pages/SharePage/SharePage.js
index ebd35409..b52f1046 100644
--- a/ui/src/Pages/SharePage/SharePage.js
+++ b/ui/src/Pages/SharePage/SharePage.js
@@ -16,6 +16,7 @@ import {
Icon,
Message,
} from 'semantic-ui-react'
+import { saveSharePassword, getSharePassword } from '../../authentication'
const shareTokenQuery = gql`
query SharePageToken($token: String!, $password: String) {
@@ -97,7 +98,7 @@ const AuthorizedTokenRoute = ({ match }) => {
const { loading, error, data } = useQuery(shareTokenQuery, {
variables: {
token,
- password: sessionStorage.getItem(`share-token-pw-${token}`),
+ password: getSharePassword(token),
},
})
@@ -185,7 +186,7 @@ const TokenRoute = ({ match }) => {
{
variables: {
token: match.params.token,
- password: sessionStorage.getItem(`share-token-pw-${token}`),
+ password: getSharePassword(match.params.token),
},
}
)
@@ -208,8 +209,8 @@ const TokenRoute = ({ match }) => {
{
- sessionStorage.setItem(`share-token-pw-${token}`, password)
- refetch({ variables: { password: password } })
+ saveSharePassword(token, password)
+ refetch({ variables: { password } })
}}
loading={loading}
/>
diff --git a/ui/src/authentication.js b/ui/src/authentication.js
index bfa2c422..3a606430 100644
--- a/ui/src/authentication.js
+++ b/ui/src/authentication.js
@@ -5,10 +5,21 @@ export function saveTokenCookie(token) {
}
export function clearTokenCookie() {
- document.cookie = 'auth-token= ;max-age=0'
+ document.cookie = 'auth-token= ;max-age=0 ;path=/ ;sameSite=Lax'
}
export function authToken() {
const match = document.cookie.match(/auth-token=([\d\w]+)/)
return match && match[1]
}
+
+export function saveSharePassword(shareToken, password) {
+ document.cookie = `share-token-pw-${shareToken}=${password} ;path=/ ;sameSite=Lax`
+}
+
+export function getSharePassword(shareToken) {
+ const match = document.cookie.match(
+ `share-token-pw-${shareToken}=([\\d\\w]+)`
+ )
+ return match && match[1]
+}
diff --git a/ui/src/components/albumGallery/AlbumBox.js b/ui/src/components/albumGallery/AlbumBox.js
index 561a6007..ce2b787d 100644
--- a/ui/src/components/albumGallery/AlbumBox.js
+++ b/ui/src/components/albumGallery/AlbumBox.js
@@ -2,7 +2,7 @@ import React, { useState } from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
import { Link } from 'react-router-dom'
-import ProtectedImage from '../photoGallery/ProtectedImage'
+import { ProtectedImage } from '../photoGallery/ProtectedMedia'
const AlbumBoxLink = styled(Link)`
width: 240px;
diff --git a/ui/src/components/header/Searchbar.js b/ui/src/components/header/Searchbar.js
index 3f1895da..ed55875b 100644
--- a/ui/src/components/header/Searchbar.js
+++ b/ui/src/components/header/Searchbar.js
@@ -4,7 +4,7 @@ import styled from 'styled-components'
import { useLazyQuery } from '@apollo/react-hooks'
import gql from 'graphql-tag'
import debounce from '../../debounce'
-import ProtectedImage from '../photoGallery/ProtectedImage'
+import { ProtectedImage } from '../photoGallery/ProtectedMedia'
import { NavLink } from 'react-router-dom'
const Container = styled.div`
diff --git a/ui/src/components/photoGallery/MediaThumbnail.js b/ui/src/components/photoGallery/MediaThumbnail.js
index c127983e..db168b0c 100644
--- a/ui/src/components/photoGallery/MediaThumbnail.js
+++ b/ui/src/components/photoGallery/MediaThumbnail.js
@@ -5,7 +5,7 @@ import PropTypes from 'prop-types'
import styled from 'styled-components'
import LazyLoad from 'react-lazyload'
import { Icon } from 'semantic-ui-react'
-import ProtectedImage from './ProtectedImage'
+import { ProtectedImage } from './ProtectedMedia'
const markFavoriteMutation = gql`
mutation markMediaFavorite($mediaId: Int!, $favorite: Boolean!) {
diff --git a/ui/src/components/photoGallery/ProtectedImage.js b/ui/src/components/photoGallery/ProtectedMedia.js
similarity index 56%
rename from ui/src/components/photoGallery/ProtectedImage.js
rename to ui/src/components/photoGallery/ProtectedMedia.js
index a9194a8d..6d41e2a4 100644
--- a/ui/src/components/photoGallery/ProtectedImage.js
+++ b/ui/src/components/photoGallery/ProtectedMedia.js
@@ -1,43 +1,26 @@
-import React, { useEffect, useState } from 'react'
+import React from 'react'
import PropTypes from 'prop-types'
-// let imageCache = {}
+const getProtectedUrl = url => {
+ const imgUrl = new URL(url)
-// export async function fetchProtectedImage(
-// src,
-// { signal, headers: customHeaders } = { signal: null, headers: null }
-// ) {
-// if (src) {
-// if (imageCache[src]) {
-// return imageCache[src]
-// }
+ if (localStorage.getItem('token') == null) {
+ // Get share token if not authorized
-// let headers = {
-// ...customHeaders,
-// }
-// if (localStorage.getItem('token')) {
-// headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`
-// }
+ const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
+ if (tokenRegex) {
+ const token = tokenRegex[1]
+ imgUrl.searchParams.set('token', token)
+ }
+ }
-// let image = await fetch(src, {
-// headers,
-// signal,
-// })
-
-// image = await image.blob()
-// const url = URL.createObjectURL(image)
-
-// // eslint-disable-next-line require-atomic-updates
-// imageCache[src] = url
-
-// return url
-// }
-// }
+ return imgUrl.href
+}
/**
- * An image that needs a authorization header to load
+ * An image that needs authorization to load
*/
-const ProtectedImage = ({ src, ...props }) => {
+export const ProtectedImage = ({ src, ...props }) => {
// const [imgSrc, setImgSrc] = useState(null)
// useEffect(() => {
@@ -85,11 +68,29 @@ const ProtectedImage = ({ src, ...props }) => {
// }
// }, [src])
- return
+ return (
+
+ )
}
ProtectedImage.propTypes = {
- src: PropTypes.string,
+ src: PropTypes.string.isRequired,
}
-export default ProtectedImage
+export const ProtectedVideo = ({ media, ...props }) => {
+ return (
+
+ )
+}
+
+ProtectedVideo.propTypes = {
+ media: PropTypes.object.isRequired,
+}
diff --git a/ui/src/components/photoGallery/presentView/PresentMedia.js b/ui/src/components/photoGallery/presentView/PresentMedia.js
index 4fa55e20..0001dfc1 100644
--- a/ui/src/components/photoGallery/presentView/PresentMedia.js
+++ b/ui/src/components/photoGallery/presentView/PresentMedia.js
@@ -1,7 +1,7 @@
import React from 'react'
import PropTypes from 'prop-types'
import styled from 'styled-components'
-import ProtectedImage from '../ProtectedImage'
+import { ProtectedImage, ProtectedVideo } from '../ProtectedMedia'
const StyledPhoto = styled(ProtectedImage)`
position: absolute;
@@ -13,7 +13,7 @@ const StyledPhoto = styled(ProtectedImage)`
object-position: center;
`
-const StyledVideo = styled.video`
+const StyledVideo = styled(ProtectedVideo)`
position: absolute;
top: 0;
left: 0;
@@ -39,18 +39,7 @@ const PresentMedia = ({ media, imageLoaded, ...otherProps }) => {
}
if (media.type == 'video') {
- return (
-
-
-
-
-
- )
+ return
}
throw new Error(`Unknown media type '${media.type}'`)
diff --git a/ui/src/components/sidebar/MediaSidebar.js b/ui/src/components/sidebar/MediaSidebar.js
index da44bbeb..5fd1c647 100644
--- a/ui/src/components/sidebar/MediaSidebar.js
+++ b/ui/src/components/sidebar/MediaSidebar.js
@@ -4,7 +4,7 @@ import styled from 'styled-components'
import { useLazyQuery } from 'react-apollo'
import gql from 'graphql-tag'
import SidebarItem from './SidebarItem'
-import ProtectedImage from '../photoGallery/ProtectedImage'
+import { ProtectedImage, ProtectedVideo } from '../photoGallery/ProtectedMedia'
import SidebarShare from './Sharing'
import SidebarDownload from './SidebarDownload'
import { authToken } from '../../authentication'
@@ -73,7 +73,7 @@ const PreviewImage = styled(ProtectedImage)`
object-fit: contain;
`
-const PreviewVideo = styled.video`
+const PreviewVideo = styled(ProtectedVideo)`
position: absolute;
width: 100%;
height: 100%;
@@ -87,16 +87,7 @@ const PreviewMedia = ({ media, previewImage }) => {
}
if (media.type == 'video') {
- return (
-
-
-
- )
+ return
}
throw new Error('Unknown media type')