Initialize Firebase Admin SDK using secret stored in Secret Manager - python

I am trying to initialize the Firebase Admin SDK within a Cloud Run application, using a separate service account (i.e not the default service account).
The documentation suggests:
import firebase_admin
from firebase_admin import credentials
cred = credentials.Certificate("path/to/serviceAccountKey.json")
firebase_admin.initialize_app(cred)
However, I would like to avoid packaging secrets into the Cloud Run container, so I am retrieving the json file from Secret Manager, and trying to create the credentials, and pass it into: firebase_admin.initialize_app(cred)
import firebase_admin
from google.cloud import secretmanager
from google.oauth2 import service_account
# Create credentials object then initialize the firebase admin client
sec_client = secretmanager.SecretManagerServiceClient()
name = sec_client.secret_version_path(GOOGLE_CLOUD_PROJECT_NUMBER, FIREBASE_SA_SECRET_NAME, "latest")
response = sec_client.access_secret_version(name)
service_account_info = json.loads(response.payload.data.decode('UTF-8'))
creds = service_account.Credentials.from_service_account_info(service_account_info)
firebase_admin.initialize_app(creds)
Error received:
ValueError: Illegal Firebase credential provided. App must be
initialized with a valid credential instance.
Any tips are appreciated.

import firebase_admin
from google.cloud import secretmanager
from google.oauth2 import service_account
# Create credentials object then initialize the firebase admin client
sec_client = secretmanager.SecretManagerServiceClient()
name = sec_client.secret_version_path(GOOGLE_CLOUD_PROJECT_NUMBER, FIREBASE_SA_SECRET_NAME, "latest")
response = sec_client.access_secret_version(name)
service_account_info = json.loads(response.payload.data.decode('utf-8'))
# build credentials with the service account dict
creds = firebase_admin.credentials.Certificate(service_account_info)
# initialize firebase admin
firebase_app = firebase_admin.initialize_app(creds)

Related

Why do I get `secretmanager.versions.access` denied in GCP?

I am trying to access a secret stored in secrets manager.
I created a service account with owner role. I created a key from it. I run:
import os
os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = './keyfile.json'
from google.cloud import secretmanager
secret_client = secretmanager.SecretManagerServiceClient()
secret_name = f'projects/{project_id}/secrets/{secret_id}/versions/{version_id}'
response = secret_client.access_secret_version(request={"name": secret_name})
but I get:
google.api_core.exceptions.PermissionDenied: 403 Permission 'secretmanager.versions.access'
denied for resource 'projects/myprojnumber/secrets/mysecret/versions/1' (or it may not exist).
I checked the secret_name was the same as the secret's value in secret manager.
I have tried adding Secret Manager Secret Accessor and Secret Manager Viewer roles.
Edit: running this from cloud shell.
I think the issue is that the code is taking the Default Credentials of the Cloud Shell instead of using your SA key.
You can specify the credentials when creating the client
from google.cloud import secretmanager
from google.oauth2 import service_account
credentials = service_account.Credentials.from_service_account_file("./keyfile.json")
secret_client = secretmanager.SecretManagerServiceClient(credentials=credentials)
secret_name = f'projects/{project_id}/secrets/{secret_id}/versions/{version_id}'
response = secret_client.access_secret_version(request={"name": secret_name})
Another option using some of the methods found in the library docs:
from google.cloud import secretmanager
secret_client = secretmanager.SecretManagerServiceClient.from_service_account_file("./keyfile.json")
secret_name = f'projects/{project_id}/secrets/{secret_id}/versions/{version_id}'
response = secret_client.access_secret_version(request={"name": secret_name})
Just as an advice, being newbie does not mean you cannot Google a little more to search for something like how to use a SA as credential for the client of the library you're using.
For example you could easily find this doc which shows a sample.
Anyway, good luck with GCP!

How do you get gcloud access token in Python?

I'm looking to do the equivalent of the following in Python with out having to call these command using something like os.system and look at the output.
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/credentials.json"
export PROJECT_ID="my-project-name"
gcloud auth application-default print-access-token
Can this be done with Google SDK?
I think you can do this using the Google Auth Library. You can install it with pip:
$ pip install google-auth
Here's an example code using buckets:
from google.oauth2 import service_account
from google.cloud import storage
KEY='/path/to/key.json'
PROJECT='your_project_id'
# gcloud auth application-default print-access-token is no necessary
credentials = service_account.Credentials.from_service_account_file(KEY)
# Initialize the Cloud Storage client using the credentials
storage_client = storage.Client(PROJECT,credentials)
# List objects in a bucket
blobs = storage_client.list_blobs("a_bucket")
for blob in blobs:
print(blob.name)
Good luck coding!
Here the answer:
import google.auth
import google.auth.transport.requests
from google.oauth2 import service_account
from os.path import expanduser
from os import getenv
# The the service account key ABSOLUTE path from env or current folder
service_account_key = getenv(
'GOOGLE_APPLICATION_CREDENTIALS',
f'{expanduser(".")}/service-account-key.json'
)
# Creates a credentials object from the service account file
credentials = service_account.Credentials.from_service_account_file(
service_account_key, # key path
scopes=['https://www.googleapis.com/auth/cloud-platform'] # scopes
)
# Prepare an authentication request
auth_req = google.auth.transport.requests.Request()
# Request refresh tokens
credentials.refresh(auth_req)
# now we can print the access token
print(credentials.token)
Using python:
import request
requests.get('http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token').json().get('access_token')

Creating a webapp for managing Gmail email signatures for all employees from one place

I'm trying to create an application which allows the HR team to manage signatures for all the employees. Right now they use Gsuite to manage all the Gmail accounts of the employees. I'm looking for something which is similar to Signature Satori
I've tried using Gmail API to access the profile settings of a user and change the email signature of that authorized user. as shown [here]. Using the following code I've changed the signature on my gmail. (https://developers.google.com/gmail/api/guides/alias_and_signature_settings)
from __future__ import print_function
from apiclient import discovery
from httplib2 import Http
from oauth2client import file,client,tools
import pickle
import os.path
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
SCOPES = 'https://www.googleapis.com/auth/gmail.settings.basic'
creds = None
store = file.Storage('storage.json')
creds = store.get()
if not creds or creds.invalid:
flow =
client.flow_from_clientsecrets('client_secret.json',SCOPES)
creds = tools.run_flow(flow,store)
GMAIL = discovery.build('gmail','v1',http=creds.authorize(Http()))
addresses =
GMAIL.users().settings().sendAs().list(userId='me')
.execute().get('sendAs',[])
for address in addresses:
if address['isPrimary']:
break
print(addresses)
def signature():
return 'hhello' + str(address['sendAsEmail'])
DATA = {'signature': signature()}
rsp =
GMAIL.users().settings().sendAs()
.patch(userId='me',sendAsEmail=address['sendAsEmail'],body=DATA)
.execute()
print(f'Primary address signature changed to {rsp['signature']}')
I want to do this and provide a template for all the users in the company managed from Gsuite. Any ideas of how to approach this problem?
You have to use a Service Account for this. According to Google's Gmail API Documentation:
Addresses other than the primary address for the account can only be
updated by service account clients that have been delegated
domain-wide authority.
You can create a service account on the Google Cloud Platform Console, the steps for setting one up can be found on the G Suite Admin Help pages here.
Once you have set up a service account, you must give it domain-wide delegation access so that you are able to gain access to the user accounts and edit their signatures on their behalf.
You can use the Users.settings.sendAs.update method of the Gmail API to update the signatures of each user by using the signature parameter of the Users.settings.sendAs resource:
{
"signature": "html-formatted-string"
}
You can now connect to the Users.settings.sendAs.update() resource, though you need to make sure you authenticate with your service account:
from __future__ import print_function
from googleapiclient.discovery import build
from apiclient import errors
from httplib2 import Http
from email.mime.text import MIMEText
import base64
from google.oauth2 import service_account
SCOPES = ['https://www.googleapis.com/auth/gmail.settings.basic']
# This is your credentials file you downloaded from the Platform Console.
credsFile = 'service-key.json'
def signatureUpdater():
#...
emailAddresses = ["Populate", "an", "array", "with", "your", "user", "email", "addresses", "using", "Users: list", "method", "of", "Admin", "SDK", "API"]
q = {"signature": "The Signature You want your users to have <b>can include some html</b>"}
for x in emailAddresses:
response = serviceAccountLogin(x).users().settings().sendAs().update(userId = x, q = q).execute()
def serviceAccountLogin(email):
credentials = service_account.Credentials.from_service_account_file(credsFile, scopes = SCOPES)
delegatedCreds = credentials.with_subject(email)
service = build('gmail', 'v1', credentials = delegatedCreds)
return service
def main():
signatureUpdater()

"No such file or directory" with firebase_admin python library

I want to retrieve some data by a firebase database with using the official library for python (firebase_admin) instead of pyrebase or python-firebase.
I try to execute the following lines of code:
from firebase_admin import db
from firebase_admin import credentials
import firebase_admin
cred = credentials.Certificate('https://project_name.firebaseio.com/.json')
firebase_admin.initialize_app(cred)
result = db.Query.get()
but then I get the following error:
FileNotFoundError: [Errno 2] No such file or directory: 'https://project_name.firebaseio.com/.json'
even though when I enter this url on my browser (with project_name replaced with my real project name) I am getting the json of data from the database.
How can I fix this error?
The Certificate should point to a local file with your credentials/certificate. You are instead pointing it to your database URL, which is not a local file, so the library throws an error.
From the documentation on initializing the Python SDK:
import firebase_admin
from firebase_admin import credentials
from firebase_admin import db
# Fetch the service account key JSON file contents
cred = credentials.Certificate('path/to/serviceAccountKey.json')
# Initialize the app with a service account, granting admin privileges
firebase_admin.initialize_app(cred, {
'databaseURL': 'https://databaseName.firebaseio.com'
})
# As an admin, the app has access to read and write all data, regardless of Security Rules
ref = db.reference('restricted_access/secret_document')
print(ref.get())
Try this ,this works for me
import os
from firebase_admin import credentials, firestore, initialize_app
# Initialize Firestore DB
data = os.path.abspath(os.path.dirname(__file__)) + "/serviceAccountKey.json"
cred = credentials.Certificate(data)
default_app = initialize_app(cred)
db = firestore.client()

Google service account can't impersonate GSuite user

from google.oauth2 import service_account
import googleapiclient.discovery
SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = 'GOOGLE_SECRET.json'
credentials = service_account.Credentials.from_service_account_file(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
delegated_credentials = credentials.with_subject('address#example.com')
google_calendar = googleapiclient.discovery.build('calendar', 'v3', credentials=delegated_credentials)
events = google_calendar.events().list(
calendarId='address#example.com',
maxResults=10
).execute()
The result of the foregoing code is:
google.auth.exceptions.RefreshError: ('unauthorized_client: Client is unauthorized to retrieve access tokens using this method.', '{\n "error" : "unauthorized_client",\n "error_description" : "Client is unauthorized to retrieve access tokens using this method."\n}')
Domain-wide delegation is on. The service account client ID is authorized with the appropriate scopes in GSuite.
The service account works for normal credentials. It only doesn't work for delegated credentials.
I've tried different APIs (scopes) and different users in our domain.
I had a colleague try to code up a sample from scratch and he got the same thing.
I think you're problem is that your not authorising your credentials before making the call to the Calendar API but there is a couple of differences that you have that I use in my own implementation
Use the following import statements
from oauth2client.service_account import ServiceAccountCredentials
from apiclient import discovery
Use the from_json_keyfile_name method
Authorise the credentials and pass it as a http argument when building the service
Try this modification to your code:
from apiclient import discovery
from oauth2client.service_account import ServiceAccountCredentials
import httplib2
SCOPES = ['https://www.googleapis.com/auth/calendar']
SERVICE_ACCOUNT_FILE = 'GOOGLE_SECRET.json'
credentials = ServiceAccountCredentials.from_json_keyfile_name(
SERVICE_ACCOUNT_FILE, scopes=SCOPES)
# Use the create_delegated() method and authorize the delegated credentials
delegated_credentials = credentials.create_delegated('address#example.com')
delegated_http = delegated_credentials.authorize(httplib2.Http())
google_calendar = discovery.build('calendar', 'v3', http=delegated_http)
events = google_calendar.events().list(
calendarId='address#example.com',
maxResults=10
).execute()
I would also suggest setting calendarId='primary' in your API call so that you always return the primary calendar of the user who you currently have delegated credentials for.
For more information on authenticating using ServiceAccountCredentials see the following link: http://oauth2client.readthedocs.io/en/latest/source/oauth2client.service_account.html

Categories

Resources