Skip to content
Snippets Groups Projects
Commit 7362b875 authored by Martin van Es's avatar Martin van Es
Browse files

Add Cache-Control and Last-Modified headers

parent c055c72b
No related branches found
No related tags found
No related merge requests found
...@@ -19,7 +19,7 @@ Reads source metadata file(s) and outputs them signed to filesystem ...@@ -19,7 +19,7 @@ Reads source metadata file(s) and outputs them signed to filesystem
Starts a metadata signer server. Starts a metadata signer server.
Reads source metadata files(s) from mdsigner.yaml configuration, see example. Reads source metadata files(s) from mdsigner.yaml configuration, see example.
Reloads metadata on inotify CLOSE_WRITE of metadata file. Reloads metadata on inotify CLOSE_WRITE of metadata file.
Serves and caches signed by domain signer from memory, on request Serves and caches signed by realm signer from memory, on request
## ```mdproxy.py``` ## ```mdproxy.py```
Reads config from mdproxy.yaml configuration, see example. Reads config from mdproxy.yaml configuration, see example.
...@@ -28,8 +28,8 @@ Caches signed and cached ```mdserver.py``` metadata requests ...@@ -28,8 +28,8 @@ Caches signed and cached ```mdserver.py``` metadata requests
## Queries ## Queries
MDQ Queries can then be pointed at MDQ Queries can then be pointed at
- ```http://mdserver:5001/<domain>/entities/<entityid>``` - ```http://mdserver:5001/<realm>/entities/<entityid>```
- ```http://mdproxy:5002/<domain>/entities/<entityid>``` - ```http://mdproxy:5002/<realm>/entities/<entityid>```
## Bootstrap softHSM2 ## Bootstrap softHSM2
This is a very brief summary of the successive commands to initialize softHSM2 for testing. Tested on Ubuntu 21.10. This is a very brief summary of the successive commands to initialize softHSM2 for testing. Tested on Ubuntu 21.10.
...@@ -47,4 +47,4 @@ This is a very brief summary of the successive commands to initialize softHSM2 f ...@@ -47,4 +47,4 @@ This is a very brief summary of the successive commands to initialize softHSM2 f
# pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -l --slot-index 0 --id a1b2 --label test -y cert -w hsm.der --pin secret # pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -l --slot-index 0 --id a1b2 --label test -y cert -w hsm.der --pin secret
# pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -l --pin secret -O # pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -l --pin secret -O
``` ```
\ No newline at end of file
...@@ -6,6 +6,7 @@ from urllib.parse import unquote ...@@ -6,6 +6,7 @@ from urllib.parse import unquote
from dateutil import parser, tz from dateutil import parser, tz
from datetime import datetime from datetime import datetime
from isoduration import parse_duration from isoduration import parse_duration
from email.utils import formatdate
from utils import read_config, hasher, Entity from utils import read_config, hasher, Entity
...@@ -37,11 +38,22 @@ def serve(domain, eid): ...@@ -37,11 +38,22 @@ def serve(domain, eid):
if entityID in cached[domain]: if entityID in cached[domain]:
if cached[domain][entityID].expires > datetime.now(tz.tzutc()): if cached[domain][entityID].expires > datetime.now(tz.tzutc()):
print(f"cache {entityID}") print(f"cache {entityID}")
return cached[domain][entityID].md max_age = int((cached[domain][entityID].expires -
datetime.now(tz.tzutc())).total_seconds())
last_modified = cached[domain][entityID].last_modified
response.headers['Cache-Control'] = f"max-age={max_age}"
response.headers['Last-Modified'] = last_modified
response.data = cached[domain][entityID].md
return response
print(f"request {entityID}") print(f"request {entityID}")
data = requests.get(f"{config[domain]['signer']}/{domain}" request = requests.get(f"{config[domain]['signer']}/{domain}"
f"/entities/{{sha1}}{entityID}").text f"/entities/{{sha1}}{entityID}")
data = request.text
last_modified = request.headers.get('Last-Modified',
formatdate(timeval=None,
localtime=False,
usegmt=True))
try: try:
root = ET.fromstring(data) root = ET.fromstring(data)
validUntil = root.get('validUntil') validUntil = root.get('validUntil')
...@@ -53,17 +65,25 @@ def serve(domain, eid): ...@@ -53,17 +65,25 @@ def serve(domain, eid):
cached_entity.expires = min(datetime.now(tz.tzutc()) cached_entity.expires = min(datetime.now(tz.tzutc())
+ cached_entity.cache_duration, + cached_entity.cache_duration,
cached_entity.valid_until) cached_entity.valid_until)
if cached_entity.valid_until > datetime.now(tz.tzutc()): cached_entity.last_modified = last_modified
if cached_entity.expires > datetime.now(tz.tzutc()):
cached[domain][entityID] = cached_entity cached[domain][entityID] = cached_entity
max_age = int((cached_entity.expires -
datetime.now(tz.tzutc())).total_seconds())
else: else:
raise KeyError raise KeyError
except Exception: except Exception:
data = "No valid metadata\n" data = "No valid metadata\n"
max_age = 60
response.headers['Content-type'] = "text/html" response.headers['Content-type'] = "text/html"
response.headers['Cache-Control'] = "max-age=60"
response.status = 404 response.status = 404
response.headers['Cache-Control'] = f"max-age={max_age}"
response.headers['Last-Modified'] = last_modified
response.data = data response.data = data
return response return response
if __name__ == "__main__": if __name__ == "__main__":
app.run(host='127.0.0.1', port=5002, debug=False) app.run(host='127.0.0.1', port=5002, debug=False)
#!/usr/bin/env python #!/usr/bin/env python
from utils import read_config, Resource from utils import read_config, Resource
from flask import Flask, Response from flask import Flask, Response
from datetime import datetime
from dateutil import tz
from email.utils import formatdate
from time import mktime
import logging import logging
log = logging.getLogger('werkzeug') log = logging.getLogger('werkzeug')
...@@ -19,12 +23,20 @@ def serve(domain, entity_id): ...@@ -19,12 +23,20 @@ def serve(domain, entity_id):
response.headers['Content-Disposition'] = "filename = \"metadata.xml\"" response.headers['Content-Disposition'] = "filename = \"metadata.xml\""
try: try:
response.data = server[domain][entity_id] data = server[domain][entity_id]
response.data = data.md
max_age = data.max_age
last_modified = data.last_modified
except Exception: except Exception:
response.data = "No valid metadata\n" response.data = "No valid metadata\n"
response.headers['Content-type'] = "text/html" response.headers['Content-type'] = "text/html"
response.status = 404 response.status = 404
max_age = 60
last_modified = datetime.now(tz.tzutc())
response.headers['Cache-Control'] = f"max-age={max_age}"
response.headers['Last-Modified'] = formatdate(timeval=mktime(last_modified.timetuple()),
localtime=False, usegmt=True)
return response return response
......
...@@ -2,7 +2,7 @@ import os ...@@ -2,7 +2,7 @@ import os
from lxml import etree as ET from lxml import etree as ET
from dateutil import parser, tz from dateutil import parser, tz
from isoduration import parse_duration from isoduration import parse_duration
from datetime import datetime from datetime import datetime, timedelta
import hashlib import hashlib
from urllib.parse import unquote from urllib.parse import unquote
import yaml import yaml
...@@ -28,6 +28,14 @@ class Entity: ...@@ -28,6 +28,14 @@ class Entity:
self.md = None self.md = None
self.valid_until = 0 self.valid_until = 0
self.cache_duration = 0 self.cache_duration = 0
self.last_modified = 0
class MData(object):
def __init__(self):
self.md = None
self.max_age = (datetime.now(tz.tzutc()) +
timedelta(seconds=60))
class EventProcessor(pyinotify.ProcessEvent): class EventProcessor(pyinotify.ProcessEvent):
...@@ -99,6 +107,7 @@ class Resource: ...@@ -99,6 +107,7 @@ class Resource:
cacheDuration = root.get('cacheDuration') cacheDuration = root.get('cacheDuration')
valid_until = parser.isoparse(validUntil) valid_until = parser.isoparse(validUntil)
cache_duration = parse_duration(cacheDuration) cache_duration = parse_duration(cacheDuration)
last_modified = datetime.now(tz.tzutc())
if valid_until > datetime.now(tz.tzutc()): if valid_until > datetime.now(tz.tzutc()):
for entity_descriptor in root.findall('md:EntityDescriptor', ns): for entity_descriptor in root.findall('md:EntityDescriptor', ns):
entityID = entity_descriptor.attrib.get('entityID', 'none') entityID = entity_descriptor.attrib.get('entityID', 'none')
...@@ -112,6 +121,7 @@ class Resource: ...@@ -112,6 +121,7 @@ class Resource:
entity.cache_duration = cache_duration entity.cache_duration = cache_duration
entity.expires = min(datetime.now(tz.tzutc()) + cache_duration, entity.expires = min(datetime.now(tz.tzutc()) + cache_duration,
valid_until) valid_until)
entity.last_modified = last_modified
self.idps[sha1] = entity self.idps[sha1] = entity
self.__dict__.pop(sha1, None) self.__dict__.pop(sha1, None)
if sha1 in old_idps: if sha1 in old_idps:
...@@ -136,14 +146,15 @@ class Resource: ...@@ -136,14 +146,15 @@ class Resource:
else: else:
sha1 = hasher(entityID) sha1 = hasher(entityID)
data = None data = MData()
if sha1 in self.__dict__: if sha1 in self.__dict__:
signed_entity = self.__dict__[sha1] signed_entity = self.__dict__[sha1]
if signed_entity.expires > datetime.now(tz.tzutc()): if signed_entity.expires > datetime.now(tz.tzutc()):
print(f"cache {sha1}") print(f"cache {sha1}")
data = self.__dict__[sha1].md data.md = self.__dict__[sha1].md
if data is None and sha1 in self.idps: if data.md is None and sha1 in self.idps:
try: try:
print(f"sign {sha1}") print(f"sign {sha1}")
valid_until = self.idps[sha1].valid_until valid_until = self.idps[sha1].valid_until
...@@ -155,12 +166,16 @@ class Resource: ...@@ -155,12 +166,16 @@ class Resource:
signed_entity.md = signed_xml signed_entity.md = signed_xml
signed_entity.expires = (datetime.now(tz.tzutc()) signed_entity.expires = (datetime.now(tz.tzutc())
+ self.idps[sha1].cache_duration) + self.idps[sha1].cache_duration)
signed_entity.last_modified = self.idps[sha1].last_modified
self.__dict__[sha1] = signed_entity self.__dict__[sha1] = signed_entity
data = signed_xml data.md = signed_xml
else: else:
raise KeyError raise KeyError
except Exception as e: except Exception as e:
print(sha1) print(sha1)
print(f" {e}") print(f" {e}")
data.max_age = int((signed_entity.expires -
datetime.now(tz.tzutc())).total_seconds())
data.last_modified = signed_entity.last_modified
return data return data
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment