mirror of
https://git.vectorsigma.ru/public/photoview.git
synced 2026-08-03 21:19:18 +00:00
Prepare back-end for token password
This commit is contained in:
@@ -60,10 +60,15 @@ func (r *shareTokenResolver) HasPassword(ctx context.Context, obj *models.ShareT
|
||||
return hasPassword, nil
|
||||
}
|
||||
|
||||
func (r *queryResolver) ShareToken(ctx context.Context, token string, password *string) (*models.ShareToken, error) {
|
||||
func (r *queryResolver) ShareToken(ctx context.Context, tokenValue string, password *string) (*models.ShareToken, error) {
|
||||
|
||||
row := r.Database.QueryRow("SELECT * FROM share_token WHERE value = ? AND (password = ? OR password IS NULL)", token, password)
|
||||
result, err := models.NewShareTokenFromRow(row)
|
||||
hashed_password, err := hashSharePassword(password)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Failed to hash password")
|
||||
}
|
||||
|
||||
row := r.Database.QueryRow("SELECT * FROM share_token WHERE value = ?", tokenValue)
|
||||
token, err := models.NewShareTokenFromRow(row)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, errors.New("share not found")
|
||||
@@ -72,7 +77,11 @@ func (r *queryResolver) ShareToken(ctx context.Context, token string, password *
|
||||
}
|
||||
}
|
||||
|
||||
return result, nil
|
||||
if token.Password != nil && hashed_password != token.Password {
|
||||
return nil, errors.New("unauthorized")
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (r *mutationResolver) ShareAlbum(ctx context.Context, albumID int, expire *time.Time, password *string) (*models.ShareToken, error) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
|
||||
"github.com/viktorstrate/photoview/api/graphql/auth"
|
||||
"github.com/viktorstrate/photoview/api/graphql/models"
|
||||
@@ -60,7 +61,7 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
|
||||
// Check if photo is authorized with a share token
|
||||
token := r.URL.Query().Get("token")
|
||||
if token == "" {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
@@ -78,6 +79,23 @@ func RegisterPhotoRoutes(db *sql.DB, router *mux.Router) {
|
||||
return
|
||||
}
|
||||
|
||||
// Validate share token password, if set
|
||||
if shareToken.Password != nil {
|
||||
tokenPassword := r.Header.Get("TokenPassword")
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(*shareToken.Password), []byte(tokenPassword)); err != nil {
|
||||
if err == bcrypt.ErrMismatchedHashAndPassword {
|
||||
w.WriteHeader(http.StatusForbidden)
|
||||
w.Write([]byte("unauthorized"))
|
||||
return
|
||||
} else {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
w.Write([]byte("internal server error"))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if shareToken.AlbumID != nil && photo.AlbumId != *shareToken.AlbumID {
|
||||
// Check child albums
|
||||
row := db.QueryRow(`
|
||||
|
||||
@@ -3,13 +3,18 @@ import PropTypes from 'prop-types'
|
||||
|
||||
let imageCache = {}
|
||||
|
||||
export async function fetchProtectedImage(src, { signal } = { signal: null }) {
|
||||
export async function fetchProtectedImage(
|
||||
src,
|
||||
{ signal, headers: customHeaders } = { signal: null, headers: null }
|
||||
) {
|
||||
if (src) {
|
||||
if (imageCache[src]) {
|
||||
return imageCache[src]
|
||||
}
|
||||
|
||||
let headers = {}
|
||||
let headers = {
|
||||
...customHeaders,
|
||||
}
|
||||
if (localStorage.getItem('token')) {
|
||||
headers['Authorization'] = `Bearer ${localStorage.getItem('token')}`
|
||||
}
|
||||
@@ -44,16 +49,27 @@ const ProtectedImage = ({ src, ...props }) => {
|
||||
setImgSrc('')
|
||||
|
||||
const imgUrl = new URL(src)
|
||||
const fetchHeaders = {}
|
||||
|
||||
if (localStorage.getItem('token') == null) {
|
||||
// Get share token if not authorized
|
||||
|
||||
const token = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
|
||||
if (token) {
|
||||
imgUrl.searchParams.set('token', token[1])
|
||||
const tokenRegex = location.pathname.match(/^\/share\/([\d\w]+)(\/?.*)$/)
|
||||
if (tokenRegex) {
|
||||
const token = tokenRegex[1]
|
||||
imgUrl.searchParams.set('token', token)
|
||||
|
||||
const tokenPassword = sessionStorage.getItem(`share-token-pw-${token}`)
|
||||
if (tokenPassword) {
|
||||
fetchHeaders['TokenPassword'] = tokenPassword
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fetchProtectedImage(imgUrl.href, { signal: fetchController.signal })
|
||||
fetchProtectedImage(imgUrl.href, {
|
||||
signal: fetchController.signal,
|
||||
headers: fetchHeaders,
|
||||
})
|
||||
.then(newSrc => {
|
||||
if (!canceled) {
|
||||
setImgSrc(newSrc)
|
||||
|
||||
Reference in New Issue
Block a user