155 lines
6.1 KiB
Python
155 lines
6.1 KiB
Python
# -*- coding: utf-8 -*- #
|
|
# Copyright 2013 Google LLC. All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""A simple auth command to bootstrap authentication with oauth2."""
|
|
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import unicode_literals
|
|
|
|
import json
|
|
|
|
from googlecloudsdk.api_lib.auth import service_account as auth_service_account
|
|
from googlecloudsdk.calliope import base
|
|
from googlecloudsdk.calliope import exceptions as c_exc
|
|
from googlecloudsdk.core import log
|
|
from googlecloudsdk.core import properties
|
|
from googlecloudsdk.core.console import console_io
|
|
from googlecloudsdk.core.credentials import exceptions as creds_exceptions
|
|
from googlecloudsdk.core.credentials import store as c_store
|
|
from googlecloudsdk.core.util import encoding
|
|
from googlecloudsdk.core.util import files
|
|
|
|
|
|
class ActivateServiceAccount(base.SilentCommand):
|
|
r"""Authorize access to Google Cloud with a service account.
|
|
|
|
To allow `gcloud` (and other tools in Google Cloud CLI) to use service account
|
|
credentials to make requests, use this command to import these credentials
|
|
from a file that contains a private authorization key, and activate them for
|
|
use in `gcloud`. {command} serves the same function as `gcloud auth login`
|
|
but uses a service account rather than Google user credentials.
|
|
|
|
For more information on authorization and credential types, see:
|
|
[](https://cloud.google.com/sdk/docs/authorizing).
|
|
|
|
_Key File_
|
|
|
|
To obtain the key file for this command, use either the [Google Cloud
|
|
Console](https://console.cloud.google.com) or `gcloud iam
|
|
service-accounts keys create`. The key file can be .json (preferred) or
|
|
.p12 (legacy) format. In the case of legacy .p12 files, a separate password
|
|
might be required and is displayed in the Console when you create the key.
|
|
|
|
_Credentials_
|
|
|
|
Credentials will also be activated (similar to running
|
|
`gcloud config set account [ACCOUNT_NAME]`).
|
|
|
|
If a project is specified using the `--project` flag, the project is set in
|
|
active configuration, which is the same as running
|
|
`gcloud config set project [PROJECT_NAME]`. Any previously active credentials,
|
|
will be retained (though no longer default) and can be
|
|
displayed by running `gcloud auth list`.
|
|
|
|
If you want to delete previous credentials, see `gcloud auth revoke`.
|
|
|
|
_Note:_ Service accounts use client quotas for tracking usage.
|
|
|
|
## EXAMPLES
|
|
|
|
To authorize `gcloud` to access Google Cloud using an existing
|
|
service account while also specifying a project, run:
|
|
|
|
$ {command} SERVICE_ACCOUNT@DOMAIN.COM \
|
|
--key-file=/path/key.json --project=PROJECT_ID
|
|
"""
|
|
|
|
@staticmethod
|
|
def Args(parser):
|
|
"""Set args for serviceauth."""
|
|
parser.add_argument('account', nargs='?',
|
|
help='E-mail address of the service account.')
|
|
parser.add_argument('--key-file',
|
|
help=('Path to the private key file.'),
|
|
required=True)
|
|
group = parser.add_mutually_exclusive_group()
|
|
group.add_argument('--password-file',
|
|
help=('Path to a file containing the password for the '
|
|
'service account private key '
|
|
'(only for a .p12 file).'))
|
|
group.add_argument('--prompt-for-password', action='store_true',
|
|
help=('Prompt for the password for the service account '
|
|
'private key (only for a .p12 file).'))
|
|
|
|
def Run(self, args):
|
|
"""Create service account credentials."""
|
|
|
|
file_content, is_json = _IsJsonFile(args.key_file)
|
|
if is_json:
|
|
cred = auth_service_account.CredentialsFromAdcDictGoogleAuth(
|
|
file_content)
|
|
if args.password_file or args.prompt_for_password:
|
|
raise c_exc.InvalidArgumentException(
|
|
'--password-file',
|
|
'A .json service account key does not require a password.')
|
|
account = cred.service_account_email
|
|
if args.account and args.account != account:
|
|
raise c_exc.InvalidArgumentException(
|
|
'ACCOUNT',
|
|
'The given account name does not match the account name in the key '
|
|
'file. This argument can be omitted when using .json keys.')
|
|
else:
|
|
account = args.account
|
|
if not account:
|
|
raise c_exc.RequiredArgumentException(
|
|
'ACCOUNT', 'An account is required when using .p12 keys')
|
|
password = None
|
|
if args.password_file:
|
|
try:
|
|
password = files.ReadFileContents(args.password_file).strip()
|
|
except files.Error as e:
|
|
raise c_exc.UnknownArgumentException('--password-file', e)
|
|
elif args.prompt_for_password:
|
|
password = console_io.PromptPassword('Password: ')
|
|
|
|
cred = auth_service_account.CredentialsFromP12Key(
|
|
file_content, account, password=password)
|
|
|
|
try:
|
|
c_store.ActivateCredentials(account, cred)
|
|
except creds_exceptions.TokenRefreshError as e:
|
|
log.file_only_logger.exception(e)
|
|
raise
|
|
|
|
project = args.project
|
|
if project:
|
|
properties.PersistProperty(properties.VALUES.core.project, project)
|
|
|
|
log.status.Print('Activated service account credentials for: [{0}]'
|
|
.format(account))
|
|
|
|
|
|
def _IsJsonFile(filename):
|
|
"""Check and validate if given filename is proper json file."""
|
|
content = console_io.ReadFromFileOrStdin(filename, binary=True)
|
|
try:
|
|
return json.loads(encoding.Decode(content)), True
|
|
except ValueError as e:
|
|
if filename.endswith('.json'):
|
|
raise auth_service_account.BadCredentialFileException(
|
|
'Could not read json file {0}: {1}'.format(filename, e))
|
|
return content, False
|