11import logging
22import requests
33from flask import redirect , url_for , Blueprint , flash , request , session
4- from flask_oauthlib . client import OAuth
4+
55
66from redash import models , settings
77from redash .authentication import (
1111)
1212from redash .authentication .org_resolving import current_org
1313
14- logger = logging .getLogger ("google_oauth" )
15-
16- oauth = OAuth ()
17- blueprint = Blueprint ("google_oauth" , __name__ )
18-
19-
20- def google_remote_app ():
21- if "google" not in oauth .remote_apps :
22- oauth .remote_app (
23- "google" ,
24- base_url = "https://www.google.com/accounts/" ,
25- authorize_url = "https://accounts.google.com/o/oauth2/auth?prompt=select_account+consent" ,
26- request_token_url = None ,
27- request_token_params = {
28- "scope" : "https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile"
29- },
30- access_token_url = "https://accounts.google.com/o/oauth2/token" ,
31- access_token_method = "POST" ,
32- consumer_key = settings .GOOGLE_CLIENT_ID ,
33- consumer_secret = settings .GOOGLE_CLIENT_SECRET ,
34- )
35-
36- return oauth .google
37-
38-
39- def get_user_profile (access_token ):
40- headers = {"Authorization" : "OAuth {}" .format (access_token )}
41- response = requests .get (
42- "https://www.googleapis.com/oauth2/v1/userinfo" , headers = headers
43- )
44-
45- if response .status_code == 401 :
46- logger .warning ("Failed getting user profile (response code 401)." )
47- return None
48-
49- return response .json ()
14+ from authlib .integrations .flask_client import OAuth
5015
5116
5217def verify_profile (org , profile ):
@@ -65,60 +30,102 @@ def verify_profile(org, profile):
6530 return False
6631
6732
68- @blueprint .route ("/<org_slug>/oauth/google" , endpoint = "authorize_org" )
69- def org_login (org_slug ):
70- session ["org_slug" ] = current_org .slug
71- return redirect (url_for (".authorize" , next = request .args .get ("next" , None )))
33+ def create_google_oauth_blueprint (app ):
34+ oauth = OAuth (app )
7235
36+ logger = logging .getLogger ("google_oauth" )
37+ blueprint = Blueprint ("google_oauth" , __name__ )
7338
74- @blueprint .route ("/oauth/google" , endpoint = "authorize" )
75- def login ():
76- callback = url_for (".callback" , _external = True )
77- next_path = request .args .get (
78- "next" , url_for ("redash.index" , org_slug = session .get ("org_slug" ))
39+ CONF_URL = "https://accounts.google.com/.well-known/openid-configuration"
40+ oauth = OAuth (app )
41+ oauth .register (
42+ name = "google" ,
43+ server_metadata_url = CONF_URL ,
44+ client_kwargs = {"scope" : "openid email profile" },
7945 )
80- logger .debug ("Callback url: %s" , callback )
81- logger .debug ("Next is: %s" , next_path )
82- return google_remote_app ().authorize (callback = callback , state = next_path )
83-
84-
85- @blueprint .route ("/oauth/google_callback" , endpoint = "callback" )
86- def authorized ():
87- resp = google_remote_app ().authorized_response ()
88- access_token = resp ["access_token" ]
89-
90- if access_token is None :
91- logger .warning ("Access token missing in call back request." )
92- flash ("Validation error. Please retry." )
93- return redirect (url_for ("redash.login" ))
94-
95- profile = get_user_profile (access_token )
96- if profile is None :
97- flash ("Validation error. Please retry." )
98- return redirect (url_for ("redash.login" ))
99-
100- if "org_slug" in session :
101- org = models .Organization .get_by_slug (session .pop ("org_slug" ))
102- else :
103- org = current_org
104-
105- if not verify_profile (org , profile ):
106- logger .warning (
107- "User tried to login with unauthorized domain name: %s (org: %s)" ,
108- profile ["email" ],
109- org ,
46+
47+ def get_user_profile (access_token ):
48+ headers = {"Authorization" : "OAuth {}" .format (access_token )}
49+ response = requests .get (
50+ "https://www.googleapis.com/oauth2/v1/userinfo" , headers = headers
11051 )
111- flash ("Your Google Apps account ({}) isn't allowed." .format (profile ["email" ]))
112- return redirect (url_for ("redash.login" , org_slug = org .slug ))
11352
114- picture_url = "%s?sz=40" % profile ["picture" ]
115- user = create_and_login_user (org , profile ["name" ], profile ["email" ], picture_url )
116- if user is None :
117- return logout_and_redirect_to_index ()
53+ if response .status_code == 401 :
54+ logger .warning ("Failed getting user profile (response code 401)." )
55+ return None
11856
119- unsafe_next_path = request .args .get ("state" ) or url_for (
120- "redash.index" , org_slug = org .slug
121- )
122- next_path = get_next_path (unsafe_next_path )
57+ return response .json ()
58+
59+ @blueprint .route ("/<org_slug>/oauth/google" , endpoint = "authorize_org" )
60+ def org_login (org_slug ):
61+ session ["org_slug" ] = current_org .slug
62+ return redirect (url_for (".authorize" , next = request .args .get ("next" , None )))
63+
64+ @blueprint .route ("/oauth/google" , endpoint = "authorize" )
65+ def login ():
66+
67+ redirect_uri = url_for (".callback" , _external = True )
68+
69+ next_path = request .args .get (
70+ "next" , url_for ("redash.index" , org_slug = session .get ("org_slug" ))
71+ )
72+ logger .debug ("Callback url: %s" , redirect_uri )
73+ logger .debug ("Next is: %s" , next_path )
74+
75+ session ["next_url" ] = next_path
76+
77+ return oauth .google .authorize_redirect (redirect_uri )
78+
79+ @blueprint .route ("/oauth/google_callback" , endpoint = "callback" )
80+ def authorized ():
81+
82+ logger .debug ("Authorized user inbound" )
83+
84+ resp = oauth .google .authorize_access_token ()
85+ user = resp .get ("userinfo" )
86+ if user :
87+ session ["user" ] = user
88+
89+ access_token = resp ["access_token" ]
90+
91+ if access_token is None :
92+ logger .warning ("Access token missing in call back request." )
93+ flash ("Validation error. Please retry." )
94+ return redirect (url_for ("redash.login" ))
95+
96+ profile = get_user_profile (access_token )
97+ if profile is None :
98+ flash ("Validation error. Please retry." )
99+ return redirect (url_for ("redash.login" ))
100+
101+ if "org_slug" in session :
102+ org = models .Organization .get_by_slug (session .pop ("org_slug" ))
103+ else :
104+ org = current_org
105+
106+ if not verify_profile (org , profile ):
107+ logger .warning (
108+ "User tried to login with unauthorized domain name: %s (org: %s)" ,
109+ profile ["email" ],
110+ org ,
111+ )
112+ flash (
113+ "Your Google Apps account ({}) isn't allowed." .format (profile ["email" ])
114+ )
115+ return redirect (url_for ("redash.login" , org_slug = org .slug ))
116+
117+ picture_url = "%s?sz=40" % profile ["picture" ]
118+ user = create_and_login_user (
119+ org , profile ["name" ], profile ["email" ], picture_url
120+ )
121+ if user is None :
122+ return logout_and_redirect_to_index ()
123+
124+ unsafe_next_path = session .get ("next_url" ) or url_for (
125+ "redash.index" , org_slug = org .slug
126+ )
127+ next_path = get_next_path (unsafe_next_path )
128+
129+ return redirect (next_path )
123130
124- return redirect ( next_path )
131+ return blueprint
0 commit comments