2019-12-03 23:56:59 +03:00
|
|
|
# A "fake" JWK server. Which returns `Cache-Control` and `Expires` headers in its response
|
|
|
|
# This is useful for testing our `jwk_url` behaviour
|
|
|
|
|
|
|
|
import datetime
|
|
|
|
from http import HTTPStatus
|
2022-09-28 12:19:47 +03:00
|
|
|
import http.server
|
|
|
|
import requests
|
2019-12-03 23:56:59 +03:00
|
|
|
|
2022-09-28 12:19:47 +03:00
|
|
|
from webserver import MkHandlers, RequestHandler, Response
|
2019-12-03 23:56:59 +03:00
|
|
|
|
|
|
|
def mkJSONResp(json_result):
|
|
|
|
return Response(HTTPStatus.OK, json_result, {'Content-Type': 'application/json'})
|
|
|
|
|
2022-01-28 03:17:53 +03:00
|
|
|
# fetch a valid JWK from google servers - this seemed easier than
|
2022-09-28 12:19:47 +03:00
|
|
|
# generating key pairs and then constructing a JWK JSON response
|
2022-01-28 03:17:53 +03:00
|
|
|
jwk_url = 'https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com'
|
2019-12-03 23:56:59 +03:00
|
|
|
state = {
|
|
|
|
'cache-control': 0,
|
|
|
|
'expires': 0
|
|
|
|
}
|
|
|
|
|
|
|
|
class JwkExpiresHandler(RequestHandler):
|
|
|
|
def post(self, request):
|
|
|
|
return Response(HTTPStatus.METHOD_NOT_ALLOWED)
|
|
|
|
|
|
|
|
def get(self, request):
|
2022-01-28 03:17:53 +03:00
|
|
|
jwk_resp = requests.get(jwk_url)
|
2019-12-03 23:56:59 +03:00
|
|
|
res = jwk_resp.json()
|
2022-09-28 12:19:47 +03:00
|
|
|
|
2019-12-03 23:56:59 +03:00
|
|
|
resp = mkJSONResp(res)
|
|
|
|
if request.qs and 'error' in request.qs and 'true' in request.qs['error']:
|
|
|
|
resp.headers['Expires'] = 'invalid-value'
|
|
|
|
else:
|
2022-01-28 03:17:53 +03:00
|
|
|
if request.qs and 'seconds' in request.qs and len(request.qs['seconds']) > 0:
|
|
|
|
expires_in_secs = int(request.qs['seconds'][0])
|
|
|
|
else:
|
|
|
|
expires_in_secs = 3
|
|
|
|
expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=expires_in_secs)
|
2019-12-03 23:56:59 +03:00
|
|
|
resp.headers['Expires'] = datetime.datetime.strftime(expiry, "%a, %d %b %Y %T GMT")
|
2022-09-28 12:19:47 +03:00
|
|
|
|
2019-12-03 23:56:59 +03:00
|
|
|
state['expires'] += 1
|
|
|
|
return resp
|
|
|
|
|
|
|
|
class JwkCacheControlHandler(RequestHandler):
|
|
|
|
def post(self, request):
|
|
|
|
return Response(HTTPStatus.METHOD_NOT_ALLOWED)
|
|
|
|
|
|
|
|
def get(self, request):
|
2022-01-28 03:17:53 +03:00
|
|
|
jwk_resp = requests.get(jwk_url)
|
2019-12-03 23:56:59 +03:00
|
|
|
res = jwk_resp.json()
|
2022-01-28 03:17:53 +03:00
|
|
|
header_vals = []
|
2019-12-03 23:56:59 +03:00
|
|
|
if request.qs:
|
2022-01-28 03:17:53 +03:00
|
|
|
for param in request.qs:
|
|
|
|
if len(request.qs[param]) > 0:
|
|
|
|
val = request.qs[param][0]
|
|
|
|
if val == 'true':
|
|
|
|
header_vals.append(param)
|
|
|
|
elif val.isnumeric():
|
|
|
|
header_vals.append(param + "=" + val)
|
2022-09-28 12:19:47 +03:00
|
|
|
|
2019-12-03 23:56:59 +03:00
|
|
|
resp = mkJSONResp(res)
|
2022-01-28 03:17:53 +03:00
|
|
|
resp.headers['Cache-Control'] = ", ".join(header_vals)
|
2019-12-03 23:56:59 +03:00
|
|
|
# HGE should always prefer Cache-Control over Expires header
|
|
|
|
expiry = datetime.datetime.utcnow() + datetime.timedelta(seconds=600)
|
|
|
|
resp.headers['Expires'] = datetime.datetime.strftime(expiry, "%a, %d %b %Y %T GMT")
|
|
|
|
state['cache-control'] += 1
|
|
|
|
return resp
|
|
|
|
|
|
|
|
class StateHandler(RequestHandler):
|
|
|
|
def post(self, request):
|
|
|
|
return Response(HTTPStatus.METHOD_NOT_ALLOWED)
|
|
|
|
|
|
|
|
def get(self, request):
|
|
|
|
resp = mkJSONResp(state)
|
|
|
|
return resp
|
|
|
|
|
2022-01-28 03:17:53 +03:00
|
|
|
class ResetStateHandler(RequestHandler):
|
|
|
|
def post(self, request):
|
|
|
|
state['cache-control'] = 0
|
|
|
|
state['expires'] = 0
|
|
|
|
return Response(HTTPStatus.OK)
|
2022-09-28 12:19:47 +03:00
|
|
|
|
2022-01-28 03:17:53 +03:00
|
|
|
def get(self, request):
|
|
|
|
return Response(HTTPStatus.METHOD_NOT_ALLOWED)
|
|
|
|
|
2019-12-03 23:56:59 +03:00
|
|
|
handlers = MkHandlers({
|
|
|
|
# sending query string: `?field=smaxage`, will return Cache-Control with s-maxage, else with max-age
|
|
|
|
# sending query string: `error=true` will respond with invalid header value
|
|
|
|
'/jwk-cache-control': JwkCacheControlHandler,
|
|
|
|
# sending query string: `error=true` will respond with invalid header value
|
|
|
|
'/jwk-expires': JwkExpiresHandler,
|
|
|
|
# API so that testing can be done
|
2022-01-28 03:17:53 +03:00
|
|
|
'/state': StateHandler,
|
|
|
|
# Resets the state back to zeros
|
|
|
|
'/reset-state': ResetStateHandler
|
2019-12-03 23:56:59 +03:00
|
|
|
})
|
|
|
|
|
2022-10-27 14:47:48 +03:00
|
|
|
def create_server(server_address):
|
|
|
|
return http.server.HTTPServer(server_address, handlers)
|
2019-12-03 23:56:59 +03:00
|
|
|
|
|
|
|
def stop_server(server):
|
|
|
|
server.shutdown()
|
|
|
|
server.server_close()
|
|
|
|
|
|
|
|
# if you want to run this module to emulate a JWK server during development
|
|
|
|
if __name__ == '__main__':
|
2022-10-27 14:47:48 +03:00
|
|
|
s = create_server(('localhost', 5001))
|
2019-12-03 23:56:59 +03:00
|
|
|
s.serve_forever()
|
|
|
|
stop_server(s)
|