Backend: Sanitize metadata titles and descriptions

Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
Michael Mayer 2020-06-27 14:15:25 +02:00
parent e7fecd3b27
commit 2b2cd8ed23
10 changed files with 108 additions and 14 deletions

View file

@ -109,6 +109,9 @@ acceptance:
acceptance-firefox:
$(info Running JS acceptance tests in Firefox...)
(cd frontend && npm run acceptance-firefox && cd ..)
reset-photoprism-db:
$(info Purging photoprism database...)
mysql < scripts/reset-photoprism-db.sql
reset-test-db:
$(info Purging test databases...)
mysql < scripts/reset-test-db.sql

View file

@ -3,6 +3,7 @@ package entity
import (
"time"
"github.com/photoprism/photoprism/pkg/txt"
"golang.org/x/crypto/bcrypt"
)
@ -17,14 +18,14 @@ type Password struct {
// NewPassword creates a new password instance.
func NewPassword(uid, password string) Password {
if uid == "" {
panic("password: uid must not be empty")
panic("auth: can't set password without uid")
}
m := Password{UID: uid}
if password != "" {
if err := m.SetPassword(password); err != nil {
log.Errorf("password: %s (set password)", err)
log.Errorf("auth: failed setting password for %s", uid)
}
}
@ -68,7 +69,7 @@ func FindPassword(uid string) *Password {
if err := Db().Where("uid = ?", uid).First(&result).Error; err == nil {
return &result
} else {
log.Errorf("password: %s (not found)", err)
log.Errorf("auth: no password for %s", txt.Quote(uid))
}
return nil

View file

@ -7,6 +7,7 @@ import (
"github.com/jinzhu/gorm"
"github.com/photoprism/photoprism/internal/acl"
"github.com/photoprism/photoprism/pkg/rnd"
"github.com/photoprism/photoprism/pkg/txt"
)
type People []Person
@ -133,7 +134,7 @@ func FindPersonByUserName(userName string) *Person {
if err := Db().Where("user_name = ?", userName).First(&result).Error; err == nil {
return &result
} else {
log.Errorf("user: %s", err)
log.Errorf("auth: user %s not found", txt.Quote(userName))
return nil
}
}
@ -149,7 +150,7 @@ func FindPersonByUID(uid string) *Person {
if err := Db().Where("person_uid = ?", uid).First(&result).Error; err == nil {
return &result
} else {
log.Errorf("user: %s", err)
log.Errorf("auth: user %s not found", txt.Quote(uid))
return nil
}
}

View file

@ -15,7 +15,7 @@ type Data struct {
Duration time.Duration `meta:"Duration,MediaDuration,TrackDuration"`
Codec string `meta:"CompressorID,Compression,FileType"`
Title string `meta:"Title"`
Subject string `meta:"Subject,PersonInImage"`
Subject string `meta:"Subject,PersonInImage,ObjectName"`
Keywords string `meta:"Keywords"`
Comment string `meta:"-"`
Artist string `meta:"Artist,Creator"`

View file

@ -342,7 +342,7 @@ func (data *Data) Exif(fileName string) (err error) {
}
if value, ok := tags["ImageDescription"]; ok {
data.Description = SanitizeString(value)
data.Description = SanitizeDescription(value)
}
data.All = tags

View file

@ -187,5 +187,8 @@ func (data *Data) JSON(jsonName, originalName string) (err error) {
data.InstanceID = SanitizeUID(data.InstanceID)
}
data.Title = SanitizeTitle(data.Title)
data.Description = SanitizeDescription(data.Description)
return nil
}

View file

@ -1,9 +1,16 @@
package meta
import (
"regexp"
"strings"
)
var DscTitleRegexp = regexp.MustCompile("\\D{3}[\\d_]\\d{4}(.JPG)?")
var UnwantedDescriptions = map[string]bool{
"OLYMPUS DIGITAL CAMERA": true,
}
// SanitizeString removes unwanted character from an exif value string.
func SanitizeString(value string) string {
value = strings.TrimSpace(value)
@ -25,3 +32,25 @@ func SanitizeUID(value string) string {
return strings.ToLower(value)
}
// SanitizeTitle normalizes titles and removes unwanted information.
func SanitizeTitle(value string) string {
value = SanitizeString(value)
if dsc := DscTitleRegexp.FindString(value); dsc == value {
value = ""
}
return value
}
// SanitizeDescription normalizes descriptions and removes unwanted information.
func SanitizeDescription(value string) string {
value = SanitizeString(value)
if remove := UnwantedDescriptions[value]; remove {
value = ""
}
return value
}

View file

@ -0,0 +1,55 @@
package meta
import "testing"
func TestSanitizeTitle(t *testing.T) {
t.Run("IMG_0599", func(t *testing.T) {
result := SanitizeTitle("IMG_0599")
if result != "" {
t.Fatal("result should be empty")
}
})
t.Run("IMG_0599.JPG", func(t *testing.T) {
result := SanitizeTitle("IMG_0599.JPG")
if result != "" {
t.Fatal("result should be empty")
}
})
t.Run("IMG_0599 ABC", func(t *testing.T) {
result := SanitizeTitle("IMG_0599 ABC")
if result != "IMG_0599 ABC" {
t.Fatal("result should be IMG_0599 ABC")
}
})
t.Run("DSC10599", func(t *testing.T) {
result := SanitizeTitle("DSC10599")
if result != "" {
t.Fatal("result should be empty")
}
})
}
func TestSanitizeDescription(t *testing.T) {
t.Run("IMG_0599", func(t *testing.T) {
result := SanitizeDescription("IMG_0599")
if result == "" {
t.Fatal("result should not be empty")
}
})
t.Run("OLYMPUS DIGITAL CAMERA", func(t *testing.T) {
result := SanitizeDescription("OLYMPUS DIGITAL CAMERA")
if result != "" {
t.Fatal("result should be empty")
}
})
}

View file

@ -203,29 +203,29 @@ func (doc *XmpDocument) Load(filename string) error {
}
func (doc *XmpDocument) Title() string {
return doc.RDF.Description.Title.Alt.Li.Text
return SanitizeTitle(doc.RDF.Description.Title.Alt.Li.Text)
}
func (doc *XmpDocument) Artist() string {
return doc.RDF.Description.Creator.Seq.Li
return SanitizeString(doc.RDF.Description.Creator.Seq.Li)
}
func (doc *XmpDocument) Description() string {
return doc.RDF.Description.Description.Alt.Li.Text
return SanitizeDescription(doc.RDF.Description.Description.Alt.Li.Text)
}
func (doc *XmpDocument) Copyright() string {
return doc.RDF.Description.Rights.Alt.Li.Text
return SanitizeString(doc.RDF.Description.Rights.Alt.Li.Text)
}
func (doc *XmpDocument) CameraMake() string {
return doc.RDF.Description.Make
return SanitizeString(doc.RDF.Description.Make)
}
func (doc *XmpDocument) CameraModel() string {
return doc.RDF.Description.Model
return SanitizeString(doc.RDF.Description.Model)
}
func (doc *XmpDocument) LensModel() string {
return doc.RDF.Description.LensModel
return SanitizeString(doc.RDF.Description.LensModel)
}

View file

@ -0,0 +1,2 @@
DROP DATABASE IF EXISTS photoprism;
CREATE DATABASE IF NOT EXISTS photoprism;