From 5ac2c4970ae2e2bceee09c290ffc8e8fe8e6a55f Mon Sep 17 00:00:00 2001
From: Joseph Schorr <joseph.schorr@coreos.com>
Date: Thu, 8 Jan 2015 13:56:17 -0500
Subject: [PATCH] Add Google auth validation and fix the case where no config
 is specified at all for Google auth or Github auth

---
 static/js/core-config-setup.js |  4 ++++
 util/config/validator.py       | 30 +++++++++++++++++++++++++++---
 util/oauth.py                  | 22 ++++++++++++++++++----
 3 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/static/js/core-config-setup.js b/static/js/core-config-setup.js
index 6950ebe68..78483ab5d 100644
--- a/static/js/core-config-setup.js
+++ b/static/js/core-config-setup.js
@@ -31,6 +31,10 @@ angular.module("core-config-setup", ['angularFileUpload'])
             return config.FEATURE_GITHUB_LOGIN;
           }},
 
+          {'id': 'google-login', 'title': 'Google Authentication', 'condition': function(config) {
+            return config.FEATURE_GOOGLE_LOGIN;
+          }},
+
           {'id': 'github-trigger', 'title': 'Github (Enterprise) Build Triggers', 'condition': function(config) {
             return config.FEATURE_GITHUB_BUILD;
           }}
diff --git a/util/config/validator.py b/util/config/validator.py
index 7be8657df..990c69c36 100644
--- a/util/config/validator.py
+++ b/util/config/validator.py
@@ -79,17 +79,21 @@ def _validate_github(config_key):
 
 def _validate_github_with_key(config_key, config):
   """ Validates the OAuth credentials and API endpoint for a Github service. """
-  endpoint = config[config_key].get('GITHUB_ENDPOINT')
+  github_config = config.get(config_key)
+  if not github_config:
+    raise Exception('Missing Github client id and client secret')
+
+  endpoint = github_config.get('GITHUB_ENDPOINT')
   if not endpoint:
     raise Exception('Missing Github Endpoint')
 
   if endpoint.find('http://') != 0 and endpoint.find('https://') != 0:
     raise Exception('Github Endpoint must start with http:// or https://')
 
-  if not config[config_key].get('CLIENT_ID'):
+  if not github_config.get('CLIENT_ID'):
     raise Exception('Missing Client ID')
 
-  if not config[config_key].get('CLIENT_SECRET'):
+  if not github_config.get('CLIENT_SECRET'):
     raise Exception('Missing Client Secret')
 
   client = app.config['HTTPCLIENT']
@@ -99,6 +103,25 @@ def _validate_github_with_key(config_key, config):
     raise Exception('Invalid client id or client secret')
 
 
+def _validate_google_login(config):
+  """ Validates the Google Login client ID and secret. """
+  google_login_config = config.get('GOOGLE_LOGIN_CONFIG')
+  if not google_login_config:
+    raise Exception('Missing client ID and client secret')
+
+  if not google_login_config.get('CLIENT_ID'):
+    raise Exception('Missing Client ID')
+
+  if not google_login_config.get('CLIENT_SECRET'):
+    raise Exception('Missing Client Secret')
+
+  client = app.config['HTTPCLIENT']
+  oauth = GoogleOAuthConfig(config, 'GOOGLE_LOGIN_CONFIG')
+  result = oauth.validate_client_id_and_secret(client)
+  if not result:
+    raise Exception('Invalid client id or client secret')
+
+
 def _validate_ssl(config):
   """ Validates the SSL configuration (if enabled). """
   if config.get('PREFERRED_URL_SCHEME', 'http') != 'https':
@@ -141,6 +164,7 @@ _VALIDATORS = {
   'mail': _validate_mailing,
   'github-login': _validate_github('GITHUB_LOGIN_CONFIG'),
   'github-trigger': _validate_github('GITHUB_TRIGGER_CONFIG'),
+  'google-login': _validate_google_login,
   'ssl': _validate_ssl,
   'ldap': _validate_ldap,
 }
\ No newline at end of file
diff --git a/util/oauth.py b/util/oauth.py
index 2405423ea..ede8823aa 100644
--- a/util/oauth.py
+++ b/util/oauth.py
@@ -70,7 +70,7 @@ class GithubOAuthConfig(OAuthConfig):
     # First: Verify that the github endpoint is actually Github by checking for the
     # X-GitHub-Request-Id here.
     api_endpoint = self._api_endpoint()
-    result = http_client.get(api_endpoint, auth=(self.client_id(), self.client_secret()))
+    result = http_client.get(api_endpoint, auth=(self.client_id(), self.client_secret()), timeout=5)
     if not 'X-GitHub-Request-Id' in result.headers:
       raise Exception('Endpoint is not a Github (Enterprise) installation')
 
@@ -87,7 +87,8 @@ class GithubOAuthConfig(OAuthConfig):
     #   - If the {client_id, client_secret} pair is invalid in some way, we get a 401 error.
     #   - If the pair is valid, then we get a 404 because the 'foo' token does not exists.
     validate_endpoint = self._get_url(api_endpoint, 'applications/%s/tokens/foo' % self.client_id())
-    result = http_client.get(validate_endpoint, auth=(self.client_id(), self.client_secret()))
+    result = http_client.get(validate_endpoint, auth=(self.client_id(), self.client_secret()),
+                                                timeout=5)
     return result.status_code == 404
 
   def get_public_config(self):
@@ -116,8 +117,21 @@ class GoogleOAuthConfig(OAuthConfig):
     return 'https://www.googleapis.com/oauth2/v1/userinfo'
 
   def validate_client_id_and_secret(self, http_client):
-    # No validation supported at this time.
-    return None
+    # To verify the Google client ID and secret, we hit the
+    # https://www.googleapis.com/oauth2/v3/token endpoint with an invalid request. If the client
+    # ID or secret are invalid, we get returned a 403 Unauthorized. Otherwise, we get returned
+    # another response code.
+    url = 'https://www.googleapis.com/oauth2/v3/token'
+    data = {
+      'code': 'fakecode',
+      'client_id': self.client_id(),
+      'client_secret': self.client_secret(),
+      'grant_type': 'authorization_code',
+      'redirect_uri': 'http://example.com'
+    }
+
+    result = http_client.post(url, data=data, timeout=5)
+    return result.status_code != 401
 
   def get_public_config(self):
     return  {