From 05a27888b8dab9646252aefecb93d453915ddcbc Mon Sep 17 00:00:00 2001
From: Liz Fong-Jones <lizf@honeycomb.io>
Date: Thu, 12 Aug 2021 14:57:13 -0700
Subject: [PATCH] instantiate gh auth flow

---
 helios_auth/auth_systems/__init__.py     |   3 +-
 helios_auth/auth_systems/github.py       |  90 +++++++++++++++++++++++
 helios_auth/media/login-icons/github.png | Bin 0 -> 2625 bytes
 settings.py                              |   4 +
 4 files changed, 96 insertions(+), 1 deletion(-)
 create mode 100644 helios_auth/auth_systems/github.py
 create mode 100644 helios_auth/media/login-icons/github.png

diff --git a/helios_auth/auth_systems/__init__.py b/helios_auth/auth_systems/__init__.py
index 84382e8..1818341 100644
--- a/helios_auth/auth_systems/__init__.py
+++ b/helios_auth/auth_systems/__init__.py
@@ -1,5 +1,5 @@
 from django.conf import settings
-from . import password, twitter, linkedin, cas, facebook, google, yahoo, clever
+from . import password, twitter, linkedin, cas, facebook, google, yahoo, clever, github
 
 AUTH_SYSTEMS = {}
 
@@ -11,6 +11,7 @@ AUTH_SYSTEMS['facebook'] = facebook
 AUTH_SYSTEMS['google'] = google
 AUTH_SYSTEMS['yahoo'] = yahoo
 AUTH_SYSTEMS['clever'] = clever
+AUTH_SYSTEMS['github'] = github
 
 # not ready
 #import live
diff --git a/helios_auth/auth_systems/github.py b/helios_auth/auth_systems/github.py
new file mode 100644
index 0000000..30c0e82
--- /dev/null
+++ b/helios_auth/auth_systems/github.py
@@ -0,0 +1,90 @@
+"""
+Github Authentication
+
+"""
+
+import httplib2
+from django.conf import settings
+from django.core.mail import send_mail
+from oauth2client.client import OAuth2WebServerFlow
+
+from helios_auth import utils
+
+# some parameters to indicate that status updating is not possible
+STATUS_UPDATES = False
+
+# display tweaks
+LOGIN_MESSAGE = "Log in with GitHub"
+
+def get_flow(redirect_url=None):
+  return OAuth2WebServerFlow(
+    client_id=settings.GH_CLIENT_ID,
+    client_secret=settings.GH_CLIENT_SECRET,
+    scope='read:user user:email',
+    auth_uri="https://github.com/login/oauth/authorize",
+    token_uri="https://github.com/login/oauth/access_token",
+    redirect_uri=redirect_url,
+  )
+
+def get_auth_url(request, redirect_url):
+  flow = get_flow(redirect_url)
+  request.session['gh_redirect_uri'] = redirect_url
+  return flow.step1_get_authorize_url()
+
+def get_user_info_after_auth(request):
+  redirect_uri = request.session['gh_redirect_uri']
+  del request.session['gh_redirect_uri']
+  flow = get_flow(redirect_uri)
+  if 'code' not in request.GET:
+    return None
+  code = request.GET['code']
+  credentials = flow.step2_exchange(code)
+
+  http = httplib2.Http(".cache")
+  http = credentials.authorize(http)
+  (_, content) = http.request("https://api.github.com/user", "GET")
+  response = utils.from_json(content.decode('utf-8'))
+  user_id = response['login']
+  user_name = response['name']
+
+  (_, content) = http.request("https://api.github.com/user/emails", "GET")
+  response = utils.from_json(content.decode('utf-8'))
+  user_email = None
+  for email in response:
+    if email['verified'] and email['primary']:
+      user_email = email['email']
+      break
+  if not user_email:
+    raise Exception("email address with GitHub not verified")
+
+  return {
+    'type': 'github',
+    'user_id': user_id,
+    'name': '%s (%s)' % (user_id, user_name),
+    'info': {'email': user_email},
+    'token': {},
+  }
+
+def do_logout(user):
+  return None
+
+def update_status(token, message):
+  pass
+
+def send_message(user_id, name, user_info, subject, body):
+  send_mail(
+    subject,
+    body,
+    settings.SERVER_EMAIL,
+    ["%s <%s>" % (user_id, user_info['email'])],
+    fail_silently=False,
+  )
+
+def check_constraint(eligibility, user_info):
+  pass
+
+#
+# Election Creation
+#
+def can_create_election(user_id, user_info):
+  return True
diff --git a/helios_auth/media/login-icons/github.png b/helios_auth/media/login-icons/github.png
new file mode 100644
index 0000000000000000000000000000000000000000..182a1a3f734fc1b7d712c68b04c29bad9460d6cd
GIT binary patch
literal 2625
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
W4I>>0bz>}E>ge79v<vGaGyei9dr3zC

literal 0
HcmV?d00001

diff --git a/settings.py b/settings.py
index f4dddc3..b22c94f 100644
--- a/settings.py
+++ b/settings.py
@@ -246,6 +246,10 @@ CAS_ELIGIBILITY_REALM = get_from_env('CAS_ELIGIBILITY_REALM', "")
 CLEVER_CLIENT_ID = get_from_env('CLEVER_CLIENT_ID', "")
 CLEVER_CLIENT_SECRET = get_from_env('CLEVER_CLIENT_SECRET', "")
 
+# GitHub
+GH_CLIENT_ID = get_from_env('GH_CLIENT_ID', '')
+GH_CLIENT_SECRET = get_from_env('GH_CLIENT_SECRET', '')
+
 # email server
 EMAIL_HOST = get_from_env('EMAIL_HOST', 'localhost')
 EMAIL_PORT = int(get_from_env('EMAIL_PORT', "2525"))
-- 
GitLab