import json import os import jingrow from jingrow.core.utils import find from jingrow.utils import get_url from jingrow.utils.oauth import get_oauth2_authorize_url from google.auth.transport.requests import Request from google.oauth2 import id_token from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import Flow from googleapiclient.discovery import build from jcloude.api.account import get_account_request_from_key, setup_account from jcloude.api.saas import ( check_subdomain_availability, create_marketplace_subscription, create_or_rename_saas_site, ) from jcloude.jcloude.pagetype.site.saas_site import get_saas_domain from jcloude.utils import log_error from jcloude.utils.telemetry import capture, identify os.environ["OAUTHLIB_INSECURE_TRANSPORT"] = "1" def google_oauth_flow(): config = jingrow.conf.get("google_oauth_config") redirect_uri = config["web"].get("redirect_uris")[0] flow = Flow.from_client_config( client_config=config, scopes=[ "https://www.googleapis.com/auth/userinfo.profile", "openid", "https://www.googleapis.com/auth/userinfo.email", ], redirect_uri=redirect_uri, ) return flow @jingrow.whitelist(allow_guest=True) def google_login(saas_app=None): flow = google_oauth_flow() authorization_url, state = flow.authorization_url() minutes = 5 jingrow.cache().set_value( f"fc_oauth_state:{state}", saas_app or state, expires_in_sec=minutes * 60 ) return authorization_url @jingrow.whitelist(allow_guest=True) def callback(code=None, state=None): cached_key = f"fc_oauth_state:{state}" cached_state = jingrow.cache().get_value(cached_key) saas_app = cached_state in jingrow.db.get_all("Saas Settings", pluck="name") jingrow.cache().delete_value(cached_key) if (state == cached_state) or (saas_app): pass else: jingrow.local.response["http_status_code"] = 401 return "Invalid state parameter. The session timed out. Please try again or contact Jingrow Cloud support at https://jcloud.jingrow.com/support" try: flow = google_oauth_flow() flow.fetch_token(authorization_response=jingrow.request.url) except Exception as e: log_error("Google oauth Login failed", data=e) jingrow.local.response.type = "redirect" jingrow.local.response.location = "/dashboard/login" # id_info token_request = Request() id_info = id_token.verify_oauth2_token( id_token=flow.credentials._id_token, request=token_request, audience=jingrow.conf.get("google_oauth_config")["web"]["client_id"], ) email = id_info.get("email") # phone (this may return nothing if info doesn't exists) number = "" if flow.credentials.refresh_token: # returns only for the first authorization credentials = Credentials.from_authorized_user_info( json.loads(flow.credentials.to_json()) ) service = build("people", "v1", credentials=credentials) person = ( service.people().get(resourceName="people/me", personFields="phoneNumbers").execute() ) if person: phone = person.get("phoneNumbers") if phone: number = phone[0].get("value") # saas signup if saas_app and cached_state: account_request = create_account_request( email=email, first_name=id_info.get("given_name"), last_name=id_info.get("family_name"), phone_number=number, ) logo = jingrow.db.get_value("Saas Signup Generator", cached_state, "image_path") jingrow.local.response.type = "redirect" jingrow.local.response.location = get_url( f"/saas-oauth.html?app={cached_state}&key={account_request.request_key}&domain={get_saas_domain(cached_state)}&logo={logo}" ) else: # fc login or signup if not jingrow.db.exists("User", email): account_request = create_account_request( email=email, first_name=id_info.get("given_name"), last_name=id_info.get("family_name"), phone_number=number, ) jingrow.local.response.type = "redirect" jingrow.local.response.location = ( f"/dashboard/setup-account/{account_request.request_key}" ) # login else: jingrow.local.login_manager.login_as(email) jingrow.local.response.type = "redirect" jingrow.response.location = "/dashboard" def create_account_request(email, first_name, last_name, phone_number=""): account_request = jingrow.get_pg( { "pagetype": "Account Request", "team": email, "email": email, "first_name": first_name, "last_name": last_name, "phone_number": phone_number, "send_email": False, "role": "Jcloude Admin", "oauth_signup": True, } ).insert(ignore_permissions=True) jingrow.db.commit() return account_request @jingrow.whitelist(allow_guest=True) def saas_setup(key, app, country, subdomain): if not check_subdomain_availability(subdomain, app): jingrow.throw(f"Subdomain {subdomain} is already taken") all_countries = jingrow.db.get_all("Country", pluck="name") country = find(all_countries, lambda x: x.lower() == country.lower()) if not country: jingrow.throw("Country filed should be a valid country name") # create team and user account_request = get_account_request_from_key(key) if not jingrow.db.exists("Team", {"user": account_request.email}): setup_account( key=key, first_name=account_request.first_name, last_name=account_request.last_name, country=country, oauth_signup=True, ) # create a signup account request signup_ar = jingrow.get_pg( { "pagetype": "Account Request", "team": account_request.team, "email": account_request.email, "first_name": account_request.first_name, "last_name": account_request.last_name, "emaill": account_request.email, "saas": True, "erpnext": False, "saas_app": app, "role": "Jcloude Admin", "country": country, "subdomain": subdomain, } ).insert(ignore_permissions=True) site_name = signup_ar.get_site_name() identify( site_name, app=app, oauth=True, ) capture("completed_oauth_account_request", "fc_saas", site_name) create_or_rename_saas_site(app, signup_ar) jingrow.set_user("Administrator") create_marketplace_subscription(signup_ar) return get_url("/prepare-site?key=" + signup_ar.request_key + "&app=" + app) @jingrow.whitelist(allow_guest=True) def oauth_authorize_url(provider): return get_oauth2_authorize_url(provider, None)