Package: cookie-manager
Cookie-ManagerSigned cookie manager for communication between multiple trusted services.Signs, verifies, and manages multiple cookies from trusted environments. Designed for use by services all within the same secure network (AWS VPC etc).Wrapsitsdangerousfor the signing and verification (but this could change in the future).Specifically, this handles:Managing multiple different cookies - one for every environment or paired serviceError correction around sign/verify commandsThis package is designed to sign and verify cookies - either ingoing or outgoing. These cookies are not encrypted,
so stick to benign data, and only transmit within a trusted environment such as an AWS VPC.InstallationInstall and update usingpip:pipinstall-UCookie-ManagerUsageImport:fromcookie_manager.cookie_managerimportCookieManagerCookie-Manager is designed to use multiple different signing/verifying keys -- one (or more) per environment.
Configure your keys in a dict:keys={"key1":"SECRET","key2":"SECRET2"}Create an instance (and seed it with your keys):cookie_manager=CookieManager(keys=keys)SigningTo sign a cookie, start with a dict payload containing your data:payload={"key":"value"}Then sign the payload, making sure to pass a validkey_idas previously configured. Thesignmethod will
retrieve your signing keySECRETto sign requests (based on thekey_idyou pass in). This WILL override any
existing key with the namekey_id.signed_cookie=cookie_manager.sign(cookie=payload,key_id="key1")This will return you a signed cookie (with an additionalkey_idpair added in):'{"key": "value", "key_id": "key1"}.XepkCA.CUZtVTCXHbqoalWVCh5xOa4S4WE'VerifyingWhen reading in a signed cookie, verification happens through the cookie payload -> whatever comes in needs to have akey_idin the payload, which is used to lookup the verification key (configured during instantiation). This is added
for you bysign:incoming_signed_cookie='{"key": "value", "key_id", "key1"}.XepkCA.CUZtVTCXHbqoalWVCh5xOa4S4WE'Verify this cookie (during which Cookie-Manager will extractkey_idfrom the payload, and lookup the key used to sign the cookie):payload=cookie_manager.verify(signed_cookie=signed_cookie)Now, you can access data inside thepayloadobject. Theverifyfunction will raise errors if it cannot verify.ConfigurationYou can pass an optional config dictionary into the constructor to override existing options.cookie_manager=CookieManager(keys=keys,config={"VERIFY_MAX_COOKIE_AGE":10})This example will override the max age of a cookie that is allowed, when verifying.Custom LoggingThis package uses dependency injection to log errors with Python'sprint. To use your own logger, pass in a
logger object which implementscritical,error,warning,debug, andinfofunctions. Here's how to patch
in the Flask logger, but any object will work providing it meets the Duck Typing rules:cookie_manager=CookieManager(keys=keys,logger=app.logger)This will result in logging calls firing toapp.logger.<logger-level>with a string passed in.Custom ExceptionsLike logging, this package uses custom error handling if you need it. By default, all errors will raise as
"Exception", but you can pass in a custom object to raise specific errors.This class will raiseUnauthorized,ServiceUnavailable, andBadRequest.Here's how to pass in aWerkzeugexception object:fromwerkzeugimportexceptionscookie_manager=CookieManager(keys=keys,exceptions=exceptions)Security DecoratorsIf using this package inflask, you can decorate routes to only allow access to certain cookies.There are 2 ways of protecting a route, allow any signed cookie or allow cookies signed with specific keys.To make use of the decorators, you will need to create a cookie manager that has all the keys you want to use for
protecting routes and create an instance of theCookieSecurityDecorator.Decorator instance (e.g. util.py)fromcookie_managerimportCookieSecurityDecoratorcookie_security=CookieSecurityDecorator()fromcookie_managerimportCookieManagerfromproject.utilimportcookie_securityfromflaskimportrequestcookie_manager=CookieManager(keys={"key_1":"","key_2":"","key_3":""},# These are the keys that will be used to protect all routesexceptions=exceptions,)cookie_security.init_app(request=request,cookie_manager=cookie_manager,cookie_name="cookie_name")The string supplied forcookie_nameis the name of the cookie in the request to use for protecting the routes.Now you are able to use the decorator as detailed below.Option 1- Allow access to any signed cookieLets say we want to have a route that can be accessed by any cookie that has been signed using one of the keys
supplied to the cookie manager used to create the decorator. If we decorate the route like the following example,
only signed cookies will be allowed to access this route.fromproject.utilimportcookie_security@cookie_security.keys_required()defmy_route():#...Option 2- Allow access to specific signed cookiesLets say we want to have a route that can only be accessed by a cookie that has been signed using a subset of keys supplied to
the cookie manager used to create the decorator. If we decorate the route like the following example, only cookies signed with
a provided key will be allowed to access this route.fromproject.utilimportcookie_security@cookie_security.keys_required(["key_1","key_2"])defmy_route():#...DevelopingThe build pipeline require your tests to pass and code to be formattedMake sure you have Python 3.x installed on your machine (usepyenv).Install the dependencies withpipenv(making sure to include dev and pre-release packages):pipenvinstall--dev--preConfigure your environment:pipenvshell&&exportPYTHONPATH="$PWD"Run the tests:pytestOr with logging:pytest-sOr tests with coverage:pytest--cov=./Format the code withBlack:black$PWDReleasesCleanup the (.gitignored)distfolder (if you have one):rm-rfdistNotch up the version number insetup.pyand build:python3setup.pysdistbdist_wheelPush to PyPi (using the ScholarPack credentials when prompted)python3-mtwineupload--repository-urlhttps://upload.pypi.org/legacy/dist/*LinksReleases:https://pypi.org/project/cookie-manager/Code:https://github.com/ScholarPack/cookie-manager/