Skip to content

Trim pydata-google-auth package and add tests #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Sep 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions pydata_google_auth/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
from .auth import default # noqa

from .auth import default
from .auth import get_user_credentials
from ._version import get_versions

versions = get_versions()
__version__ = versions.get('closest-tag', versions['version'])
__git_revision__ = versions['full-revisionid']
del get_versions, versions

"""pydata-google-auth

This package provides helpers for fetching Google API credentials.
"""

__all__ = [
'__version__',
'__git_revision__',
'default',
'get_user_credentials',
]
192 changes: 86 additions & 106 deletions pydata_google_auth/auth.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,62 @@
"""Private module for fetching Google BigQuery credentials."""
"""Private module for fetching Google API credentials."""

import json
import logging
import os
import os.path

import pydata_google_auth.exceptions
import google.auth
import google.auth.exceptions
import google.oauth2.credentials
from google_auth_oauthlib import flow
import oauthlib.oauth2.rfc6749.errors
import google.auth.transport.requests

from pydata_google_auth import exceptions

logger = logging.getLogger(__name__)

logger = logging.getLogger(__name__)

def default(
scopes,
client_id,
client_secret,
credentials_dirname,
credentials_filename,
project_id=None,
auth_local_webserver=False,
try_credentials=None):
if try_credentials is None:
def try_credentials(credentials, project_id):
return credentials, project_id

return get_credentials(
scopes,
client_id,
client_secret,
credentials_dirname,
credentials_filename,
project_id=project_id,
auth_local_webserver=auth_local_webserver,
try_credentials=try_credentials)
CLIENT_ID = (
'262006177488-3425ks60hkk80fssi9vpohv88g6q1iqd'
'.apps.googleusercontent.com'
)
CLIENT_SECRET = 'JSF-iczmzEgbTR-XK-2xaWAc'
CREDENTIALS_DIRNAME = 'pydata'
CREDENTIALS_FILENAME = 'pydata_google_credentials.json'


def get_credentials(
def default(
scopes,
client_id,
client_secret,
credentials_dirname,
credentials_filename,
try_credentials,
project_id=None, reauth=False,
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
credentials_dirname=CREDENTIALS_DIRNAME,
credentials_filename=CREDENTIALS_FILENAME,
reauth=False,
auth_local_webserver=False):
# Try to retrieve Application Default Credentials
credentials, default_project = get_application_default_credentials(
scopes, project_id=project_id, try_credentials=try_credentials)
credentials, default_project = get_application_default_credentials(scopes)

if credentials:
if credentials and credentials.valid:
return credentials, default_project

credentials = get_user_account_credentials(
credentials = get_user_credentials(
scopes,
client_id,
client_secret,
credentials_dirname,
credentials_filename,
project_id=project_id,
client_id=client_id,
client_secret=client_secret,
credentials_dirname=credentials_dirname,
credentials_filename=credentials_filename,
reauth=reauth,
auth_local_webserver=auth_local_webserver,
try_credentials=try_credentials)
return credentials, project_id
auth_local_webserver=auth_local_webserver)

if not credentials or not credentials.valid:
raise exceptions.PyDataCredentialsError(
'Could not get any valid credentials.')

def get_application_default_credentials(
scopes, try_credentials, project_id=None):
return credentials, None


def get_application_default_credentials(scopes):
"""
This method tries to retrieve the "default application credentials".
This could be useful for running code on Google Cloud Platform.
Expand All @@ -85,29 +76,28 @@ def get_application_default_credentials(
from the environment. Or, the retrieved credentials do not
have access to the project (project_id) on BigQuery.
"""
import google.auth
from google.auth.exceptions import DefaultCredentialsError

try:
credentials, default_project = google.auth.default(scopes=scopes)
except (DefaultCredentialsError, IOError):
credentials, project = google.auth.default(scopes=scopes)
except (google.auth.exceptions.DefaultCredentialsError, IOError) as exc:
logger.debug('Error getting default credentials: {}'.format(str(exc)))
return None, None

# Even though we now have credentials, check that the credentials can be
# used with BigQuery. For example, we could be running on a GCE instance
# that does not allow the BigQuery scopes.
billing_project = project_id or default_project
return try_credentials(credentials, billing_project)
if credentials and not credentials.valid:
request = google.auth.transport.requests.Request()
credentials.refresh(request)

return credentials, project


def get_user_account_credentials(
def get_user_credentials(
scopes,
client_id,
client_secret,
credentials_dirname,
credentials_filename,
project_id=None, reauth=False, auth_local_webserver=False,
credentials_path=None):
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
credentials_dirname=CREDENTIALS_DIRNAME,
credentials_filename=CREDENTIALS_FILENAME,
reauth=False,
auth_local_webserver=False):
"""Gets user account credentials.

This method authenticates using user credentials, either loading saved
Expand All @@ -122,26 +112,16 @@ def get_user_account_credentials(
GoogleCredentials : credentials
Credentials for the user with BigQuery access.
"""
from google_auth_oauthlib.flow import InstalledAppFlow
from oauthlib.oauth2.rfc6749.errors import OAuth2Error

# Use the default credentials location under ~/.config and the
# equivalent directory on windows if the user has not specified a
# credentials path.
if not credentials_path:
credentials_path = get_default_credentials_path(
credentials_dirname,
credentials_filename)

# Previously, pandas-gbq saved user account credentials in the
# current working directory. If the bigquery_credentials.dat file
# exists in the current working directory, move the credentials to
# the new default location.
if os.path.isfile('bigquery_credentials.dat'):
os.rename(credentials_filename, credentials_path)
credentials_path = get_default_credentials_path(
credentials_dirname,
credentials_filename)

credentials = load_user_account_credentials(
project_id=project_id, credentials_path=credentials_path)
credentials = None
if not reauth:
credentials = load_user_credentials_from_file(credentials_path)

client_config = {
'installed': {
Expand All @@ -153,26 +133,40 @@ def get_user_account_credentials(
}
}

if credentials is None or reauth:
app_flow = InstalledAppFlow.from_client_config(
if credentials is None:
app_flow = flow.InstalledAppFlow.from_client_config(
client_config, scopes=scopes)

try:
if auth_local_webserver:
credentials = app_flow.run_local_server()
else:
credentials = app_flow.run_console()
except OAuth2Error as ex:
raise pydata_google_auth.exceptions.AccessDenied(
"Unable to get valid credentials: {0}".format(ex))
except oauthlib.oauth2.rfc6749.errors.OAuth2Error as exc:
raise exceptions.PyDataCredentialsError(
"Unable to get valid credentials: {0}".format(exc))

save_user_account_credentials(credentials, credentials_path)

if credentials and not credentials.valid:
request = google.auth.transport.requests.Request()
credentials.refresh(request)

return credentials


def load_user_account_credentials(
try_credentials, project_id=None, credentials_path=None):
def load_user_credentials_from_info(credentials_json):
return google.oauth2.credentials.Credentials(
token=credentials_json.get('access_token'),
refresh_token=credentials_json.get('refresh_token'),
id_token=credentials_json.get('id_token'),
token_uri=credentials_json.get('token_uri'),
client_id=credentials_json.get('client_id'),
client_secret=credentials_json.get('client_secret'),
scopes=credentials_json.get('scopes'))


def load_user_credentials_from_file(credentials_path):
"""
Loads user account credentials from a local file.

Expand All @@ -192,40 +186,26 @@ def load_user_account_credentials(
credentials do not have access to the project (project_id)
on BigQuery.
"""
import google.auth.transport.requests
from google.oauth2.credentials import Credentials

try:
with open(credentials_path) as credentials_file:
credentials_json = json.load(credentials_file)
except (IOError, ValueError):
except (IOError, ValueError) as exc:
logger.debug('Error loading credentials from {}: {}'.format(
credentials_path, str(exc)))
return None

credentials = Credentials(
token=credentials_json.get('access_token'),
refresh_token=credentials_json.get('refresh_token'),
id_token=credentials_json.get('id_token'),
token_uri=credentials_json.get('token_uri'),
client_id=credentials_json.get('client_id'),
client_secret=credentials_json.get('client_secret'),
scopes=credentials_json.get('scopes'))

# Refresh the token before trying to use it.
request = google.auth.transport.requests.Request()
credentials.refresh(request)

return try_credentials(credentials, project_id)
return load_user_credentials_from_info(credentials_json)


def get_default_credentials_path(credentials_dirname, credentials_filename):
"""
Gets the default path to the BigQuery credentials
Gets the default path to the Google user credentials

.. versionadded 0.3.0

Returns
-------
Path to the BigQuery credentials
Path to the Google user credentials
"""
if os.name == 'nt':
config_path = os.environ['APPDATA']
Expand All @@ -234,7 +214,7 @@ def get_default_credentials_path(credentials_dirname, credentials_filename):

config_path = os.path.join(config_path, credentials_dirname)

# Create a pandas_gbq directory in an application-specific hidden
# Create a pydata directory in an application-specific hidden
# user folder on the operating system.
if not os.path.exists(config_path):
os.makedirs(config_path)
Expand Down
10 changes: 1 addition & 9 deletions pydata_google_auth/exceptions.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@


class AccessDenied(ValueError):
class PyDataCredentialsError(ValueError):
"""
Raised when invalid credentials are provided, or tokens have expired.
"""
pass


class InvalidPrivateKeyFormat(ValueError):
"""
Raised when provided private key has invalid format.
"""
pass
10 changes: 0 additions & 10 deletions tests/system/conftest.py

This file was deleted.

Loading