Index: appengine/monorail/services/secrets_svc.py |
diff --git a/appengine/monorail/services/secrets_svc.py b/appengine/monorail/services/secrets_svc.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..cfb5df291627c63514f510aec11ec8fac601f5a0 |
--- /dev/null |
+++ b/appengine/monorail/services/secrets_svc.py |
@@ -0,0 +1,110 @@ |
+# Copyright 2016 The Chromium Authors. All rights reserved. |
+# Use of this source code is govered by a BSD-style |
+# license that can be found in the LICENSE file or at |
+# https://developers.google.com/open-source/licenses/bsd |
+ |
+"""A set of functions that provide persistence for secret keys. |
+ |
+These keys are used in generating XSRF tokens, calling the CAPTCHA API, |
+and validating that inbound emails are replies to notifications that |
+we sent. |
+ |
+Unlike other data stored in Monorail, this is kept in the GAE |
+datastore rather than SQL because (1) it never needs to be used in |
+combination with other SQL data, and (2) we may want to replicate |
+issue content for various off-line reporting functionality, but we |
+will never want to do that with these keys. A copy is also kept in |
+memcache for faster access. |
+ |
+When no secrets are found, a new Secrets entity is created and initialized |
+with randomly generated values for XSRF and email keys. |
+ |
+If these secret values ever need to change: |
+(1) Make the change on the Google Cloud Console in the Cloud Datastore tab. |
+(2) Flush memcache. |
+""" |
+ |
+import logging |
+ |
+from google.appengine.api import memcache |
+from google.appengine.ext import ndb |
+ |
+import settings |
+from framework import framework_helpers |
+ |
+ |
+GLOBAL_KEY = 'secrets_singleton_key' |
+ |
+ |
+class Secrets(ndb.Model): |
+ """Model for representing secret keys.""" |
+ # Keys we use to generate tokens. |
+ xsrf_key = ndb.StringProperty(required=True) |
+ email_key = ndb.StringProperty(required=True) |
+ |
+ # Keys for other APIs that we use. |
+ recaptcha_public_key = ndb.StringProperty() |
+ recaptcha_private_key = ndb.StringProperty() |
+ |
+ |
+def MakeSecrets(): |
+ """Make a new Secrets model with random values for keys.""" |
+ secrets = Secrets(id=GLOBAL_KEY) |
+ secrets.xsrf_key = framework_helpers.MakeRandomKey() |
+ secrets.email_key = framework_helpers.MakeRandomKey() |
+ # Note that recaptcha keys are not generated. An admin |
+ # will need to set them via the Google Cloud Console. |
+ return secrets |
+ |
+ |
+def GetSecrets(): |
+ """Get secret keys from memcache or datastore. Or, make new ones.""" |
+ secrets = memcache.get(GLOBAL_KEY) |
+ if secrets: |
+ return secrets |
+ |
+ secrets = Secrets.get_by_id(GLOBAL_KEY) |
+ if not secrets: |
+ secrets = MakeSecrets() |
+ secrets.put() |
+ |
+ memcache.set(GLOBAL_KEY, secrets) |
+ return secrets |
+ |
+ |
+def GetXSRFKey(): |
+ """Return a secret key string used to generate XSRF tokens.""" |
+ return GetSecrets().xsrf_key |
+ |
+ |
+def GetEmailKey(): |
+ """Return a secret key string used to generate email tokens.""" |
+ return GetSecrets().email_key |
+ |
+ |
+def GetRecaptchaPublicKey(): |
+ """Return our public API key for reCAPTCHA.""" |
+ if settings.dev_mode: |
+ return '6LebzNMSAAAAAMY8b_FaZvp8wymUO5Jsa0pIX7HO' |
+ |
+ result = GetSecrets().recaptcha_public_key |
+ if not result: |
+ logging.warn('No recaptcha_public_key set. Get one at recaptcha.net.') |
+ logging.warn('Store it in Cloud Datastore via the Google Cloud Console.') |
+ |
+ return result |
+ |
+ |
+def GetRecaptchaPrivateKey(): |
+ """Return our private API key for reCAPTCHA.""" |
+ if settings.dev_mode: |
+ return '6LebzNMSAAAAAHNVNiP2I7aNMv2AmxY5nReE2LZ4' |
+ |
+ result = GetSecrets().recaptcha_private_key |
+ if not result: |
+ logging.warn('No recaptcha_private_key set. Get one at recaptcha.net.') |
+ logging.warn('Store it in Cloud Datastore via the Google Cloud Console.') |
+ |
+ return result |
+ |
+ |