Connecting Twilio API to Google Cloud Functions - python

I am trying to deploy a python script in Google Cloud Functions where the user submits a yes-or-no answer from Whatsapp and it reaches the Twilio API. If this function receives a 'Yes' it activates a query in Google Big Query table and reply with a report. My point is: if I create this function with no authentication, it works fine. However, it doesn't work for me, because it gives private information about my company.
So, in order to avoid problems, I create a function authenticating with Cloud IAM. When I pass the service account to this function, I give permission to invoke cloud function, the account inherit all permissions to read and execute jobs in Big Query and I give permission to service agents. Even following all these steps, I'm still receiving a 403 error.
Here is the code I'm trying to deploy in Google Cloud Functions:
from datetime import datetime, timedelta
from dotenv import load_dotenv
import os
from sales import Sales
from flask import Flask, request
from functools import wraps
from utils import format_currency, emoji_alerts
load_dotenv()
app = Flask(__name__)
def message_sales():
data = (datetime.now()-timedelta(days=1)).strftime('%d/%m/%Y')
resultado = Sales()
return f""" message """
#here is where the message is generated, this f-string queries results in bigquery
#app.route('/reply', methods=['GET','POST'])
def send_message(request):
from twilio.rest import Client
from twilio.twiml.messaging_response import MessagingResponse
account_sid = os.getenv('TWILIO_ACCOUNT_SID')
auth_token = os.getenv('TWILIO_AUTH_TOKEN')
incoming_msg = request.values.get('Body', '').lower()
resp = MessagingResponse()
msg = resp.message()
responded = False
report = message_sales()
if 'yes' in incoming_msg:
msg.body(report)
responded = True
elif 'no' in incoming_msg:
msg.body('Ok')
responded = True
return str(resp)
if __name__ == "__main__":
app.run(debug=False, host='0.0.0.0', port=2020)
And here is the connection function to big query and a example of a query:
def run_google_query(query):
credentials = service_account.Credentials.from_service_account_file(GOOGLE_APPLICATION_CREDENTIALS)
client = bigquery.Client(project='project-id', credentials=credentials)
return [row[0] for row in client.query(query)][0]
def get_resultado_dia(self):
return f"""
SELECT RESULTADO_ATUAL FROM `table`
WHERE DATA_VENDA = '{self.ontem.strftime('%Y-%m-%d')}'
"""
This is my last deploy. I have tried to use Secret Manager library, I have created a service account with the necessary permissions, I've given more permission to actual service accounts and nothing worked.
I believe that I need to authenticate the Twilio API with Google Cloud, but I can't find a clear explanation on how to procedure with that. Anyway, create a unauthenticated http request won't be an option, since the information shouldn't be open.

Related

Bigquery - google auth does not direct to url

I'm trying to run a query on bigquery in a Django project and get results. While working successfully in localhost, it does not redirect to the verification link at all when I take it to the live server.
I think I need to change the redirect_uri value as I read it. I added this in Da appflow variable but the url doesn't change. I am using the same query below with the example query in google's document, I am submitting my own query because it contains private information, but it is exactly the same query.
I have added to Authorized redirect URIs, and I put the api in production mode.;
The resulting redirect url is output as localhost in this way;
https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=123-nml31ekr2n0didomei5.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fbigquery&state=XF1WdtCoR4HaICwzSKk9A1giBrSzBv&access_type=offline
def query_stackoverflow():
launch_browser = True
project = 'xx-prod'
appflow = flow.InstalledAppFlow.from_client_secrets_file("static/client_secret_518684-nmpoqtgo5flvcgnl31ekr2ni5.apps.googleusercontent.com.json", scopes=["https://www.googleapis.com/auth/bigquery"], redirect_uri=["https://xx.com/"])
if launch_browser:
appflow.run_local_server()
else:
appflow.run_console()
credentials = appflow.credentials
client = bigquery.Client(project=project, credentials=credentials)
client = bigquery.Client()
query_job = client.query(
"""
SELECT
CONCAT(
'https://stackoverflow.com/questions/',
CAST(id as STRING)) as url,
view_count
FROM `bigquery-public-data.stackoverflow.posts_questions`
WHERE tags like '%google-bigquery%'
ORDER BY view_count DESC
LIMIT 10"""
)
results = query_job.result() # Waits for job to complete.
for row in results:
print("{} : {} views".format(row.url, row.view_count))
On live server google return auth url like this;
Please visit this URL to authorize this application: https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=51864584-nmpoqtgo5flvcgnln0didomei5.apps.googleusercontent.com&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fbigquery&state=W2uMZwzaYMEpFzExodRCf2wA4&access_type=offline
The first problem is that it does not automatically redirect to the link as in localhost, the second problem is that when I open this link manually, the link cannot be reached after mail verification.
From what i can see your code is using installed app flow. This means that the consent screen is going to open up on the machine its running on. If you have this running on a server, are you logging into the server and running it or are you in fact creating a web application?
flow.InstalledAppFlow
web app
If you are making a web application then you should be following this sample.
API access on behalf of your clients (web flow)
You will need to convert it to work with big query.
import google.oauth2.credentials
import google_auth_oauthlib.flow
# Initialize the flow using the client ID and secret downloaded earlier.
# Note: You can use the GetAPIScope helper function to retrieve the
# appropriate scope for AdWords or Ad Manager.
flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file(
'client_secret.json',
scope=[oauth2.GetAPIScope('adwords')])
# Indicate where the API server will redirect the user after the user completes
# the authorization flow. The redirect URI is required.
flow.redirect_uri = 'https://www.example.com/oauth2callback'
The code for a web application is slightly different then that of an installed application.

Stuck with azure function app in python using managed identity

I am trying to code function app which will get data from Log analytics workspace and push to event hub using python3. Function app uses managed identity.i am using azure sdk for python. my current code looks like this:
def getAzureEventData():
"""
if "MSI_ENDPOINT" in os.environ:
print("GeTTING MSI Authentication")
creds = MSIAuthentication()
else:
creds, *_ = get_azure_cli_credentials()
"""
## want to find out which one is correct tested each one.
creds = DefaultAzureCredential()
creds=CredentialWrapper()
creds = MSIAuthentication()
#creds, _ = get_azure_cli_credentials(resource="https://api.loganalytics.io")
log_client = LogAnalyticsDataClient(creds)
laQuery = 'ActivityLog | where TimeGenerated > ago(1d)'
result = log_client.query(cisalog_workspace_id, QueryBody(query=laQuery))
as per examples I have seen ,
creds, _ = get_azure_cli_credentials(resource="https://api.loganalytics.io")
was used, but when I use that function without any DefaultCredential(), then I get 404 error which says System managed identity is not enabled. when I use DefualtCrednetial I get access_token error and as per suggestion I am using wrapper found in internet. when I use that, I get Exception: ErrorResponseException: (InvalidTokenError) The provided authentication is not valid for this resource.
So I am confused how to use Loganalytics SDK client. I am testing in local and also in portal. My end goal is a function app using system managed identity with IAM roles to access LA workspace . I have granted Monitoring reader role on workspace to SMI. still facing issue.
If you want to call Azure Log Analytics Rest API in Azure function with AzureMSI, you need to assign Azure RABC role Log Analytics Reader to the MSI. For more details, please refer to here.
For example
Enable Azure Function MSI
Assign role
New-AzRoleAssignment -ObjectId "<the objectId of Azure function MSI>" -RoleDefinitionName "Log Analytics Reader" -Scope "/subscriptions/{subId}"
Code
My cred_wrapper.py
from msrest.authentication import BasicTokenAuthentication
from azure.core.pipeline.policies import BearerTokenCredentialPolicy
from azure.core.pipeline import PipelineRequest, PipelineContext
from azure.core.pipeline.transport import HttpRequest
from azure.identity import DefaultAzureCredential
class CredentialWrapper(BasicTokenAuthentication):
def __init__(self, credential=None, resource_id="https://westus2.api.loganalytics.io/.default", **kwargs):
"""Wrap any azure-identity credential to work with SDK that needs azure.common.credentials/msrestazure.
Default resource is ARM (syntax of endpoint v2)
:param credential: Any azure-identity credential (DefaultAzureCredential by default)
:param str resource_id: The scope to use to get the token (default ARM)
"""
super(CredentialWrapper, self).__init__(None)
if credential is None:
#credential = DefaultAzureCredential()
credential = DefaultAzureCredential()
self._policy = BearerTokenCredentialPolicy(
credential, resource_id, **kwargs)
def _make_request(self):
return PipelineRequest(
HttpRequest(
"CredentialWrapper",
"https://fakeurl"
),
PipelineContext(None)
)
def set_token(self):
"""Ask the azure-core BearerTokenCredentialPolicy policy to get a token.
Using the policy gives us for free the caching system of azure-core.
We could make this code simpler by using private method, but by definition
I can't assure they will be there forever, so mocking a fake call to the policy
to extract the token, using 100% public API."""
request = self._make_request()
self._policy.on_request(request)
# Read Authorization, and get the second part after Bearer
token = request.http_request.headers["Authorization"].split(" ", 1)[1]
self.token = {"access_token": token}
def signed_session(self, session=None):
self.set_token()
return super(CredentialWrapper, self).signed_session(session)
My function code
import logging
from azure.loganalytics import LogAnalyticsDataClient
from .cred_wrapper import CredentialWrapper
import azure.functions as func
from azure.loganalytics.models import QueryBody
import json
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function processed a request.')
creds = CredentialWrapper()
client = LogAnalyticsDataClient(creds)
result = client.query(workspace_id='',
body=QueryBody(query='Heartbeat | take 10'))
return func.HttpResponse(
json.dumps(result.tables[0].rows),
status_code=200
)

How to access authentication by Strava API using Python?

I am starting a small python script (not an application) that can upload my *.fit activity files on Strava whenever they are created in a desired folder.
The main steps I plan to do are:
1. monitor *.fit file system modifications
2. access authentication to Strava to enable my program to upload files
(This tool will be personal use only, thus I expect no need to authenticate every time uploading)
3. upload the file to my Strava account
4. automatically doing this fixed routine with the help of Windows Task Scheduler
(For example, there will be 4-5 new riding activities generated in my computer folder, I expect this tool can automatically upload all of them once a week so that I do not need to manually complete the task.)
For step2, I really have no ideas how to implement even though reading through Strava Authentication Documentation and several source codes other peoples have developed (e.g. toravir's "rk2s (RunKeeper 2 Strava)" project on GitHub). I grabbed that some of the python modules like stravalib, swagger_client, request, json, etc. as well as concepts like OAuth2 may be related to step2 but I still can not put everything together...
Can any experienced give me some advice for the implementations of step2? or any related readings will be perfect!
Advice for other parts of this project will also be very welcomed and appreciated.
I thank you very much in advance:)
This is a code example on how you can access the Strava API, check out this gist or use the code below:
import time
import pickle
from fastapi import FastAPI
from fastapi.responses import RedirectResponse
from stravalib.client import Client
CLIENT_ID = 'GET FROM STRAVA API SITE'
CLIENT_SECRET = 'GET FROM STRAVA API SITE'
REDIRECT_URL = 'http://localhost:8000/authorized'
app = FastAPI()
client = Client()
def save_object(obj, filename):
with open(filename, 'wb') as output: # Overwrites any existing file.
pickle.dump(obj, output, pickle.HIGHEST_PROTOCOL)
def load_object(filename):
with open(filename, 'rb') as input:
loaded_object = pickle.load(input)
return loaded_object
def check_token():
if time.time() > client.token_expires_at:
refresh_response = client.refresh_access_token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, refresh_token=client.refresh_token)
access_token = refresh_response['access_token']
refresh_token = refresh_response['refresh_token']
expires_at = refresh_response['expires_at']
client.access_token = access_token
client.refresh_token = refresh_token
client.token_expires_at = expires_at
#app.get("/")
def read_root():
authorize_url = client.authorization_url(client_id=CLIENT_ID, redirect_uri=REDIRECT_URL)
return RedirectResponse(authorize_url)
#app.get("/authorized/")
def get_code(state=None, code=None, scope=None):
token_response = client.exchange_code_for_token(client_id=CLIENT_ID, client_secret=CLIENT_SECRET, code=code)
access_token = token_response['access_token']
refresh_token = token_response['refresh_token']
expires_at = token_response['expires_at']
client.access_token = access_token
client.refresh_token = refresh_token
client.token_expires_at = expires_at
save_object(client, 'client.pkl')
return {"state": state, "code": code, "scope": scope}
try:
client = load_object('client.pkl')
check_token()
athlete = client.get_athlete()
print("For {id}, I now have an access token {token}".format(id=athlete.id, token=client.access_token))
# To upload an activity
# client.upload_activity(activity_file, data_type, name=None, description=None, activity_type=None, private=None, external_id=None)
except FileNotFoundError:
print("No access token stored yet, visit http://localhost:8000/ to get it")
print("After visiting that url, a pickle file is stored, run this file again to upload your activity")
Download that file, install the requirements, and run it (assuming the filename is main):
pip install stravalib
pip install fastapi
pip install uvicorn
uvicorn main:app --reload
I believe you need to authenticate using OAuth in order to upload your activity, which pretty much requires you to have a web server setup that Strava can post back to after you "Authorize". I just set the authentication piece up using Rails & Heroku.
This link has a pretty good flowchart of what needs to happen.
https://developers.strava.com/docs/authentication/
Actually it looks like if you go to API Settings you can get your access token and refresh token there. I would also check out the Python Strava Library but it looks like you could do something like:
from stravalib.client import Client
access_token = 'your_access_token_from_your_api_application_settings_page'
refresh_token = 'your_refresh_token_from_your_api_application_settings_page'
client = Client()
athlete = client.get_athlete()
You may need to dig in a little more to that library to figure out the upload piece.

Python REST API gives Internal Server Error

I'm trying to create a REST API in python .. I have decent experience in python but relatively new to REST APIs ... When I run my python script I get a "Internal Server Error" on my browser ..
Some thing like this:
Error on the browser
and on my console I see this:
Error as displayed on my console
Here's my code:
from flask import Flask, request
from flask_restful import Resource, Api
import firebase_admin
# For connecting to firestore database and authentication
from firebase_admin import credentials, firestore
# For Data base Connectivity
from firebase_admin import db
from flask import jsonify
app = Flask(__name__)
api = Api(app)
class Firebase_Data(Resource):
def getData(self):
# Setting up credentials to connect
cred = credentials.Certificate(../Path)
# Setting up secure connection to firestore Real time database
app = firebase_admin.initialize_app(cred, {
'projectId' : 'project_ID'
})
# Connecting to the firestore client
db_ref = firestore.client()
# Referring to the '** section of the data
ref_inc = db_ref.collection(u'name of column')
# Fetching all the records under that particular section and
converting them to list of dictionaries
docs = list( ref_inc.get() )
lat_long = []
for doc in docs:
data = doc.to_dict()
lat_long.append(
{ 'Latitude:' : data['latitude'], 'Longitude' :
data['longitude'] } )
return jsonify(lat_long)
api.add_resource(Firebase_Data, '/Firebase_Data') # Route_1
if __name__ == '__main__':
app.run(port=5002)
I'm basically trying to fetch some data from a Fire store database and display it on the browser. I don't think the fire store part has anything to do with my error, I think I'm missing something on executing the "get" function of my python class which I'm not able to figure out .. Any help is highly appreciated.. Thanks in advance

Using OAuth to authenticate Office 365/Graph users with Django

We are creating an application for use in our organization, but we only want people in our organization to be able to use the app. We had the idea of using Microsoft's OAuth endpoint in order to authenticate whether a user is part of our org or not. The idea is to bring up a sign in screen where the user can enter their Office 365 username and password, which will then allow them to use our app upon submission of their credentials.
Our app is running on Django, and I've only found a solution to this problem using Flask and Microsoft's Graph API connect sample for Python (See code snippet below). This sample uses a similar idea to the one above to log in to the app. Are there any similar methods of authentication for Django?
import requests
from flask import Flask, redirect, url_for, session, request, render_template
from flask_oauthlib.client import OAuth
# read private credentials from text file
client_id, client_secret, *_ = open('_PRIVATE.txt').read().split('\n')
if (client_id.startswith('*') and client_id.endswith('*')) or \
(client_secret.startswith('*') and client_secret.endswith('*')):
print('MISSING CONFIGURATION: the _PRIVATE.txt file needs to be edited ' + \
'to add client ID and secret.')
sys.exit(1)
app = Flask(__name__)
app.debug = True
app.secret_key = 'development'
oauth = OAuth(app)
# since this sample runs locally without HTTPS, disable InsecureRequestWarning
requests.packages.urllib3.disable_warnings()
msgraphapi = oauth.remote_app( \
'microsoft',
consumer_key=client_id,
consumer_secret=client_secret,
request_token_params={'scope': 'User.Read Mail.Send'},
base_url='https://graph.microsoft.com/v1.0/',
request_token_url=None,
access_token_method='POST',
access_token_url='https://login.microsoftonline.com/common/oauth2/v2.0/token',
authorize_url='https://login.microsoftonline.com/common/oauth2/v2.0/authorize'
)
#app.route('/login')
def login():
"""Handler for login route."""
guid = uuid.uuid4() # guid used to only accept initiated logins
session['state'] = guid
return msgraphapi.authorize(callback=url_for('authorized', _external=True), state=guid)
#app.route('/login/authorized')
def authorized():
"""Handler for login/authorized route."""
response = msgraphapi.authorized_response()
if response is None:
return "Access Denied: Reason={0}\nError={1}".format( \
request.args['error'], request.args['error_description'])
# Check response for state
if str(session['state']) != str(request.args['state']):
raise Exception('State has been messed with, end authentication')
session['state'] = '' # reset session state to prevent re-use
# Okay to store this in a local variable, encrypt if it's going to client
# machine or database. Treat as a password.
session['microsoft_token'] = (response['access_token'], '')
# Store the token in another session variable for easy access
session['access_token'] = response['access_token']
me_response = msgraphapi.get('me')
me_data = json.loads(json.dumps(me_response.data))
username = me_data['displayName']
email_address = me_data['userPrincipalName']
session['alias'] = username
session['userEmailAddress'] = email_address
return redirect('main')
You should be able to use just about any OAUTH 2.0 library for Python. I've not worked with Django but I know there are several out there for Python.
I came across django-azure-ad-auth which seems to be exactly what you're looking for.
I also found a general OAUTH library called django-allauth which seems to have a lot of activity. It doesn't have a built-in provider but the model they use for providers seems simple enough that you may be able to extend it without too much trouble.

Categories