diff --git a/endpoints/keyserver/__init__.py b/endpoints/keyserver/__init__.py index 13248fcc7..3a90169ba 100644 --- a/endpoints/keyserver/__init__.py +++ b/endpoints/keyserver/__init__.py @@ -1,18 +1,20 @@ import logging + from datetime import datetime, timedelta from flask import Blueprint, jsonify, abort, request, make_response from jwt import get_unverified_header from app import app -from data.interfaces.key_server import pre_oci_model as model, ServiceKeyDoesNotExist -from data.model.log import log_action +from endpoints.keyserver.models_interface import ServiceKeyDoesNotExist +from endpoints.keyserver.models_pre_oci import pre_oci_model as model from util.security import jwtutil logger = logging.getLogger(__name__) key_server = Blueprint('key_server', __name__) + JWT_HEADER_NAME = 'Authorization' JWT_AUDIENCE = app.config['PREFERRED_URL_SCHEME'] + '://' + app.config['SERVER_HOSTNAME'] @@ -125,7 +127,7 @@ def put_service_key(service, kid): model.create_service_key('', kid, service, jwk, metadata, expiration_date, rotation_duration=rotation_duration) - key_log_metadata = { + model.log_action('service_key_create', request.remote_addr, metadata_dict={ 'kid': kid, 'preshared': False, 'service': service, @@ -133,9 +135,8 @@ def put_service_key(service, kid): 'expiration_date': expiration_date, 'user_agent': request.headers.get('User-Agent'), 'ip': request.remote_addr, - } + }) - log_action('service_key_create', None, metadata=key_log_metadata, ip=request.remote_addr) return make_response('', 202) # Key is going to be rotated. @@ -150,7 +151,7 @@ def put_service_key(service, kid): except ServiceKeyDoesNotExist: abort(404) - key_log_metadata = { + model.log_action('service_key_rotate', request.remote_addr, metadata_dict={ 'kid': kid, 'signer_kid': signer_key.kid, 'service': service, @@ -158,9 +159,8 @@ def put_service_key(service, kid): 'expiration_date': expiration_date, 'user_agent': request.headers.get('User-Agent'), 'ip': request.remote_addr, - } + }) - log_action('service_key_rotate', None, metadata=key_log_metadata, ip=request.remote_addr) return make_response('', 200) @@ -187,16 +187,15 @@ def delete_service_key(service, kid): except ServiceKeyDoesNotExist: abort(404) - key_log_metadata = { + model.log_action('service_key_delete', request.remote_addr, metadata_dict={ 'kid': kid, 'signer_kid': signer_key.kid, 'service': service, 'name': signer_key.name, 'user_agent': request.headers.get('User-Agent'), 'ip': request.remote_addr, - } + }) - log_action('service_key_delete', None, metadata=key_log_metadata, ip=request.remote_addr) return make_response('', 204) abort(403) diff --git a/endpoints/keyserver/models_interface.py b/endpoints/keyserver/models_interface.py new file mode 100644 index 000000000..3b6f566c5 --- /dev/null +++ b/endpoints/keyserver/models_interface.py @@ -0,0 +1,72 @@ +from abc import ABCMeta, abstractmethod +from collections import namedtuple + +from six import add_metaclass + + +class ServiceKey(namedtuple('ServiceKey', ['name', 'kid', 'service', 'jwk', 'metadata', + 'created_date', 'expiration_date', 'rotation_duration', + 'approval'])): + """ + Service Key represents a public key (JWK) being used by an instance of a particular service to + authenticate with other services. + """ + pass + + +class ServiceKeyException(Exception): + pass + + +class ServiceKeyDoesNotExist(ServiceKeyException): + pass + + +@add_metaclass(ABCMeta) +class KeyServerDataInterface(object): + """ + Interface that represents all data store interactions required by a JWT key service. + """ + + @abstractmethod + def list_service_keys(self, service): + """ + Returns a list of service keys or an empty list if the service does not exist. + """ + pass + + @abstractmethod + def get_service_key(self, signer_kid, service=None, alive_only=None, approved_only=None): + """ + Returns a service kid with the given kid or raises ServiceKeyNotFound. + """ + pass + + @abstractmethod + def create_service_key(self, name, kid, service, jwk, metadata, expiration_date, + rotation_duration=None): + """ + Stores a service key. + """ + pass + + @abstractmethod + def replace_service_key(self, old_kid, kid, jwk, metadata, expiration_date): + """ + Replaces a service with a new key or raises ServiceKeyNotFound. + """ + pass + + @abstractmethod + def delete_service_key(self, kid): + """ + Deletes and returns a service key with the given kid or raises ServiceKeyNotFound. + """ + pass + + @abstractmethod + def log_action(self, action_name, ip, metadata_dict=None): + """ + Records a particular serivce key interaction to an audit log. + """ + pass diff --git a/data/interfaces/key_server.py b/endpoints/keyserver/models_pre_oci.py similarity index 51% rename from data/interfaces/key_server.py rename to endpoints/keyserver/models_pre_oci.py index b9b6d324b..8bcb65986 100644 --- a/data/interfaces/key_server.py +++ b/endpoints/keyserver/models_pre_oci.py @@ -1,71 +1,7 @@ -from abc import ABCMeta, abstractmethod -from collections import namedtuple - -from six import add_metaclass - import data.model - -class ServiceKey(namedtuple('ServiceKey', ['name', 'kid', 'service', 'jwk', 'metadata', - 'created_date', 'expiration_date', 'rotation_duration', - 'approval'])): - """ - Service Key represents a public key (JWK) being used by an instance of a particular service to - authenticate with other services. - """ - pass - - -class ServiceKeyException(Exception): - pass - - -class ServiceKeyDoesNotExist(ServiceKeyException): - pass - - -# TODO(jzelinskie): maybe make this interface support superuser API -@add_metaclass(ABCMeta) -class KeyServerDataInterface(object): - """ - Interface that represents all data store interactions required by a JWT key service. - """ - - @abstractmethod - def list_service_keys(self, service): - """ - Returns a list of service keys or an empty list if the service does not exist. - """ - pass - - @abstractmethod - def get_service_key(self, signer_kid, service=None, alive_only=None, approved_only=None): - """ - Returns a service kid with the given kid or raises ServiceKeyNotFound. - """ - pass - - @abstractmethod - def create_service_key(self, name, kid, service, jwk, metadata, expiration_date, - rotation_duration=None): - """ - Stores a service key. - """ - pass - - @abstractmethod - def replace_service_key(self, old_kid, kid, jwk, metadata, expiration_date): - """ - Replaces a service with a new key or raises ServiceKeyNotFound. - """ - pass - - @abstractmethod - def delete_service_key(self, kid): - """ - Deletes and returns a service key with the given kid or raises ServiceKeyNotFound. - """ - pass +from endpoints.keyserver.models_interface import (KeyServerDataInterface, ServiceKey, + ServiceKeyDoesNotExist) class PreOCIModel(KeyServerDataInterface): @@ -102,6 +38,10 @@ class PreOCIModel(KeyServerDataInterface): except data.model.ServiceKeyDoesNotExist: raise ServiceKeyDoesNotExist() + def log_action(self, action_name, ip, metadata_dict=None): + metadata_dict = {} if metadata_dict is None else metadata_dict + data.model.log.log_action(action_name, None, metadata=metadata_dict, ip=ip) + pre_oci_model = PreOCIModel() @@ -121,4 +61,3 @@ def _db_key_to_servicekey(key): rotation_duration=key.rotation_duration, approval=key.approval, ) -