editied original question:
Im trying to make a google appengine app which uses the g+ avatar and human name...
...
So it seems that i need the google-api-python-client library in my app.
...
to enable access to the profile scope so i can look up 'me' and grab the users name and avatar and chuck them in a couple of properties in my user objects (with a button to reload the values again or something).
So has anyone does this? Or has a working example (or even a pointer to which of the ways to authorise my app for scope=[profile])?
Discoveries:
I dont need the google-api-python-client library for this. The simple approach was to do the g+ access in pure js on the client and then lookup and push the results to my appengine app. It isnt as secure as doing via the backend, but it is only for displayname and icon (which can be set manually anyway).
I did need to make some other tweaks to make it work though...
following this workflow:
https://developers.google.com/+/web/signin/javascript-flow
Important things to note:
step1 should also state that you MUST fill out "APIs & auth" -> "Consent screen" field "PRODUCT NAME" and "EMAIL ADDRESS" or you get weird errors
You (might) have to do this before you generate the credential (or delete and recreate it)
(credit to answer: Error: invalid_client no application name)
set meta google-signin-scope to "profile" (or maybe "email")
remove the meta header for google-signin-requestvisibleactions (otherwise i got a frame sameorigin error)
obviously the button line from step4 needs to go after the body tag in your document
skip step2, the code from step2 is also included in step4
also on the workflow page, the 'working example' button on that page does not work (dont try it)
Once i did that I could put the following in the successful callback code and do a lookup:
gapi.client.load('plus','v1', function(){
var request = gapi.client.plus.people.get({ 'userId': 'me' });
request.execute(function(resp) {
console.log('Retrieved profile for:' + resp.displayName);
console.log(resp);
console.log(resp.result);
console.log(resp.result.displayName);
console.log(resp.result.image);
});
});
you can see here full example on how to use client library
https://code.google.com/p/google-api-python-client/source/browse/samples/plus/plus.py
i see a snippet of the code stating
try:
person = service.people().get(userId='me').execute()
print 'Got your ID: %s' % person['displayName']
https://developers.google.com/+/api/latest/people#resource
so basically person['image']['url'] will be your path to user's avatar.
full folder: https://code.google.com/p/google-api-python-client/source/browse/samples/plus/
Related
I have pushed my django project onto pythonanywhere, a free hosting website.
The code works fine in my local and onto the web as well, but somehow have an issue with instagram authentication.
Instagram uses an oauth2 authentication system, in which when the user allows your application, it redirects the user to a specified URI and appends the code inside the URL which further can be used to authenticate the user.
Instagram doesn't allow localhosts as valid redirect URL, therefore for the testing I have used something like this
InstaAuth.html
<script>
history.pushState(null, null, '?code=AQD_4...q6#_');
function reloadThePage() {
window.location.reload();
}
</script>
<button type="submit" class="btn btn-primary" onclick="reloadThePage()">Continue</button>
which changes the state of the url but doesn't actually refresh it. Then I have button which refresh the current page, which directly trigger below code in my views.py and gets the code through request.GET['code'].
Views.py
def instaAuth(request):
if 'code' in dict(request.GET).keys():
code = request.GET['code']
print('the given code is', code)
core = instaCore()
user_id, short_lived_access_token = core.code_to_short_access_with_userid(code_string=code)
print(user_id)
obj = InstaModel.objects.get(username=request.user)
obj.instagram_userid = user_id
obj.short_lived_access_token = short_lived_access_token
obj.is_active = True
obj.save()
print('all data saved!')
messages.success(request, "Your instagram is connected!")
return render(request, 'authentication/success.html')
return render(request, 'authentication/instaAuth.html')
Above code works perfectly fine when I add the code using pushState mehod and refresh the page using button. but when I do the same in my webapp, authorising the app and then clicking continue button, it throughs the KeyError at /instaAuth 'access_token'.
This error usually occurs when someone tries to use the same oauth code more than once. When I looked into the logs I found the error in the same line where I was exchanging the oauth code with the access token. I tried to look into the network tab of the requests, but I am not sure what is missing. I have done similar thing using streamlit and it worked fine, you can check the streamlit app here https://instalogintest.herokuapp.com/
I am stuck at this place, I want to have some logic which either doesn't refresh the page or a way that django knows that this request is coming from Instagram and verify the user using the code.
Problem solved after going through everything for a whole day. Can't believe it was such a small mistake.
I didn't change the redirect uri when I was exchanging the code with access_token.
The problem was I was not printing the error, it always through try and error block, will never do the same again.
Always use this syntax
try
do_something()
except Exception as e:
print(e)
do_something_else()
I've found many examples of using the Office365-REST-Python-Client however, none of them are correctly obtaining the access token. I've registered an app under the Azure Portal, granted it API permissions using 'Application permissions', created a secret and used the client_secret and client_id in my settings dictionary to use in the below code.
def read_folder_and_files(context, list_title):
"""Read a folder example"""
list_obj = context.web.lists.get_by_title(list_title)
folder = list_obj.root_folder
context.load(folder)
context.execute_query()
print("List url: {0}".format(folder.properties["ServerRelativeUrl"]))
files = folder.files
context.load(files)
context.execute_query()
for cur_file in files:
print("File name: {0}".format(cur_file.properties["Name"]))
folders = context.web.folders
context.load(folders)
context.execute_query()
for folder in folders:
print("Folder name: {0}".format(folder.properties["Name"]))
if __name__ == '__main__':
ctx_auth = AuthenticationContext(url=settings['url'])
if ctx_auth.acquire_token_for_app(client_id=settings['client_credentials']['client_id'],
client_secret=settings['client_credentials']['client_secret']):
ctx = ClientContext(settings['url'], ctx_auth)
read_folder_and_files(ctx, "Documents")
# read_folder_and_files_alt(ctx, "Documents")
# upload_file_into_library(target_library, name, content)
# download_file(ctx)
else:
print(ctx_auth.get_last_error())
When I run the above code I get the following error:
File "/usr/local/lib/python3.7/site-packages/office365/runtime/auth/acs_token_provider.py", line 76, in get_authorization_header
return 'Bearer {0}'.format(self.access_token["access_token"])
KeyError: 'access_token'
My end goal is to upload files to a Sharepoint Document Libary with metadata from a python data pipeline. Sharepoint is not hosted locally and is included in our 365 licences.
Kind Regards
So it looks like this error can happen when you're not getting an access token.
I fixed this by ditching the client and secret in my Azure Portal and instead generated them in the SharePoint site under the following URL:
URL: https://[tenant].sharepoint.com/_layouts/15/appregnew.aspx
To find out what you should use in the space of [tenant] look at your SharePoint URL and pick out the text between 'https://' and '.sharepoint.com'. This is assuming your SharePoint is hosted by Microsoft.
Click the generate buttons, use a relevant Title and unless you know better just enter localhost for the App Domain and Redirect URL. (My project is just a simple upload script). Take a copy of the Client ID and Secret.
If you want your App to have full access then navigate to:
https://[tenant]-admin.sharepoint.com/_layouts/15/appinv.aspx
There is another link 'https://[tenant].sharepoint.com/_layouts/15/appinv.aspx' but this won't let you apply for full control permissions.
Paste in the client id into the App id, where would be the fun in using the same field name, or linking the form together? Click lookup and use the below XML to grant full control.
<AppPermissionRequests AllowAppOnlyPolicy="true">
<AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
</AppPermissionRequests>
Click create and on the next page click trust. You will need to be logged in as a site owner with full admin permissions to grant this control.
I found all this info here which goes into more detail if you need it:
https://www.anupams.net/app-only-policy-with-tenant-level-permissions-in-sharepoint-online/
Bonus Info:
Our Sharepoint has two 'sites' so passing the base URL of 'https://[tenant].sharepoint.com' took me to the wrong site by default and meant that the document libraries I was looking didn't exist. To fix this using Office365-REST-Python-Client in your settings dictionary make sure the URL setting has the fill path to your site like this:
https://[tenant]-admin.sharepoint.com/sites/[site]
Hope this helps, this info cost me far to much time!
I'm trying to document an already existing python API with Swagger. I wrote the swagger.yaml with every route documented with the help of their editor. Now i would like to deploy the documentation using connexion.
(brief example of the swagger.yaml file)
swagger: "2.0"
info:
description: "This is a description of the routes of the API"
version: "1.0.0"
title: "API"
basePath: "/api"
paths:
/home:
get:
tags:
- "API"
summary: "Home of the application"
operationId: home
responses:
200:
description: "Success"
schema:
type: object
properties:
user_id:
type: string
username:
type: string
403:
description: "Limit of api connections overrun"
I changed the Flask app by a connexion.app during the launch of the server, and was able to specify the .yaml file. But when i'm trying to launch it, it crashes instantly:
File "/usr/local/lib/python2.7/dist-packages/connexion/utils.py", line 74, in get_function_from_name
raise ValueError("Empty function name")
exceptions.ValueError: Empty function name
From my understanding connexion will base it's manual testing feature from the object operationId in every route that needs to point on the function handling the request.
Problem: every route of the API are defined as nested function.
def add_routes(app, oauth):
#app.route('/api/home', methods=['GET'])
#oauth.require_oauth()
def home():
user = request.oauth.user
return jsonify(
user_id=user.user_id,
username=user.username
)
I know nested functions in python are not actually functions at all: not callable, just present in the language in order for us programmers to organize our code.
I think that would be the issue with connexion, it is just not capable of finding these functions and map them for the manual testing feature, but i'm not sure how to fix this. Do you see something that would allow connexion to map the function without having to refactor the entire API in order not to have nested functions ?
Thanks a lot for any help.
My guess is that you haven't defined your handler function. You need to provide a module+function that matches the operationId in the spec.
For example:
I have a function called get_user() in a file app.py , I need to set the operationId to app.get_user.
operationId: app.get_user
Hope that helps!
Anyone know if this is possible?
I just want to automate dropping some documents into my onedrive for business account.
I tried
import onedrivesdk
from onedrivesdk.helpers import GetAuthCodeServer
from onedrivesdk.helpers.resource_discovery import ResourceDiscoveryRequest
redirect_uri = 'http://localhost:8080'
client_id = 'appid'
client_secret = 'mysecret'
discovery_uri = 'https://api.office.com/discovery/'
auth_server_url='https://login.live.com/oauth20_authorize.srf?scope=wl.skydrive_update'
#auth_server_url='https://login.microsoftonline.com/common/oauth2/authorize',
auth_token_url='https://login.microsoftonline.com/common/oauth2/token'
http = onedrivesdk.HttpProvider()
auth = onedrivesdk.AuthProvider(http,
client_id,
auth_server_url=auth_server_url,
auth_token_url=auth_token_url)
auth_url = auth.get_auth_url(redirect_uri)
code = GetAuthCodeServer.get_auth_code(auth_url, redirect_uri)
auth.authenticate(code, redirect_uri, client_secret, resource=resource)
# If you have access to more than one service, you'll need to decide
# which ServiceInfo to use instead of just using the first one, as below.
service_info = ResourceDiscoveryRequest().get_service_info(auth.access_token)[0]
auth.redeem_refresh_token(service_info.service_resource_id)
client = onedrivesdk.OneDriveClient(service_info.service_resource_id + '/_api/v2.0/', auth, http)
I registered an APP and got a secret and id. But when I ran this I got scope is invalid errors. Plus it tries to launch a webpage which isn't great for a command line kinda environment. I think this SDK might be outdated as well because originally this script had login.microsoftonline, but that wasn't reachable so I changed it to login.live.com.
I wrote this sample code you posted. You replaced the auth_server_URLwith the authentication URL for Microsoft Account authentication, which can only be used to access OneDrive (the consumer product). You need to continue using the login.microsoftonline.com URL to log into your OneDrive for Business account.
You are correct that this pops up a dialog. However, you can write a little supporting code so that only happens the first time you log into a particular app. Follow these steps (assuming you are using the default implementation of AuthProvider:
Use the sample code above up through the line auth.redeem_refresh_token()
The AuthProvider will now have a Session object, which caches the credentials of the current user and session. Use AuthProvider.save_session() to save the credentials for later.
Next time you start your app, use AuthProvider.load_session() and AuthProvider.refresh_token() to retrieve the previous session and refresh the auth token. This will all be headless.
Take note that the default implementation of SessionBase (found here) uses Pickle and is not safe for product use. Make sure to create a new implementation of Session if you intend to deploy this app to other users.
Onerive's website shows "Not Yet" on "OneDrive SDK for Python" to "OneDrive for Business"
https://dev.onedrive.com/SDKs.htm
The github sample codes did not work for me either, it tried to popup a window of authentication, but IE can not find the address:
http://('https//login.microsoftonline.com/common/oauth2/authorize',)?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=034xxxx9-9xx8-4xxf-bexx-1bc5xxxxbd0c&response_type=code
or removed all the "-" in client id
http://('https//login.microsoftonline.com/common/oauth2/authorize',)?redirect_uri=http%3A%2F%2Flocalhost%3A8080&client_id=034xxxx99xx84xxfbexx1bc5xxxxbd0c&response_type=code
Either way, I got the same result, IE did not show the popup with a line "This page can’t be displayed"
I want to send a HTML email to the users after they signup to the website. Earlier I wrote this script to send
from google.appengine.api import mail
message = mail.EmailMessage(sender="Example.com Support <support#example.com>",
subject="Your account has been approved")
message.to = "Albert Johnson <Albert.Johnson#example.com>"
message.body = """
Dear Albert:
Your example.com account has been approved. You can now visit
http://www.example.com/ and sign in using your Google Account to
access new features.
Please let us know if you have any questions.
The example.com Team
"""
message.html = """
<html><head></head><body>
Dear Albert:
Your example.com account has been approved. You can now visit
http://www.example.com/ and sign in using your Google Account to
access new features.
Please let us know if you have any questions.
The example.com Team
</body></html>
"""
message.send()
But instead of placing the HTML directly in the main code, I want to have a separate HTML file which would be used as a body.
I tried to do it as follows:
message.html = 'emailHTML.html'
but in vain. How can I use a HTML file in the place of HTML code?
You can set
message.html = open('emailHTML.html').read()
to get exactly the same effect as what you're doing now; or, you could have the HTML as an attachment (so the email's body is just the plain text one, but the recipient can download the HTML as an attachment) with:
message.attachments = [('emailHTML.html', open('emailHTML.html').read())]
I'm not quite sure what you'd hope to accomplish in either case, but these are pretty much the only two possibilities I can think of. If neither is satisfactory, please edit your Q to explain exactly what you want this email to look like to the user (is the body supposed to be plain or html, is there supposed to be an attachment...?).
Probably the best way to do this would be to use a templating engine to load and generate the HTML as a string from the HTML file. For example, if you use the webapp2 jinja2 extras package, you could do something along the lines of:
from webapp2_extras import jinja2 as webapp_extras_jinja2
# ...
def get_message_html():
jinja2 = webapp_extras_jinja2.get_jinja2()
return jinja2.render_template('relative/path/to/template.html')
# ...
def send_email():
# ...
message.html = get_message_html()
# ...
Note that to get this working, you need to add jinja2 to the libraries section of app.yaml as in:
libraries:
- name: webapp2
version: 2.5.2
- name: jinja2
version: 2.6
... and you also need to include an appropriate 'webapp2_extras.jinja2' to the app config. Ex:
config = {
'webapp2_extras.jinja2': {
'template_path': 'path/containing/my/templates',
'environment_args': {
# Keep generated HTML short
'trim_blocks': True,
'extensions': [
# Support auto-escaping for security
'jinja2.ext.autoescape',
# Handy but might not be needed for you
'jinja2.ext.with_'
# ... other extensions? ...
],
# Auto-escape by default for security
'autoescape': True
},
# .. other configuration options for jinja2 ...
},
# ... other configuration for the app ...
},
# ...
app = webapp2.WSGIApplication(routes, is_debug_enabled, config)
While you can just as easily open the HTML file yourself, the benefit of using a templating engine such as jinja2 is that it will encourage you to compose and reuse the HTML in a more sane way (whereas simply loading the HTML file might result in you eventually applying substitutions by hand). Also, just a quick security reminder: if any of the data you include in the email comes from untrusted sources (like the user or another user), make sure to properly validate and sanity-check the content (and also enable auto-escaping in the templating engine).
You can obviously choose a templating other than jinja2, but I specifically chose that one for my answer since it is well supported and well documented for App Engine.