Skip to content

Commit 268b813

Browse files
authored
Merge pull request #495 from loris-imageserver/auth_rules
ImageInfo/auth_rules updates
2 parents 67eb7ca + 1007834 commit 268b813

File tree

8 files changed

+91
-118
lines changed

8 files changed

+91
-118
lines changed

loris/img_info.py

Lines changed: 16 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -57,50 +57,41 @@ def default(self, obj):
5757

5858
class ImageInfo(JP2Extractor):
5959
'''Info about the image.
60-
See: <http://iiif.io/api/image/>
60+
See: <http://iiif.io/api/image/>, <https://iiif.io/api/image/2.1/#complete-response>
6161
6262
Slots:
6363
width (int)
6464
height (int)
6565
scaleFactors [(int)]
6666
sizes [(str)]: the optimal sizes of the image to request
6767
tiles: [{}]
68-
service (dict): services associated with the image
6968
profile (Profile): Features supported by the server/available for
7069
this image
70+
service [{}]: optional - services associated with the image
71+
attribution [{}]: optional - text that must be shown when content obtained from the Image API service is
72+
displayed or used
73+
license []: optional - link to an external resource that describes the license or rights statement under which
74+
content obtained from the Image API service may be used
75+
logo {}: optional - small image that represents an individual or organization associated with the content
7176
7277
src_img_fp (str): the absolute path on the file system [non IIIF]
7378
src_format (str): the format of the source image file [non IIIF]
7479
color_profile_bytes []: the embedded color profile, if any [non IIIF]
7580
auth_rules (dict): extra information about authorization [non IIIF]
7681
7782
'''
78-
__slots__ = ('scaleFactors', 'width', 'tiles', 'height',
79-
'profile', 'sizes', 'service',
80-
'attribution', 'logo', 'license', 'auth_rules',
81-
'src_format', 'src_img_fp', 'color_profile_bytes')
83+
__slots__ = ('width', 'height', 'scaleFactors', 'sizes', 'tiles',
84+
'profile', 'service', 'attribution', 'license', 'logo',
85+
'src_img_fp', 'src_format', 'color_profile_bytes', 'auth_rules')
8286

83-
def __init__(self, app=None, src_img_fp="", src_format="", extra={}):
87+
def __init__(self, app=None, service=None, attribution=None, license=None, logo=None, src_img_fp="", src_format="", auth_rules=None):
8488
self.src_img_fp = src_img_fp
8589
self.src_format = src_format
86-
self.attribution = None
87-
self.logo = None
88-
self.license = None
89-
self.service = {}
90-
self.auth_rules = extra
91-
92-
# The extraInfo parameter can be used to override specific attributes.
93-
# If there are extra attributes, drop an error.
94-
bad_attrs = []
95-
for (k, v) in extra.get('extraInfo', {}).items():
96-
try:
97-
setattr(self, k, v)
98-
except AttributeError:
99-
bad_attrs.append(k)
100-
if bad_attrs:
101-
raise ImageInfoException(
102-
"Invalid parameters in extraInfo: %s." % ', '.join(bad_attrs)
103-
)
90+
self.attribution = attribution
91+
self.logo = logo
92+
self.license = license
93+
self.service = service or {}
94+
self.auth_rules = auth_rules or {}
10495

10596
# If constructed from JSON, the pixel info will already be processed
10697
if app:

loris/resolver.py

Lines changed: 42 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,44 @@
22
`resolver` -- Resolve Identifiers to Image Paths
33
================================================
44
"""
5+
from contextlib import closing
6+
import glob
7+
import json
58
from logging import getLogger
69
from os.path import join, exists, dirname, split
710
from os import remove
811
from shutil import copy
912
import tempfile
10-
from contextlib import closing
11-
import glob
12-
import json
13-
import os
1413
from urllib.parse import unquote
14+
import warnings
1515

1616
import requests
1717

1818
from loris import constants
1919
from loris.identifiers import CacheNamer, IdentRegexChecker
20-
from loris.loris_exception import ResolverException
20+
from loris.loris_exception import ResolverException, ConfigError
2121
from loris.utils import mkdir_p, safe_rename
2222
from loris.img_info import ImageInfo
2323

2424

2525
logger = getLogger(__name__)
2626

2727

28-
class _AbstractResolver(object):
28+
class _AbstractResolver:
2929

3030
def __init__(self, config):
3131
self.config = config
3232
if config:
33+
# check for previous settings
34+
if "use_extra_info" in self.config and "use_auth_rules" in self.config:
35+
raise ConfigError("You cannot set both use_extra_info and use_auth_rules. Please remove use_extra_info from your config.")
36+
37+
if "use_extra_info" in self.config:
38+
warnings.warn("The use_extra_info field has been renamed to use_auth_rules and will be removed in a future version. Please update your config.", DeprecationWarning)
39+
self.config["use_auth_rules"] = self.config["use_extra_info"]
40+
3341
self.auth_rules_ext = self.config.get('auth_rules_ext', 'rules.json')
34-
self.use_extra_info = self.config.get('use_extra_info', True)
42+
self.use_auth_rules = self.config.get('use_auth_rules', False)
3543

3644
def is_resolvable(self, ident):
3745
"""
@@ -65,12 +73,10 @@ def resolve(self, app, ident, base_uri):
6573
cn = self.__class__.__name__
6674
raise NotImplementedError('resolve() not implemented for %s' % (cn,))
6775

68-
def get_extra_info(self, ident, source_fp):
76+
def get_auth_rules(self, ident, source_fp):
6977
"""
70-
Given the identifier and any resolved source file, find the associated
71-
extra information to include in info.json, plus any additional authorizer
72-
specific information. It might end up there after being copied by a
73-
caching implementation, or live there permanently.
78+
Given the identifier and any resolved source file (ie. on the filesystem), grab the associated
79+
authentication rules from the filesystem (if they exist).
7480
7581
Args:
7682
ident (str):
@@ -80,12 +86,12 @@ def get_extra_info(self, ident, source_fp):
8086
Returns:
8187
dict: The dict of information to embed in info.json
8288
"""
89+
if not self.use_auth_rules:
90+
return {}
8391
xjsfp = source_fp.rsplit('.', 1)[0] + "." + self.auth_rules_ext
8492
if exists(xjsfp):
85-
fh = open(xjsfp)
86-
xjs = json.load(fh)
87-
fh.close()
88-
return xjs
93+
with open(xjsfp) as fh:
94+
return json.load(fh)
8995
else:
9096
return {}
9197

@@ -130,14 +136,13 @@ def is_resolvable(self, ident):
130136
return not self.source_file_path(ident) is None
131137

132138
def resolve(self, app, ident, base_uri):
133-
134139
if not self.is_resolvable(ident):
135140
self.raise_404_for_ident(ident)
136141

137142
source_fp = self.source_file_path(ident)
138143
format_ = self.format_from_ident(ident)
139-
extra = self.get_extra_info(ident, source_fp)
140-
return ImageInfo(app, source_fp, format_, extra)
144+
auth_rules = self.get_auth_rules(ident, source_fp)
145+
return ImageInfo(app=app, src_img_fp=source_fp, src_format=format_, auth_rules=auth_rules)
141146

142147

143148
class ExtensionNormalizingFSResolver(SimpleFSResolver):
@@ -271,7 +276,7 @@ def _web_request_url(self, ident):
271276
return (url, self.request_options())
272277

273278
def cache_dir_path(self, ident):
274-
return os.path.join(
279+
return join(
275280
self.cache_root,
276281
CacheNamer.cache_directory_name(ident=ident),
277282
ident,
@@ -347,19 +352,19 @@ def copy_to_cache(self, ident):
347352
# These files are < 2k in size, so fetch in one go.
348353
# Assumes that the rules will be next to the image
349354
# cache_dir is image specific, so this is easy
350-
351-
bits = split(source_url)
352-
fn = bits[1].rsplit('.', 1)[0] + "." + self.auth_rules_ext
353-
rules_url = bits[0] + '/' + fn
354-
try:
355-
resp = requests.get(rules_url)
356-
if resp.status_code == 200:
357-
local_rules_fp = join(cache_dir, "loris_cache." + self.auth_rules_ext)
358-
if not exists(local_rules_fp):
359-
with open(local_rules_fp, 'w') as fh:
360-
fh.write(resp.text)
361-
except requests.exceptions.RequestException:
362-
pass
355+
if self.use_auth_rules:
356+
bits = split(source_url)
357+
fn = bits[1].rsplit('.', 1)[0] + "." + self.auth_rules_ext
358+
rules_url = bits[0] + '/' + fn
359+
try:
360+
resp = requests.get(rules_url)
361+
if resp.status_code == 200:
362+
local_rules_fp = join(cache_dir, "loris_cache." + self.auth_rules_ext)
363+
if not exists(local_rules_fp):
364+
with open(local_rules_fp, 'w') as fh:
365+
fh.write(resp.text)
366+
except requests.exceptions.RequestException:
367+
pass
363368

364369
return local_fp
365370

@@ -368,11 +373,8 @@ def resolve(self, app, ident, base_uri):
368373
if not cached_file_path:
369374
cached_file_path = self.copy_to_cache(ident)
370375
format_ = self.get_format(cached_file_path, None)
371-
if self.use_extra_info:
372-
extra = self.get_extra_info(ident, cached_file_path)
373-
else:
374-
extra = {}
375-
return ImageInfo(app, cached_file_path, format_, extra)
376+
auth_rules = self.get_auth_rules(ident, cached_file_path)
377+
return ImageInfo(app=app, src_img_fp=cached_file_path, src_format=format_, auth_rules=auth_rules)
376378

377379

378380
class TemplateHTTPResolver(SimpleHTTPResolver):
@@ -553,5 +555,5 @@ def resolve(self, app, ident, base_uri):
553555

554556
cache_fp = self.cache_file_path(ident)
555557
format_ = self.format_from_ident(ident)
556-
extra = self.get_extra_info(ident, cache_fp)
557-
return ImageInfo(app, cache_fp, format_, extra)
558+
auth_rules = self.get_auth_rules(ident, cache_fp)
559+
return ImageInfo(app=app, src_img_fp=cache_fp, src_format=format_, auth_rules=auth_rules)

tests/authorizer_t.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def setUp(self):
4848
fp = "img/test.png"
4949
fmt = "png"
5050
self.authorizer = NullAuthorizer({})
51-
self.info = ImageInfo(None, fp, fmt)
51+
self.info = ImageInfo(app=None, src_img_fp=fp, src_format=fmt)
5252
self.request = MockRequest()
5353

5454
def test_is_protected(self):
@@ -69,7 +69,7 @@ def setUp(self):
6969
fp = "img/test.png"
7070
fmt = "png"
7171
self.authorizer = NooneAuthorizer({})
72-
self.info = ImageInfo(None, fp, fmt)
72+
self.info = ImageInfo(app=None, src_img_fp=fp, src_format=fmt)
7373
self.request = MockRequest()
7474

7575
def test_is_protected(self):
@@ -90,9 +90,9 @@ def setUp(self):
9090
fp = "img/test.png"
9191
fmt = "png"
9292
self.authorizer = SingleDegradingAuthorizer({})
93-
self.badInfo = ImageInfo(None, fp, fmt)
93+
self.badInfo = ImageInfo(app=None, src_img_fp=fp, src_format=fmt)
9494
self.okayIdent = "67352ccc-d1b0-11e1-89ae-279075081939.jp2"
95-
self.okayInfo = ImageInfo(None, "img/%s" % self.okayIdent, "jp2")
95+
self.okayInfo = ImageInfo(app=None, src_img_fp="img/%s" % self.okayIdent, src_format="jp2")
9696
self.request = MockRequest()
9797

9898
def test_is_protected(self):
@@ -120,9 +120,9 @@ def setUp(self):
120120
{"cookie_secret": b"4rakTQJDyhaYgoew802q78pNnsXR7ClvbYtAF1YC87o=",
121121
"token_secret": b"hyQijpEEe9z1OB9NOkHvmSA4lC1B4lu1n80bKNx0Uz0=",
122122
"salt": b"4rakTQJD4lC1B4lu"})
123-
self.badInfo = ImageInfo(None, fp, fmt)
123+
self.badInfo = ImageInfo(app=None, src_img_fp=fp, src_format=fmt)
124124
self.okayIdent = "67352ccc-d1b0-11e1-89ae-279075081939.jp2"
125-
self.okayInfo = ImageInfo(None, "img/%s" % self.okayIdent, "jp2")
125+
self.okayInfo = ImageInfo(app=None, src_img_fp="img/%s" % self.okayIdent, src_format="jp2")
126126

127127
self.origin = "localhost"
128128
# role to get access is "test"

0 commit comments

Comments
 (0)