Google Admin SDK: Get a list of groups that a user belongs to - google-admin-sdk

I see that it is possible to list all members that belong to a specific group, as documented here:
https://developers.google.com/admin-sdk/directory/v1/guides/manage-group-members#get_all_members
But is it possible to get a list of groups that a user belongs to? I was hoping that Users.get would contain this information, or the Members API would have something similar, but I don't see it.

So I've found the solution in the Developer Guide, although it does not exist in the API documentation!!
https://developers.google.com/admin-sdk/directory/v1/guides/manage-groups#get_all_member_groups

The best way to achieve this is by using the google admin sdk api.
groups.list()
groups.list() with details
For example if you want to do it using the python sdk, you can use the following
from __future__ import print_function
import logging
import os.path
import csv
import json
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
# If modifying these scopes, delete the file token.json.
### https://developers.google.com/admin-sdk/directory/v1/guides/authorizing
### https://developers.google.com/admin-sdk/directory/v1/quickstart/python
SCOPES = ['https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.group']
"""
########################################################################################################################################################
# Logging level set for the script
# https://realpython.com/python-logging/
########################################################################################################################################################
"""
logging.basicConfig(level=logging.INFO)
class GSuite_management(object):
"""
########################################################################################################################################################
# GSuite_management CLASS
# --> This class will have methods to manage the memebers of organization using Gsuite Admin SDK
########################################################################################################################################################
"""
service = None
def __init__(self):
"""
GSuite_management Constrouctor
"""
creds = None
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
token.write(creds.to_json())
self.service = build('admin', 'directory_v1', credentials=creds)
def list_all_groups_a_user_is_a_part_of(self, userEmail):
"""
This method will list all the groups a user is a part of and return them as a list
"""
listOfEmailGroups=[]
try:
results = self.service.groups().list(domain="yourdomain.com",userKey=userEmail, maxResults=400).execute()
logging.debug(results)
groups = results.get('groups', [])
if not groups:
print('No groups in the domain.')
else:
for group in groups:
logging.info(u'{0} {1} {2}'.format(group['email'],group['name'], group['directMembersCount']))
listOfEmailGroups.append(group['email'])
except Exception as e:
logging.error("Exiting!!!")
SystemExit(e)
return listOfEmailGroups

We can use any google-admin-sdk library like google-api-nodejs-client to query groups of a particular user
This method is using GET https://admin.googleapis.com/admin/directory/v1/groups?userKey=userkey endpoint linked in jekennedy's answer
/**
* Retrieve all groups for a member
*
* #param {google.auth.OAuth2} auth An authorized OAuth2 client.
* #param {string} userEmail primary email of the user
*/
function listGroupsOfMember(auth, userEmail) {
const admin = google.admin({version: 'directory_v1', auth});
admin.groups.list({
userKey: userEmail,
maxResults: 10,
orderBy: 'email',
}, (err, res) => {
if (err) {
console.log(err)
return console.error('The API returned an error:', err.message);
}
const groups = res.data.groups;
if (groups.length) {
console.log('Groups:');
groups.forEach((group) => {
console.log(group);
});
} else {
console.log('No groups found.');
}
});
}
Example usage:
const {google} = require('googleapis');
const path = require('path');
// acquire an authentication client using a service account
const auth = await google.auth.getClient({
keyFile: path.join(__dirname, '../serviceaccount.json'),
scopes: [
'https://www.googleapis.com/auth/admin.directory.user',
'https://www.googleapis.com/auth/admin.directory.group',
],
});
// impersonate super admin
auth.subject = "admin#foo.me"
listGroupsOfMember(auth, "username#foo.me")

Related

How to authenticate requests made to AWS AppSync in Python?

I have a website with a backend of AWS Amplify. For a post-payment function, I am creating a lambda function to update the database. I am trying to query certain fields with the help of AppSync and then run a mutation. This is my function code:
import json
import boto3
import os
import decimal
import requests
from requests_aws4auth import AWS4Auth
def lambda_handler(event, context):
dynamoDB = boto3.resource('dynamodb', region_name='ap-northeast-1')
// load event data (hidden)
userid = sentData.get("userid")
slots = sentData.get("slots")
url = os.environ.get("AWS_GRAPHQL_API_ENDPOINT")
api_key = os.environ.get("AWS_GRAPHQL_API_KEY")
session = requests.Session()
query = """
query MyQuery {
getUserPlan(id: "9ddf437a-55b1-445d-8ae6-254c77493c30") {
traits
traitCount
}
}
"""
credentials = boto3.session.Session().get_credentials()
session.auth = AWS4Auth(
credentials.access_key,
credentials.secret_key,
'ap-northeast-1',
'appsync',
session_token=credentials.token
)
# response = session.request(
# url=url,
# method="POST",
# json={"query": query},
# headers={"Authorization": api_key},
# )
# response = requests.post(
# url=url,
# json={"query": query},
# headers={"x-api-key": api_key}
# )
response = session.request(
url=url,
method="POST",
json={"query": query},
);
print(response.json())
return {
"statusCode": 200,
}
I get the following error when I execute the function:
{'data': {'getUserPlan': None}, 'errors': [{'path': ['getUserPlan'], 'data': None, 'errorType': 'Unauthorized', 'errorInfo': None, 'locations': [{'line': 3, 'column': 9, 'sourceName': None}], 'message': 'Not Authorized to access getUserPlan on type UserPlan'}]}
I have referred to this and this. I have tried their solutions but they haven't worked for me. I have confirmed that all the environment variables are working properly and even added the local aws-cli iam user to the custom-roles.json file for admin privileges by Amplify. When I was trying with the API Key, I made sure that it hadn't expired as well.
I figured out how to fix it. I had to create a function through the amplify-cli, give it access to the api, push the function and then add the name of the role to adminRoleNames in custom-roles.json

Unit testing with AWS Cognito and GraphQL

I'm currently writing tests for my software but got stuck at the point.
I try to get data from my db with a normal GraphQL Query but my endpoint is first checking, if the idToken within the header is valid.
For the user handling I'm using AWS Cognito but couldn't find a good way to mock the login to retrieve the valid token to query and mutate the data within various endpoints.
Any idea how to handle this case?
Here is my code from the graphene docs (https://docs.graphene-python.org/projects/django/en/latest/testing/):
# Create a fixture using the graphql_query helper and `client` fixture from `pytest-django`.
import json
import pytest
from graphene_django.utils.testing import graphql_query
# https://docs.graphene-python.org/projects/django/en/latest/testing/
#pytest.fixture
def client_query(client):
def func(*args, **kwargs):
return graphql_query(*args, **kwargs, client=client)
return func
# Test you query using the client_query fixture
def test_some_query(client_query):
response = client_query(
'''
query GetAllProjectConfig{
getAllProjectConfig{
project{
id
slug
name
}
config{
id
}
}
}
''',
)
content = json.loads(response.content)
assert 'errors' not in content
The answer was not so hard:
auth_data = {'USERNAME': username, 'PASSWORD': password}
# auth the user on cognito
def auth_cognito_user():
provider_client = boto3.client(
'cognito-idp', region_name=os.environ.get('region_name'))
resp = provider_client.admin_initiate_auth(
UserPoolId=userpool_id, AuthFlow='ADMIN_NO_SRP_AUTH', AuthParameters=auth_data, ClientId=client_id)
# print("RESPONSE COGNITO", resp['AuthenticationResult']['IdToken'])
return resp['AuthenticationResult']['IdToken']

Webhook Payload Result is null in Dialogflow but not in Postman or Locally

I am failing to send over my personal banking data via a flask webhook from the Nordigen API to Dialogflow via fulfilment as only null is being received within the Dialogflow payload:
{
"fulfillmentText": "Your text response",
"fulfillmentMessages": [
{
"payload": [
null
]
}
]
}
The webhook error message is: Webhook call failed. Error: Failed to parse webhook JSON response: Expect a map object but found: [null].
When I just send the data as a fulfillmentText I receive "fulfillmentText": null.
I have tested my webhook with postman and there - as well as locally and other webhook'esque tests - everything is fine as I receive my most recent banking data.
The overall flow is twofold and simple:
User gets the correct banking and user specific login link to a specified bank, copy & pastes it to perform banking login by query_text = 'login'.
After a successful banking login the user can fetch different kinds of banking data (like balance) by query_text = 'balance'.
I went crazy with overengineering the flask webhook as I tried out many different things like asynchronous functions, uploading my Flask app to Heroku or CORS. I have even implemented an OAuth2 process where the user would query_text = 'google auth' and initiate the OAuth2 process in a step 0) by creating OAuth2 credentials and the Flask-Dance Python package. (Even though I have hardcoded the OAuth2 redirect link but this shouldn't be an issue atm). I was even trying to trick Dialogflow by creating a small Sqlite3 db within my webhook to at least upload the data there and then use it but without success.
So my question is .. what am I missing here? Why do I receive my banking data everywhere else but not in Dialogflow. My intuition is telling me Google is blocking this data for whatever reason.
Honestly I just don't know how to continue and I would appreciate any helpful comments!
This is my Flask webhook:
from dialogflow_fulfillment import QuickReplies, WebhookClient, Payload
from flask import Flask, request, jsonify, make_response, session, render_template, redirect, url_for
from flask_cors import CORS, cross_origin
import json
from json import JSONEncoder
import os
import asyncio
import requests
import sqlite3
from app.src.handler_login import handler_login
from app.src.handler_balance import handler_balance
from app.banking_data.init_db import create_connection
from flask_dance.contrib.google import make_google_blueprint, google
from oauthlib.oauth2.rfc6749.errors import InvalidGrantError, TokenExpiredError, OAuth2Error
from google.cloud import dialogflow_v2beta1 as dialogflow
from google.oauth2 import service_account
from uuid import uuid4
from nordigen import NordigenClient
# NORDIGEN
# Credentials
secret_id="XXX"
secret_key="XXX"
# Configuration
institution_id = "XXX"
app = Flask(__name__)
# set Flask secret key
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "supersekrit")
# GOOGLE API & AUTHENTICATION
app.config["GOOGLE_OAUTH_CLIENT_ID"] = "XXX"
app.config["GOOGLE_OAUTH_CLIENT_SECRET"] = "XXX"
os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = "1"
os.environ['OAUTHLIB_RELAX_TOKEN_SCOPE'] = "1"
google_bp = make_google_blueprint(scope=["profile", "email"])
app.register_blueprint(google_bp, url_prefix="/login")
app.config['CORS_HEADERS'] = 'Content-Type'
cors = CORS(app, supports_credentials=True, resources={r"/webhook": {"origins": "*"}})
client = NordigenClient(
secret_id=secret_id,
secret_key=secret_key
)
client.generate_token()
# subclass JSONEncoder
class setEncoder(JSONEncoder):
def default(self, obj):
return list(obj)
#app.route("/")
def index():
if not google.authorized:
return redirect(url_for("google.login"))
try:
resp = google.get("/oauth2/v1/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
except (InvalidGrantError, TokenExpiredError) as e: # or maybe any OAuth2Error
return redirect(url_for("google.login"))
#app.route('/webhook', methods=['GET', 'POST', 'OPTION'])
async def webhook():
"""Handles webhook requests from Dialogflow."""
req = request.get_json(force=True)
query_text = req.get('queryResult').get('queryText')
if query_text:
if query_text == 'google auth':
if not google.authorized:
auth_link = 'MY HARD CODED GOOGLE AUTHENTICATION LINK HERE'
auth_link = {
"fulfillmentText": auth_link,
"source": 'webhook'
}
return auth_link
try:
resp = google.get("/oauth2/v1/userinfo")
assert resp.ok, resp.text
return "You are {email} on Google".format(email=resp.json()["email"])
except (InvalidGrantError, TokenExpiredError) as e: # or maybe any OAuth2Error
auth_link = 'MY HARD CODED GOOGLE AUTHENTICATION LINK HERE'
auth_link = {
"fulfillmentText": auth_link,
"source": 'webhook'
}
return auth_link
if query_text == 'login':
link = await handler_login(client, institution_id, session)
link = {
"fulfillmentText": link,
"source": 'webhook'
}
link = make_response(jsonify(link))
link.headers.add('Access-Control-Allow-Origin', '*')
return link
if query_text == 'balance':
balance = await handler_balance(client, session)
balance = {
"fulfillmentText": "Your text response",
"fulfillmentMessages": [
{
"text": {
"text": [
"Your text response"
]
}
},
{
"payload": {
balance
}
}
]
}
balance = json.dumps(balance, indent=4, cls=setEncoder)
balance = make_response(balance)
return balance
if __name__ == "__main__":
app.run(debug=True)
Here are two helper functions I have created that perform the creation of the login link the the fetching of my banking data via Nordigen:
from uuid import uuid4
async def handler_login(client, institution_id, session):
"""Handles the webhook request."""
# Initialize bank session
init = client.initialize_session(
# institution id
institution_id=institution_id,
# redirect url after successful authentication
redirect_uri="https://nordigen.com",
# additional layer of unique ID defined by you
reference_id=str(uuid4())
)
link = init.link
session["req_id"] = init.requisition_id
return link
async def handler_balance(client, session):
if "req_id" in session:
# Get account id after you have completed authorization with a bank
# requisition_id can be gathered from initialize_session response
#requisition_id = init.requisition_id
accounts = client.requisition.get_requisition_by_id(
requisition_id=session["req_id"]
)
# Get account id from the list.
account_id = accounts["accounts"][0]
#account_id = accounts["id"]
# Create account instance and provide your account id from previous step
account = client.account_api(id=account_id)
# Fetch account metadata
#meta_data = account.get_metadata()
# Fetch details
#details = account.get_details()
# Fetch balances
balance = account.get_balances()
balance = balance["balances"][0]
balance = balance["balanceAmount"]["amount"]
#balance = json.loads(balance)
# Fetch transactions
#transactions = account.get_transactions()
#agent.add(Payload({'balance': balance}))
return balance
Feel free to comment if you need any more input!

Flask dance example for login with Azure AD

I am trying to implement SSO for one of my applications using flask-login and flask-dance. As a starting point I am using sample code given on Flask Dance website - https://flask-dance.readthedocs.io/en/v1.2.0/quickstarts/sqla-multiuser.html
Only change I did was - I replaced GitHub with my Azure AD credentials
Please find the code below:
import sys
from flask import Flask, redirect, url_for, flash, render_template
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm.exc import NoResultFound
from flask_dance.contrib.github import make_github_blueprint, github
from flask_dance.contrib.azure import make_azure_blueprint, azure
from flask_dance.consumer.storage.sqla import OAuthConsumerMixin, SQLAlchemyStorage
from flask_dance.consumer import oauth_authorized, oauth_error
from flask_login import (
LoginManager, UserMixin, current_user,
login_required, login_user, logout_user
)
# setup Flask application
app = Flask(__name__)
app.secret_key = "XXXXXXXXXXXXXX"
blueprint = make_azure_blueprint(
client_id="XXXXXXXXXXXXXXXXXXXXX",
client_secret="XXXXXXXXXXXXXXXXXXXXXXXX",
tenant="XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
)
app.register_blueprint(blueprint, url_prefix="/login")
# setup database models
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///multi.db"
db = SQLAlchemy()
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
# Your User model can include whatever columns you want: Flask-Dance doesn't care.
# Here are a few columns you might find useful, but feel free to modify them
# as your application needs!
username = db.Column(db.String(1028), unique=True)
email = db.Column(db.String(1028), unique=True)
name = db.Column(db.String(1028))
class OAuth(OAuthConsumerMixin, db.Model):
provider_user_id = db.Column(db.String(1028), unique=True)
user_id = db.Column(db.Integer, db.ForeignKey(User.id))
user = db.relationship(User)
# setup login manager
login_manager = LoginManager()
login_manager.login_view = 'azure.login'
#login_manager.user_loader
def load_user(user_id):
#print(User.query.get(int(user_id)))
return User.query.get(int(user_id))
# setup SQLAlchemy backend
blueprint.storage = SQLAlchemyStorage(OAuth, db.session, user=current_user,user_required=False)
# create/login local user on successful OAuth login
#oauth_authorized.connect_via(blueprint)
def azure_logged_in(blueprint, token):
if not token:
#print(token)
flash("Failed to log in with azure.", category="error")
return False
resp = blueprint.session.get("/user")
if not resp.ok:
#print(resp)
msg = "Failed to fetch user info from Azure."
flash(msg, category="error")
return False
azure_info = resp.json()
azure_user_id = str(azure_info["id"])
#print(azure_user_id)
# Find this OAuth token in the database, or create it
query = OAuth.query.filter_by(
provider=blueprint.name,
provider_user_id=azure_user_id,
)
try:
oauth = query.one()
except NoResultFound:
oauth = OAuth(
provider=blueprint.name,
provider_user_id=azure_user_id,
token=token,
)
if oauth.user:
login_user(oauth.user)
flash("Successfully signed in with Azure.")
else:
# Create a new local user account for this user
user = User(
# Remember that `email` can be None, if the user declines
# to publish their email address on GitHub!
email=azure_info["email"],
name=azure_info["name"],
)
# Associate the new local user account with the OAuth token
oauth.user = user
# Save and commit our database models
db.session.add_all([user, oauth])
db.session.commit()
# Log in the new local user account
login_user(user)
flash("Successfully signed in with Azure.")
# Disable Flask-Dance's default behavior for saving the OAuth token
return False
# notify on OAuth provider error
#oauth_error.connect_via(blueprint)
def azure_error(blueprint, error, error_description=None, error_uri=None):
msg = (
"OAuth error from {name}! "
"error={error} description={description} uri={uri}"
).format(
name=blueprint.name,
error=error,
description=error_description,
uri=error_uri,
)
flash(msg, category="error")
#app.route("/logout")
#login_required
def logout():
logout_user()
flash("You have logged out")
return redirect(url_for("index"))
#app.route("/")
def index():
return render_template("home.html")
# hook up extensions to app
db.init_app(app)
login_manager.init_app(app)
if __name__ == "__main__":
if "--setup" in sys.argv:
with app.app_context():
db.create_all()
db.session.commit()
print("Database tables created")
else:
app.run(debug=True,port=5011)
I have also done appropriate changes in HTML file for 'azure.login'.
So after running it as python multi.py --setup database tables are getting created
and after I run python multi.py Oauth dance is actually starting but in the end I am getting error like below:
HTTP Response:
127.0.0.1 - - [28/Oct/2020 10:17:44] "?[32mGET /login/azure/authorized?code=0.<Token>HTTP/1.1?[0m" 302 -
127.0.0.1 - - [28/Oct/2020 10:17:44] "?[37mGET / HTTP/1.1?[0m" 200 -
Am I missing something? Is it a good idea to use Flask Dance and Flask Login to have SSO with Azure AD? Or I should go with MSAL only along with Flask Session?
Kindly give your valuable inputs..
Since you use Azure AD as the Flask dance provider, we need to use Microsoft Graph to get user's information. The URL should be https://graph.microsoft.com/v1.0/me. So please update the code resp = blueprint.session.get("/user") to resp = blueprint.session.get("/v1.0/me") in method azure_logged_in. Besides, please note that the azure ad user's information has different names. We also need to update the code about creating users.
For example
#oauth_authorized.connect_via(blueprint)
def azure_logged_in(blueprint, token):
if not token:
# print(token)
flash("Failed to log in with azure.", category="error")
return False
resp = blueprint.session.get("/v1.0/me")
# azure.get
if not resp.ok:
# print(resp)
msg = "Failed to fetch user info from Azure."
flash(msg, category="error")
return False
azure_info = resp.json()
azure_user_id = str(azure_info["id"])
# print(azure_user_id)
# Find this OAuth token in the database, or create it
query = OAuth.query.filter_by(
provider=blueprint.name,
provider_user_id=azure_user_id,
)
try:
oauth = query.one()
except NoResultFound:
oauth = OAuth(
provider=blueprint.name,
provider_user_id=azure_user_id,
token=token,
)
if oauth.user:
login_user(oauth.user)
flash("Successfully signed in with Azure.")
else:
# Create a new local user account for this user
user = User(
# create user with user information from Microsoft Graph
email=azure_info["mail"],
username=azure_info["displayName"],
name=azure_info["userPrincipalName"]
)
# Associate the new local user account with the OAuth token
oauth.user = user
# Save and commit our database models
db.session.add_all([user, oauth])
db.session.commit()
# Log in the new local user account
login_user(user)
flash("Successfully signed in with Azure.")
# Disable Flask-Dance's default behavior for saving the OAuth token
return False
For more details, please refer to here and here

Zoho CRM Python SDK v2 initialization problem for Django

Im trying to integrate the Zoho CRM v2 SDK with my Django app.
On the Django runserver, im able to get access tokens and using the refresh method and store them in the zcrm_oauthtokens.pkl file. The sdk then automatically refreshes the access token using the refresh token, so no problem here. However on my production server (heroku) im getting this error message:
2019-01-16T11:07:22.314759+00:00 app[web.1]: 2019-01-16 11:07:22,314 - Client_Library_OAUTH - ERROR - Exception occured while fetching oauthtoken from db; Exception Message::'NoneType' object has no attribute 'accessToken'
It seems to me that the tokens are being saved to file, but when the sdk try to access them it is looking for them in a DB and not the file specified in the token_persistence_path.
In my settings.py I have this:
ZOHO_CLIENT_ID = config('ZOHO_CLIENT_ID')
ZOHO_CLIENT_SECRET = config('ZOHO_CLIENT_SECRET')
ZOHO_REDIRECT_URI = config('ZOHO_REDIRECT_URI')
ZOHO_CURRENT_USER_EMAIL = 'jamesalexander#mylastwill.co.uk'
ZOHO_PATH = os.path.join(BASE_DIR, 'wills_online', 'zoho')
zoho_config = {'apiBaseUrl': "https://www.zohoapis.com",
'currentUserEmail': ZOHO_CURRENT_USER_EMAIL,
'client_id': ZOHO_CLIENT_ID,
'client_secret': ZOHO_CLIENT_SECRET,
'redirect_uri': ZOHO_REDIRECT_URI,
'token_persistence_path': ZOHO_PATH}
and in a views file I have this:
from zcrmsdk import *
import logging
from django.shortcuts import HttpResponse
from wills.models import PersonalDetails, ZoHoRecord, WillDocument
from wills_online.decorators import start_new_thread
from wills_online.settings import zoho_config
logger = logging.getLogger(__name__)
class ZohoRunOnce:
def __init__(self):
self.already_run = False
def run_once(self):
if not self.already_run:
print('zoho init run once')
ZCRMRestClient.initialize(zoho_config)
self.already_run = True
zoho_init = ZohoRunOnce()
zoho_init.run_once()
print(zoho_config['token_persistence_path'])
def zoho_callback():
return HttpResponse(200)
#start_new_thread
def zoho_personal_details(request):
""" updates or create a user account on zoho on profile completion """
personal_details_ob = PersonalDetails.objects.get(user=request.user)
zoho_ob = ZoHoRecord.objects.get(user=request.user)
try:
if zoho_ob.account:
record = ZCRMRecord.get_instance('Accounts', zoho_ob.account)
record.set_field_value('Account_Name', request.user.email)
record.set_field_value('Name', personal_details_ob.full_name)
record.set_field_value('Email', request.user.email)
record.set_field_value('Address_Line_1', personal_details_ob.address_line_1)
record.set_field_value('Address_Line_2', personal_details_ob.address_line_2)
record.set_field_value('Post_Town', personal_details_ob.post_town)
record.set_field_value('Post_Code', personal_details_ob.post_code)
record.set_field_value('Dob_Day', personal_details_ob.dob_day)
record.set_field_value('Dob_Month', personal_details_ob.dob_month)
record.set_field_value('Dob_Year', personal_details_ob.dob_year)
record.set_field_value('Gender', personal_details_ob.sex)
record.set_field_value('Marital_Status', personal_details_ob.marital_status)
record.set_field_value('Partner_Name', personal_details_ob.partner_full_name)
record.set_field_value('Partner_Gender', personal_details_ob.partner_gender)
record.set_field_value('Partner_Email', personal_details_ob.partner_email)
record.set_field_value('Children', personal_details_ob.children)
record.set_field_value('Pets', personal_details_ob.pets)
record.update()
else:
user = ZCRMUser.get_instance(name='James Alexander')
record = ZCRMRecord.get_instance('Accounts')
record.set_field_value('Account_Owner', user)
record.set_field_value('Account_Name', request.user.email)
record.set_field_value('Name', personal_details_ob.full_name)
record.set_field_value('Email', request.user.email)
record.set_field_value('Address_Line_1', personal_details_ob.address_line_1)
record.set_field_value('Address_Line_2', personal_details_ob.address_line_2)
record.set_field_value('Post_Town', personal_details_ob.post_town)
record.set_field_value('Post_Code', personal_details_ob.post_code)
record.set_field_value('Dob_Day', personal_details_ob.dob_day)
record.set_field_value('Dob_Month', personal_details_ob.dob_month)
record.set_field_value('Dob_Year', personal_details_ob.dob_year)
record.set_field_value('Gender', personal_details_ob.sex)
record.set_field_value('Marital_Status', personal_details_ob.marital_status)
record.set_field_value('Partner_Name', personal_details_ob.partner_full_name)
record.set_field_value('Partner_Gender', personal_details_ob.partner_gender)
record.set_field_value('Partner_Email', personal_details_ob.partner_email)
record.set_field_value('Children', personal_details_ob.children)
record.set_field_value('Pets', personal_details_ob.pets)
response = record.create()
# save account id to db for future updates
zoho_ob.account = response.details['id']
zoho_ob.save()
except ZCRMException as ex:
logger.log(1, ex.status_code)
logger.log(1, ex.error_message)
logger.log(1, ex.error_details)
logger.log(1, ex.error_content)
print(ex.status_code)
print(ex.error_message)
print(ex.error_content)
print(ex.error_details)
Ive tried running ZCRMRestClient.initialize(zoho_config) in settings.py, with no luck.
My method for getting the access token and refresh token, which seems to work is:
import os
import pprint
from sys import argv
import django
import requests
import zcrmsdk
from django.conf import settings
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'wills_online.settings')
django.setup()
def zoho_refresh_token(code):
""" supply a self client token from the zoho api credentials from web site """
zoho_config = {"apiBaseUrl": "https://www.zohoapis.com",
"currentUserEmail": settings.ZOHO_CURRENT_USER_EMAIL,
"client_id": settings.ZOHO_CLIENT_ID,
"client_secret": settings.ZOHO_CLIENT_SECRET,
"redirect_uri": settings.ZOHO_REDIRECT_URI,
"token_persistence_path": settings.ZOHO_PATH}
pprint.pprint(zoho_config)
print('working')
address = f'https://accounts.zoho.com/oauth/v2/token?code={code}&redirect_uri={settings.ZOHO_REDIRECT_URI}&client_id={settings.ZOHO_CLIENT_ID}&client_secret={settings.ZOHO_CLIENT_SECRET}&grant_type=authorization_code'
response = requests.post(address)
data = response.json()
pprint.pprint(data)
zcrmsdk.ZCRMRestClient.initialize(zoho_config)
oauth_client = zcrmsdk.ZohoOAuth.get_client_instance()
refresh_token = data['refresh_token']
print(type(refresh_token))
oauth_client.generate_access_token_from_refresh_token(refresh_token, settings.ZOHO_CURRENT_USER_EMAIL)
print(refresh_token)
print('finished')
if name == 'main':
zoho_refresh_token(argv[1])
This is driving me mad. Help would be greatly appreciated. This is my first post so go easy, lol.
For future reference, you will need to define persistence_handler_class and persistence_handler_path in your configuration dictionary. You will also need a handler class and a user-defined model to store the results. Sample code follows:
# settings.py
import zcrmsdk
configuration_dictionary = {
'apiBaseUrl': 'https://www.zohoapis.com',
'apiVersion': 'v2',
'currentUserEmail': ZOHO_CURRENT_USER_EMAIL,
'sandbox': 'False',
'applicationLogFilePath': '',
'client_id': ZOHO_CLIENT_ID,
'client_secret': ZOHO_CLIENT_SECRET,
'redirect_uri': ZOHO_REDIRECT_URI,
'accounts_url': 'https://accounts.zoho.com',
'access_type': 'online',
'persistence_handler_class': ZOHO_HANDLER_CLASS,
'persistence_handler_path': ZOHO_HANDLER_PATH,
}
zcrmsdk.ZCRMRestClient.initialize(configuration_dictionary)
# zoho.models.py
from django.db import models
from zcrmsdk.OAuthClient import ZohoOAuthTokens
class ZohoOAuthHandler:
#staticmethod
def get_oauthtokens(email_address):
oauth_model_instance = ZohoOAuth.objects.get(user_email=email_address)
return ZohoOAuthTokens(oauth_model_instance.refresh_token,
oauth_model_instance.access_token,
oauth_model_instance.expiry_time,
user_email=oauth_model_instance.user_email)
#staticmethod
def save_oauthtokens(oauth_token):
defaults = {
'refresh_token': oauth_token.refreshToken,
'access_token': oauth_token.accessToken,
'expiry_time': oauth_token.expiryTime,
}
ZohoOAuth.objects.update_or_create(user_email=oauth_token.userEmail, defaults=defaults)
class ZohoOAuth(models.Model):
refresh_token = models.CharField(max_length=250)
access_token = models.CharField(max_length=250)
expiry_time = models.BigIntegerField()
user_email = models.EmailField()
In this example ZOHO_HANDLER_CLASS = 'ZohoOAuthHandler' and ZOHO_HANDLER_PATH = 'zoho.models'
The first time you go to use this you will need a grant_token from https://accounts.zoho.com/developerconsole. For the scope use aaaserver.profile.READ,ZohoCRM.modules.ALL to start (see https://www.zoho.com/crm/developer/docs/api/oauth-overview.html#scopes)
Before you can use the api you'll need to run the code below in a django shell. This uses a grant token to generate your initial access and refresh tokens. Afterwards, the api should handle refreshing your access token.
grant_token = GRANT_TOKEN
import zcrmsdk
oauth_client = zcrmsdk.ZohoOAuth.get_client_instance()
oauth_tokens = oauth_client.generate_access_token(grant_token)