Skip to content

Commit c7f3c6d

Browse files
Merge pull request #4670 from communitybridge/unicron-add-return-url-to-get-user-from-session
Support redirect_url when no github session yet in /v2/user-from-session and state, code from github callback
2 parents 55eb073 + 116fe42 commit c7f3c6d

File tree

4 files changed

+83
-14
lines changed

4 files changed

+83
-14
lines changed

cla-backend/cla/controllers/repository_service.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"""
77

88
import cla
9-
9+
from falcon import HTTP_202, HTTP_404
1010

1111
def received_activity(provider, data):
1212
"""
@@ -34,18 +34,19 @@ def sign_request(provider, installation_id, github_repository_id, change_request
3434
service = cla.utils.get_repository_service(provider)
3535
return service.sign_request(installation_id, github_repository_id, change_request_id, request)
3636

37-
def user_from_session(redirect, request, response=None):
37+
def user_from_session(redirect, redirect_url, state, code, request, response=None):
3838
"""
3939
Return user from OAuth2 session
4040
"""
4141
# LG: to test using MockGitHub class
4242
# import os
4343
# from cla.models.github_models import MockGitHub
44-
# user = MockGitHub(os.environ["GITHUB_OAUTH_TOKEN"]).user_from_session(request)
45-
user = cla.utils.get_repository_service('github').user_from_session(request, redirect)
44+
# user = MockGitHub(os.environ["GITHUB_OAUTH_TOKEN"]).user_from_session(request, redirect, redirect_url, state, code)
45+
user = cla.utils.get_repository_service('github').user_from_session(request, redirect, redirect_url, state, code)
4646
if user is None:
4747
response.status = HTTP_404
4848
return {"errors": "Cannot find user from session"}
4949
if isinstance(user, dict):
50+
response.status = HTTP_202
5051
return user
5152
return user.to_dict()

cla-backend/cla/models/github_models.py

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -96,21 +96,49 @@ def received_activity(self, data):
9696
else:
9797
cla.log.debug("github_models.received_activity - Ignoring unsupported action: {}".format(data["action"]))
9898

99-
def user_from_session(self, request, redirect):
100-
fn = "github_models.user_from_session" # function name
99+
def user_from_session(self, request, redirect, redirect_url, state, code):
100+
fn = "github_models.user_from_session"
101101
cla.log.debug(f"{fn} - Loading session from request: {request}...")
102102
session = self._get_request_session(request)
103+
cla.log.debug(f"{fn} - redirect: {redirect}, redirect_url: {redirect_url}, state: {state}, code: {code}, session: {session}")
104+
105+
# we can already have token in the session
103106
if "github_oauth2_token" in session:
104107
cla.log.debug(f"{fn} - Using existing session GitHub OAuth2 token")
105108
user = self.get_or_create_user(request)
106109
cla.log.debug(f"{fn} - loaded user {user.to_dict()}")
107110
return user
111+
112+
# if not then we can either request a new OAuth2 GitHub authentication or user code & state from GitHub to create a session
113+
if code and state:
114+
session_state = None
115+
if "github_oauth2_state" in session:
116+
session_state = session["github_oauth2_state"]
117+
cla.log.warning(f"{fn} - github_oauth2_state in current session: {session_state}")
118+
else:
119+
cla.log.warning(f"{fn} - github_oauth2_state not set in current session")
120+
if session_state and state != session_state:
121+
cla.log.warning(f"{fn} - invalid GitHub OAuth2 state {session_state} expecting {state}")
122+
raise falcon.HTTPBadRequest(f"Invalid OAuth2 state: '{session_state}' != '{state}'")
123+
token_url = cla.conf["GITHUB_OAUTH_TOKEN_URL"]
124+
client_id = os.environ["GH_OAUTH_CLIENT_ID"]
125+
client_secret = os.environ["GH_OAUTH_SECRET"]
126+
try:
127+
token = self._fetch_token(client_id, state, token_url, client_secret, code)
128+
except Exception as err:
129+
cla.log.warning(f"{fn} - GitHub OAuth2 error: {err}. Likely bad or expired code.")
130+
raise falcon.HTTPBadRequest("OAuth2 code is invalid or expired")
131+
cla.log.debug(f"{fn} - oauth2 token received for state {state}: {token} - storing token in session")
132+
session["github_oauth2_token"] = token
133+
user = self.get_or_create_user(request)
134+
cla.log.debug(f"{fn} - loaded user {user.to_dict()}")
135+
return user
108136
else:
109137
cla.log.debug(f"{fn} - No existing GitHub OAuth2 token - building authorization url and state")
110-
authorization_url, state = self.get_authorization_url_and_state(None, None, None, ["user:email"])
138+
authorization_url, new_state = self.get_github_oauth2_redirect_url_and_state(redirect_url)
111139
cla.log.debug(f"{fn} - Obtained GitHub OAuth2 state from authorization - storing state in the session...")
112-
session["github_oauth2_state"] = state
113-
cla.log.debug(f"{fn} - GitHub OAuth2 request with state {state} - sending user to {authorization_url}")
140+
session["github_oauth2_state"] = new_state
141+
cla.log.debug(f"{fn} - GitHub OAuth2 request with state {new_state} - sending user to {authorization_url}")
114142
if redirect:
115143
raise falcon.HTTPFound(authorization_url)
116144
else:
@@ -176,6 +204,25 @@ def _get_request_session(self, request) -> dict: # pylint: disable=no-self-use
176204

177205
return session
178206

207+
def get_github_oauth2_redirect_url_and_state(self, redirect_uri):
208+
fn = "github_models.get_github_oauth2_redirect_url_and_state"
209+
github_oauth_url = cla.conf["GITHUB_OAUTH_AUTHORIZE_URL"]
210+
github_oauth_client_id = os.environ["GH_OAUTH_CLIENT_ID"]
211+
if not redirect_uri:
212+
redirect_uri = os.environ.get("CLA_API_BASE", "").strip() + "/v2/user-from-session"
213+
214+
scope = ["user:email"]
215+
cla.log.debug(
216+
f"{fn} - Directing user to the github authorization url: {github_oauth_url} via "
217+
f"our github installation flow: {redirect_uri} "
218+
f"using the github oauth client id: {github_oauth_client_id[0:5]} "
219+
f"with scope: {scope}"
220+
)
221+
222+
return self._get_authorization_url_and_state(
223+
client_id=github_oauth_client_id, redirect_uri=redirect_uri, scope=scope, authorize_url=github_oauth_url
224+
)
225+
179226
def get_authorization_url_and_state(self, installation_id, github_repository_id, pull_request_number, scope):
180227
"""
181228
Helper method to get the GitHub OAuth2 authorization URL and state.
@@ -1848,7 +1895,7 @@ def _fetch_token(self, client_id, state, token_url, client_secret, code): # pyl
18481895
def _get_request_session(self, request) -> dict:
18491896
if self.oauth2_token:
18501897
return {
1851-
"github_oauth2_token": "random-token",
1898+
"github_oauth2_token": "random-token", # LG: comment this out to see how Mock class woudl attempt to fetch GitHub token using state & code
18521899
"github_oauth2_state": "random-state",
18531900
"github_origin_url": "http://github/origin/url",
18541901
"github_installation_id": 1,

cla-backend/cla/routes.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,7 +1808,8 @@ def get_event(event_id: hug.types.text, response):
18081808
def user_from_session(request, response):
18091809
"""
18101810
GET: /user-from-session
1811-
Example: https://api.dev.lfcla.com/v2/user-from-session
1811+
Example: https://api.dev.lfcla.com/v2/user-from-session?redirect=0&redirect_url=localhost%3A4200
1812+
Example: https://api.dev.lfcla.com/v2/user-from-session?state=xyz&code=xyz
18121813
Returns user object from OAuth2 session
18131814
Example user returned:
18141815
{
@@ -1832,10 +1833,20 @@ def user_from_session(request, response):
18321833
"user_name": "Test User",
18331834
"version": "v1"
18341835
}
1836+
Can also return 302 redirect (if redirect mode is set redirect=1|yes|true)
1837+
Can also return 202 redirect_url for GitHub OAuth2 if redirect mode is not set (redirect=0|no|false) with payload:
1838+
{
1839+
"redirect_url": "https://github.com/login/oauth/authorize?response_type=code&client_id=38f6d46ff92b7ed04071&redirect_uri=abc&scope=user%3Aemail&state=VCshZQtMs0hPMw6XuMBBZODVaWAxXX"
1840+
}
1841+
Can also return 404 on OAuth2 errors or missing redirect_url when no session present
1842+
return_url should ideally be "CLA contributor console" URL + /v2/user-from-session, Github will add "?state=xyz&code=xyz"
18351843
"""
18361844
raw_redirect = request.params.get('redirect', 'false').lower()
18371845
redirect = raw_redirect in ('1', 'true', 'yes')
1838-
return cla.controllers.repository_service.user_from_session(redirect, request, response)
1846+
redirect_url = request.params.get('redirect_url', '')
1847+
state = request.params.get('state', '')
1848+
code = request.params.get('code', '')
1849+
return cla.controllers.repository_service.user_from_session(redirect, redirect_url, state, code, request, response)
18391850

18401851

18411852
@hug.post("/events", versions=1)

utils/get_user_from_session_py.sh

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
# API_URL=https://[xyz].ngrok-free.app (defaults to localhost:5000)
33
# API_URL=https://api.lfcla.dev.platform.linuxfoundation.org
44
# REDIRECT=0|1 DEBUG='' ./utils/get_user_from_session_py.sh
5+
# API_URL=https://api.lfcla.dev.platform.linuxfoundation.org DEBUG=1 REDIRECT=0 ./utils/get_user_from_session_py.sh 'https://contributor.easycla.lfx.linuxfoundation.org'
6+
# CODE=xyz STAE=xyz
7+
8+
export redirect_url="${1}"
9+
export encoded_redirect_url=$(jq -rn --arg x "$redirect_url" '$x|@uri')
510

611
if [ -z "$API_URL" ]
712
then
@@ -13,12 +18,17 @@ then
1318
export REDIRECT="0"
1419
fi
1520

16-
API="${API_URL}/v2/user-from-session?redirect=${REDIRECT}"
21+
if ( [ -z "${CODE}" ] && [ -z "${STATE}" ] )
22+
then
23+
export API="${API_URL}/v2/user-from-session?redirect=${REDIRECT}&redirect_url=${encoded_redirect_url}"
24+
else
25+
export API="${API_URL}/v2/user-from-session?code=${CODE}&state=${STATE}"
26+
fi
1727

1828
if [ ! -z "$DEBUG" ]
1929
then
2030
echo "curl -s -XGET -H \"Content-Type: application/json\" \"${API}\""
21-
curl -s -XGET -H "Content-Type: application/json" "${API}"
31+
curl -i -s -XGET -H "Content-Type: application/json" "${API}"
2232
else
2333
curl -s -XGET -H "Content-Type: application/json" "${API}" | jq -r '.'
2434
fi

0 commit comments

Comments
 (0)