Google App Engine oauth2 Callback 404 - google-app-engine

with the following code I am getting a 404 resource not found error after clicking authorize button for google drive. My code is below- any ideas what I am doing incorrectly?
from __future__ import print_function
import httplib2
import os
from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage
from apiclient.discovery import build
from google.appengine.ext import webapp
from oauth2client.appengine import OAuth2DecoratorFromClientSecrets
import webapp2
try:
import argparse
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
flags = None
except SystemExit:
flags= None
#
## If modifying these scopes, delete your previously saved credentials
## at ~/.credentials/drive-python-quickstart.json
SCOPES = 'https://www.googleapis.com/auth/drive'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Drive API Quickstart'
decorator = OAuth2DecoratorFromClientSecrets( CLIENT_SECRET_FILE,SCOPES)
service = build('drive', 'v3')
class MainHandler(webapp.RequestHandler):
#decorator.oauth_required
def get(self):
# Get the authorized Http object created by the decorator.
http = decorator.http()
# Call the service using the authorized Http object.
request = service.files().list(q = "mimeType != 'application/vnd.google-apps.folder'", pageSize=1000, )
response = request.execute(http=http)
app = webapp.WSGIApplication([
('/', MainHandler),
], debug=True)
I have https://drive-156701.appspot.com/oauth2callback with and without a / at the end as redirects, which I thought where the correct redirect urls?
Thanks!!

You don't have a URL handler for /oauth2callback. You only have a url handler for /. Try:
app = webapp.WSGIApplication([
('/oauth2callback', Oauth2CallbackHandler),
('/', MainHandler),
], debug=True)
and make a Oauth2CallbackHandler class to handle the callback.

In addition to including the :
app = webapp2.WSGIApplication(
[
('/', MainHandler),
(decorator.callback_path, decorator.callback_handler()),
],
debug=True)
The #decorator.oauth_required handles the callbacks for you.
Check out this video: https://www.youtube.com/watch?v=HoUdWBzUZ-M
You need to create credentials for both localhost development and deployed.
And make sure you have checked out: https://cloud.google.com/appengine/docs/standard/python/tools/using-libraries-python-27
Your api imports must be handled like 3rd party imports, details here:
https://developers.google.com/api-client-library/python/start/installation

Related

How to create 404 error page in Dash (plotly)?

In Flask I can generate 404 (https://flask.palletsprojects.com/en/1.1.x/errorhandling/):
#app.errorhandler(404)
def page_not_found(error):
output = render_template("404.html",
title = 'Page not found')
but how create 404 page in Dash? With 404 response status.
You could create a Flask app and pass it to your Dash app. Then you could use the Flask app for the error handling and for redirecting to your Dash app:
from flask import Flask, render_template, redirect
from dash import Dash
import dash_html_components as html
server = Flask(__name__)
app = Dash(__name__, server=server, url_base_pathname='/dash/')
app.layout = html.P("Hello World")
#server.errorhandler(404)
def page_not_found(error):
return render_template("404.html", title="Page not found")
#server.route("/dash")
def dashboard():
return redirect("/dash/")
if __name__ == "__main__":
server.run()

Is there a way to explicitly set MIME types for Starlette/Uvicorn?

It appears that my simple Starlette/Uvicorn web app is serving up incorrect MIME content type for Jinja templated (served from the same server) JavaScript files. As you can see from the screen shot the uvicorn server is casting the *.js file as type (“text/plain”).
Screenshot
I have scoured the documents for both Starlette and Uvicorn and am simply stumped.
My simple web app is as follows:
from starlette.applications import Starlette
from starlette.staticfiles import StaticFiles
from starlette.responses import HTMLResponse
from starlette.templating import Jinja2Templates
from starlette.middleware.cors import CORSMiddleware
import uvicorn
from random import randint
port = randint(49152,65535)
templates = Jinja2Templates(directory='templates')
app = Starlette(debug=True)
app.mount('/static', StaticFiles(directory='statics', html=True), name='static')
app.add_middleware(
CORSMiddleware, allow_origins=["*"], allow_headers=["*"], allow_methods=["*"]
)
#app.route('/')
async def homepage(request):
template = "index.html"
context = {"request": request}
return templates.TemplateResponse(template, context, media_type='text/html')
#app.route('/error')
async def error(request):
"""
An example error. Switch the `debug` setting to see either tracebacks or 500 pages.
"""
raise RuntimeError("Oh no")
#app.exception_handler(404)
async def not_found(request, exc):
"""
Return an HTTP 404 page.
"""
template = "404.html"
context = {"request": request}
return templates.TemplateResponse(template, context, status_code=404)
#app.exception_handler(500)
async def server_error(request, exc):
"""
Return an HTTP 500 page.
"""
template = "500.html"
context = {"request": request}
return templates.TemplateResponse(template, context, status_code=500)
if __name__ == "__main__":
uvicorn.run("app-567:app", host='0.0.0.0', port=port, log_level="info", http='h11', loop='asyncio', reload=True)
The JavaScript files that are loaded in the head give the same error but load nonetheless. This is a bi-product of the new 'nosniff' default setting in Firefox (73.0 64-bit). The scripts that are loaded as module imports fail outright.
I am running Windows 10 (x64), Python 3.7, uvicorn 0.11.2, and starlette 0.13.1.
Any help is greatly appreciated.
Thank you in advance.
I was able to fix this issue by explicitly setting the mimetypes variables as follows:
from starlette.applications import Starlette
from starlette.staticfiles import StaticFiles
from starlette.responses import HTMLResponse
from starlette.templating import Jinja2Templates
from starlette.middleware.cors import CORSMiddleware
import uvicorn
from random import randint
import mimetypes
mimetypes.init()
port = randint(49152,65535)
templates = Jinja2Templates(directory='templates')
app = Starlette(debug=True)
app.mount('/static', StaticFiles(directory='statics', html=True), name='static')
app.add_middleware(
CORSMiddleware, allow_origins=["*"], allow_headers=["*"], allow_methods=["*"]
)
#app.route('/')
async def homepage(request):
mimetypes.add_type('application/javascript', '.js')
mimetypes.add_type('text/css', '.css')
mimetypes.add_type('image/svg+xml', '.svg')
template = "index.html"
context = {"request": request}
return templates.TemplateResponse(template, context, media_type='text/html')
#app.route('/error')
async def error(request):
"""
An example error. Switch the `debug` setting to see either tracebacks or 500 pages.
"""
raise RuntimeError("Oh no")
#app.exception_handler(404)
async def not_found(request, exc):
"""
Return an HTTP 404 page.
"""
template = "404.html"
context = {"request": request}
return templates.TemplateResponse(template, context, status_code=404)
#app.exception_handler(500)
async def server_error(request, exc):
"""
Return an HTTP 500 page.
"""
template = "500.html"
context = {"request": request}
return templates.TemplateResponse(template, context, status_code=500)
if __name__ == "__main__":
uvicorn.run("app-567:app", host='0.0.0.0', port=port, log_level="info", http='h11', loop='asyncio', reload=True)

403 Forbidden when trying to access API via DRF

I am trying to access a django server using django-rest-framework from angular using the following service:
myapp.factory("GetData", ["$resource",function($resource) {
return $resource(
"http://local/path/**/users/:username/",
{},
{
"query": {
method: "GET",
isArray: true,
headers:{
"Content-Type":"application/json"
}
}
},
{
stripTrailingSlashes:false
}
);
}]);
When I put the URL in the browser, I get the desired output, but the problem is that when I try to access via Angular, in my console I am getting a 403 forbidden error. Can anyone explain to me whats causing this. and how I can possibly fix this issue.
My url.py looks as follows:
from rest_framework.routers import DefaultRouter
from .views import UserViewSet
router = DefaultRouter()
router.register(prefix='users', viewset=UserViewSet)
urlpatterns = router.urls
My views.py file is as follows:
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from .models import users
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = users.objects.all()
serializer_class = UserSerializer
I think the 403 forbidden is coming to browser block cross origin requests.
You need to enable allow CORS at your API server by setting appropriate headers.
You can lookup https://github.com/ottoyiu/django-cors-headers
For this you need to add in your installed apps.
'corsheaders'
And just set to test it out. Check for fine tuned settings in the docs.
CORS_ORIGIN_ALLOW_ALL = True
Edit
Also you need to add this in your middlewares.
'corsheaders.middleware.CorsMiddleware',
I managed to fix the problem by combining what #Bipul Jain suggested https://stackoverflow.com/a/42044910/7210105 (above) and also altering this link in my $resource
"http://local/path/**/users/:username/"
I removed http://local/path from the link and the 403 forbidden error went away.

Integrating Chat with (webapp2 + python 2.7 + Jinja2)

I'm trying to add chat to my site and am integrating some code with my existing code. The chat app works fine on its own when it's all set up in the original main.app file. But when I try to move that same code to a handlers.py file and then setup up routes in routes.py I get errors saying template variables are undefined. Are the two different codes conflicting in the way they render templates? They seem to be using webapp2 differently, i.e. my code renders templates like this:
self.render_template('secure_zone.html', **params)
And the chat app like this:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
Are both acceptable?
Here's my handlers.py file:
Routes are setup in routes.py and added in main.py
"""
import httpagentparser
from boilerplate import models
from boilerplate.lib.basehandler import BaseHandler
from boilerplate.lib.basehandler import user_required
class SecureRequestHandler(BaseHandler):
"""
Only accessible to users that are logged in
"""
#user_required
def get(self, **kwargs):
user_session = self.user
user_session_object = self.auth.store.get_session(self.request)
user_info = models.User.get_by_id(long( self.user_id ))
user_info_object = self.auth.store.user_model.get_by_auth_token(
user_session['user_id'], user_session['token'])
try:
params = {
"user_session" : user_session,
"user_session_object" : user_session_object,
"user_info" : user_info,
"user_info_object" : user_info_object,
"userinfo_logout-url" : self.auth_config['logout_url'],
}
return self.render_template('secure_zone.html', **params)
except (AttributeError, KeyError), e:
return "Secure zone error:" + " %s." % e
Here's the main.py file for the chat app:
import os
import hashlib
import urllib
import logging
import re
import json
import webapp2
import jinja2
from google.appengine.api import channel as channel_api # 'channel' is kind of ambiguous in context
from google.appengine.ext import db
from google.appengine.api import memcache
# This section will eventually get moved to a Handler class
template_dir = os.path.join(
os.path.dirname(__file__), 'templates')
jinja_env = jinja2.Environment(
loader = jinja2.FileSystemLoader(template_dir),
autoescape = True)
def render_str(template, **params):
'''Returns a string of the rendered template'''
t = jinja_env.get_template(template)
return t.render(params)
def render(template, **kw):
'''Render using the template and parameters'''
return(render_str(template, **kw))
# End Handler
class Main(webapp2.RequestHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
def post(self):
'''Displays chat UI'''
username = self.request.get('username')
channelname = self.request.get('channel')
usernameerror = ""
if not username:
usernameerror="Please enter a username"
elif not re.compile(r'^[a-zA-Z0-9_-]{3,20}$').match(username):
usernameerror = "Username must consist of 3-20 alphanumeric characters."
elif get_user(username):
usernameerror="Username already in use"
channelerror = ""
if channelname and not re.compile(r'^#[\w]{3,20}$').match(channelname):
channelerror="Channel must consist of 3-20 alpha_numeric characters and start with a #"
if len(usernameerror+channelerror) > 0:
self.response.out.write(render("main.html",
username=username,
usernameerror=usernameerror,
channel=channelname,
channelerror=channelerror))
app = webapp2.WSGIApplication([
('/', Main),
('/communication', Communication),
('/_ah/channel/connected/?', Connect),
('/_ah/channel/disconnected/?', Disconnect)
], debug=True)
The specific error you posted in the comments "Error: 'Main' object has no attribute 'render_template'" is because in your Main handler, you try to return self.render_template. You should be just calling the function like this:
render_template("main.html", channel="#udacity")
Please note that I did not check the rest of your code, so if you run into any other issues, please post the specific errors you get.
It is because your webapp2.RequestHandler do not have the corresponding function "render_template"
you can use a BaseHandler with render_template function added to achieve the template rendering
from google.appengine.ext.webapp import template
class BaseHandler(webapp2.RequestHandler):
def render_template(self, filename, **template_args):
path = os.path.join(os.path.dirname(__file__), 'templates', filename)
self.response.write(template.render(path, template_args))
class Main(BaseHandler):
def get(self):
'''Show connection page'''
return self.render_template("main.html", channel="#udacity")
ref: http://blog.notdot.net/2011/11/Migrating-to-Python-2-7-part-2-Webapp-and-templates
I just started using webapp2 + python 2.7 + Jinja2 few days, and that is the same problem I encountered.
Hope this solution can help you ;)

User Accounts with (Python 2.7 + Webapp2 + Google App Engine + Jinja + NDB + WTForms)

I have been racking my brain for weeks trying to get this authentication module to work under the webapp2 framework using Jinja, NDB and WTForms. I've looked online everywhere and have reached my breaking point and need to reach out to somebody for help. I've been following this tutorial (which is pretty thorough but I think leaves out some important details, especially for a newbie like me):
User Accounts with Webapp2 + Google App Engine
As far as I know I have everything setup correctly but I continually get a 404 - resources can not be found. What could I be doing wrong? Here is my code...
handlers.py:
import webapp2
import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb
import webapp2_extras.appengine.auth.models as auth_models
from google.appengine.api import users
from webapp2_extras import sessions, auth # we'll use auth later on
from webapp2_extras.auth import InvalidAuthIdError
from webapp2_extras.auth import InvalidPasswordError
from wtforms import Form, TextField, PasswordField, validators
from webapp2_extras.appengine.users import login_required
def jinja2_factory(app):
j = jinja2.Jinja2(app)
j.environment.filters.update({
# Set filters. (http://tinyurl.com/jinja2-factory)
# ...
})
j.environment.globals.update({
# Set global variables.
'uri_for': webapp2.uri_for,
# ...
})
return j
def login_required(handler):
def check_login(self, *args, **kwargs):
if not self.user:
return self.redirect_to('login')
else:
return handler(self, *args, **kwargs)
return check_login
class UserAwareHandler(webapp2.RequestHandler):
def dispatch(self):
try:
super(UserAwareHandler, self).dispatch()
finally:
# Save the session after each request
self.session_store.save_sessions(self.response)
#webapp2.cached_property
def session_store(self):
return sessions.get_store(request=self.request)
#webapp2.cached_property
def session(self):
return self.session_store.get_session(backend="datastore")
#webapp2.cached_property
def auth(self):
return auth.get_auth(request=self.request)
#webapp2.cached_property
def user(self):
user = self.auth.get_user_by_session()
return user
#webapp2.cached_property
def user_model(self):
user_model, timestamp = self.auth.store.user_model.get_by_auth_token(
self.user['user_id'],
self.user['token']) if self.user else (None, None)
return user_model
#webapp2.cached_property
def jinja2(self):
return jinja2.get_jinja2(factory=jinja2_factory, app=self.app)
def render_response(self, _template, **context):
ctx = {'user': self.user_model}
ctx.update(context)
rv = self.jinja2.render_template(_template, **ctx)
self.response.write(rv)
class SignupForm(Form):
email = TextField('Email',
[validators.Required(),
validators.Email()])
password = PasswordField('Password',
[validators.Required(),
validators.EqualTo('confirm_password',
message="Passwords must match.")])
password_confirm = PasswordField('Confirm Password',
[validators.Required()])
class SignupHandler(UserAwareHandler):
#Serves up a signup form, creates new users
def get(self):
self.render_response("templates/signup.html", form=SignupForm())
def post(self):
form = SignupForm(self.request.POST)
error = None
if form.validate():
success, info = self.auth.store.user_model.create_user(
"auth:" + form.email.data,
unique_properties=['email'],
email= form.password.data,
password_raw= form.password.data)
if success:
self.auth.get_user_by_password("auth:"+form.email.data,
form.password.data)
return self.redirect_to("index")
else:
error = "That email is already in use." if 'email'\
in user else "Something has gone horrible wrong."
self.render_response("templates/signup.html", form=form, error=error)
class LoginForm(Form):
email = TextField('Email',
[validators.Required(), validators.Email()])
password = PasswordField('Password',
[validators.Required()])
class LoginHandler(UserAwareHandler):
def get(self):
self.render_response("templates/index.html", form=LoginForm())
def post(self):
form = LoginForm(self.request.POST)
error = None
if form.validate():
try:
self.auth.get_user_by_password(
"auth:"+form.email.data,
form.password.data)
return self.redirect_to('secure')
except (auth.InvalidAuthIdError, auth.InvalidPasswordError):
error = "Invalid Email / Password"
self.render_response("templates/login.html", form=form, error=error)
class LogoutHandler(UserAwareHandler):
#Destroy the user session and return them to the login screen.
#login_required
def get(self):
self.auth.unset_session()
self.redirect_to('login')
class IndexHandler(UserAwareHandler):
def get(self):
ctx = {
'title1': "ALAZA",
'title2': "HOA",
'slogan': "A communication tool for the Alazan HOA.",
'message1': """
<p>The whole idea here is to show how to set up a simple static web site
on Google App Engine. I want to create an easy way to host your modest web
site on App Engine. My approach is dead simple. All I use is some boilerplate
code almost anyone can follow. You can have multiple pages and use template
variable features that are part of App Engine's WebApp Framework. Most modest
web sites don't do much more than this. Your certainly free to expand on what
you find here.</p>""",
}
self.render_response('templates/index.html', **ctx)
main.py:
import webapp2
import config
import routes
import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb
import webapp2_extras.appengine.auth.models as auth_models
class AwesomeUser(auth_models.User):
email = ndb.StringProperty()
webapp2_config = {}
webapp2_config['webapp2_extras.sessions'] = {
'secret_key': 'othello',}
webapp2_config['webapp2_extras.auth'] = {
'user_model': AwesomeUser,}
app = webapp2.WSGIApplication(config=config.webapp2_config)
routes.add_routes(app)
routes.py:
import handlers
import webapp2
from webapp2_extras.routes import RedirectRoute
# Using redirect route instead of simple routes since it supports strict_slash
# Simple route: http://webapp-improved.appspot.com/guide/routing.html#simple-routes
# RedirectRoute: http://webapp-improved.appspot.com/api/webapp2_extras/routes.html#webapp2_extras.routes.RedirectRoute
_routes = [
RedirectRoute('templates/login.html', handlers.LoginHandler, name='login'),
RedirectRoute('/templates/logout.html', handlers.LogoutHandler, name='logout'),
RedirectRoute('/templates/index.html', handlers.IndexHandler, name='index'),
RedirectRoute('/templates/signup.html', handlers.SignupHandler, name='signup'),]
def get_routes():
return _routes
def add_routes(app):
if app.debug:
secure_scheme = 'http'
for r in _routes:
app.router.add(r)
app.yaml:
application: alazan-hoa
version: main
runtime: python27
api_version: 1
threadsafe: true
handlers:
- url: /js
static_dir: js
- url: /images
static_dir: images
- url: /css
static_dir: css
- url: /.*
script: main.app
libraries:
- name: webapp2
version: "2.5.1"
- name: jinja2
version: "2.6"
builtins:
- appstats: on
In your routes.py file, in the RedirectRoute you specify html files, while you should specify paths that are mapped to handler methods and inside them render the html templates.
For example, in your routes.py file you should have something like this:
_routes = [
RedirectRoute('/login', handlers.LoginHandler, name='login'),
RedirectRoute('/logout', handlers.LogoutHandler, name='logout'),
RedirectRoute('/index', handlers.IndexHandler, name='index'),
RedirectRoute('/signup', handlers.SignupHandler, name='signup')
]
You can take a look at Google App Engine Boilerplate for an introduction to Google App Engine development and best practices around it.
Hope this helps!

Resources