diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..02fe7b72 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +# Compiled source # +################### +*.com +*.class +*.dll +*.exe +*.o +*.so + +# ignore all vagrant files +**/.vagrant + +# ignore all .json files +*.json + +# Packages # +############ +# it's better to unpack these files and commit the raw source +# git has its own built in compression methods +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Logs and databases # +###################### +*.log +*.sql +*.sqlite diff --git a/DeepinScreenshot_select-area_20190511121248.png b/DeepinScreenshot_select-area_20190511121248.png new file mode 100644 index 00000000..e609fc22 Binary files /dev/null and b/DeepinScreenshot_select-area_20190511121248.png differ diff --git a/README.md b/README.md index 90aafdc5..16c7cf96 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,18 @@ # OAuth2.0 Starter Code for Auth&Auth course +# Making the Code work with latest OAuth2.0 googleApi changes with Javascript + +This changed Codework has changes required to remove Jquery and Ajax completely and do all the Login and Logout requests using Javascript on python 2.7 + +No New libraries are required + +The code is tested on the legacy vagrant environment + +This code also has new features as *"signOut"* & *"Login"* links addeed to the restaurant page + +![Restaurant main Page](https://github.com/sananand007/OAuth2.0/blob/master/DeepinScreenshot_select-area_20190511121248.png) + + # Installing the Vagrant VM for ud330 - Authentication & Authorization **Note: If you already have a vagrant machine installed from previous Udacity courses skip to the 'Fetch the Source Code and VM Configuration' section** diff --git a/database_setup.pyc b/database_setup.pyc new file mode 100644 index 00000000..4da65f5d Binary files /dev/null and b/database_setup.pyc differ diff --git a/project.py b/project.py index 0c329b4e..994aeb4a 100644 --- a/project.py +++ b/project.py @@ -1,10 +1,22 @@ -from flask import Flask, render_template, request, redirect,jsonify, url_for, flash -app = Flask(__name__) - +from flask import Flask, render_template, request, redirect, jsonify, url_for, flash from sqlalchemy import create_engine, asc from sqlalchemy.orm import sessionmaker from database_setup import Base, Restaurant, MenuItem +from flask import session as login_session +import random +import string +from oauth2client.client import flow_from_clientsecrets +from oauth2client.client import FlowExchangeError +import httplib2 +import json +from flask import make_response +import requests +app = Flask(__name__) + +CLIENT_ID = json.loads( + open('client_secrets.json', 'r').read())['web']['client_id'] +APPLICATION_NAME = "Restaurant Menu Application" #Connect to Database and create database session engine = create_engine('sqlite:///restaurantmenu.db') @@ -14,6 +26,141 @@ session = DBSession() +@app.route('/login') +def showLogin(): + state = ''.join(random.choice(string.ascii_uppercase + string.digits) for x in xrange(32)) + login_session['state'] = state + # Render the login template + return render_template('logingoogle.html', state = state, GOOGLE_CLIENT_ID=CLIENT_ID) + +@app.route('/gconnect', methods=['POST']) +def gconnect(): + if request.args.get('state') != login_session['state']: + response = make_response(json.dumps('Invalid state parameter.'), 401) + response.headers['Content-Type'] = 'application/json' + return response + # Obtain authorization code + code = request.data + + try: + # Upgrade the authorization code into a credentials object + oauth_flow = flow_from_clientsecrets('client_secrets.json', scope='') + oauth_flow.redirect_uri = 'postmessage' + credentials = oauth_flow.step2_exchange(code) + except FlowExchangeError: + response = make_response( + json.dumps('Failed to upgrade the authorization code.'), 401) + response.headers['Content-Type'] = 'application/json' + return response + + # Check that the access token is valid. + access_token = credentials.access_token + url = ('https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=%s' + % access_token) + h = httplib2.Http() + result = json.loads(h.request(url, 'GET')[1]) + # If there was an error in the access token info, abort. + if result.get('error') is not None: + response = make_response(json.dumps(result.get('error')), 500) + response.headers['Content-Type'] = 'application/json' + return response + + # Verify that the access token is used for the intended user. + gplus_id = credentials.id_token['sub'] + if result['user_id'] != gplus_id: + response = make_response( + json.dumps("Token's user ID doesn't match given user ID."), 401) + response.headers['Content-Type'] = 'application/json' + return response + + # Verify that the access token is valid for this app. + if result['issued_to'] != CLIENT_ID: + response = make_response( + json.dumps("Token's client ID does not match app's."), 401) + print "Token's client ID does not match app's." + response.headers['Content-Type'] = 'application/json' + return response + + stored_access_token = login_session.get('access_token') + stored_gplus_id = login_session.get('gplus_id') + if stored_access_token is not None and gplus_id == stored_gplus_id: + response = make_response(json.dumps('Current user is already connected.'), + 200) + response.headers['Content-Type'] = 'application/json' + return response + + # Store the access token in the session for later use. + login_session['access_token'] = credentials.access_token + login_session['gplus_id'] = gplus_id + + # Get user info + userinfo_url = "https://www.googleapis.com/oauth2/v1/userinfo" + params = {'access_token': credentials.access_token, 'alt': 'json'} + answer = requests.get(userinfo_url, params=params) + + data = answer.json() + login_session['logged_in'] = True + login_session['provider'] = 'google' + login_session['username'] = data['name'] + login_session['picture'] = data['picture'] + login_session['email'] = data['email'] + + output = '' + output += '

Welcome, ' + output += login_session['username'] + output += '!

' + output += ' ' + flash("you are now logged in as %s" % login_session['username']) + print "done!" + return output + # DISCONNECT - Revoke a current user's token and reset their login_session + +@app.route('/sign-out') +def signOut(): + access_token = None + if 'access_token' in login_session: + access_token = login_session['access_token'] + print("access token", access_token) + if access_token is None: + print 'Access Token is None' + response = make_response(json.dumps( + 'Current user not connected.'), 401) + response.headers['Content-Type'] = 'application/json' + return response + print 'In gdisconnect access token is %s', access_token + print 'Login session: ' + print login_session.keys() + url = 'https://accounts.google.com/o/oauth2/revoke?token={}'.format(access_token) + h = httplib2.Http() + result = h.request(url, 'GET')[0] + print 'result is ' + print result + if result['status'] == '200': + del login_session['logged_in'] + del login_session['access_token'] + del login_session['gplus_id'] + del login_session['username'] + del login_session['email'] + del login_session['picture'] + del login_session['provider'] + flash("You have been logged out") + return render_template('signout.html', GOOGLE_CLIENT_ID=CLIENT_ID) + else: + response = make_response(json.dumps('Failed to revoke token for given user.', 400)) + response.headers['Content-Type'] = 'application/json' + del login_session['logged_in'] + del login_session['access_token'] + del login_session['gplus_id'] + del login_session['username'] + del login_session['email'] + del login_session['picture'] + del login_session['provider'] + flash("You have been logged out") + return render_template('signout.html', GOOGLE_CLIENT_ID=CLIENT_ID) + #return response + #JSON APIs to view Restaurant Information @app.route('/restaurant//menu/JSON') def restaurantMenuJSON(restaurant_id): @@ -43,6 +190,8 @@ def showRestaurants(): #Create a new restaurant @app.route('/restaurant/new/', methods=['GET','POST']) def newRestaurant(): + if 'username' not in login_session: + return redirect('/login') if request.method == 'POST': newRestaurant = Restaurant(name = request.form['name']) session.add(newRestaurant) @@ -55,6 +204,8 @@ def newRestaurant(): #Edit a restaurant @app.route('/restaurant//edit/', methods = ['GET', 'POST']) def editRestaurant(restaurant_id): + if 'username' not in login_session: + return redirect('/login') editedRestaurant = session.query(Restaurant).filter_by(id = restaurant_id).one() if request.method == 'POST': if request.form['name']: @@ -68,6 +219,8 @@ def editRestaurant(restaurant_id): #Delete a restaurant @app.route('/restaurant//delete/', methods = ['GET','POST']) def deleteRestaurant(restaurant_id): + if 'username' not in login_session: + return redirect('/login') restaurantToDelete = session.query(Restaurant).filter_by(id = restaurant_id).one() if request.method == 'POST': session.delete(restaurantToDelete) @@ -90,6 +243,8 @@ def showMenu(restaurant_id): #Create a new menu item @app.route('/restaurant//menu/new/',methods=['GET','POST']) def newMenuItem(restaurant_id): + if 'username' not in login_session: + return redirect('/login') restaurant = session.query(Restaurant).filter_by(id = restaurant_id).one() if request.method == 'POST': newItem = MenuItem(name = request.form['name'], description = request.form['description'], price = request.form['price'], course = request.form['course'], restaurant_id = restaurant_id) @@ -103,7 +258,8 @@ def newMenuItem(restaurant_id): #Edit a menu item @app.route('/restaurant//menu//edit', methods=['GET','POST']) def editMenuItem(restaurant_id, menu_id): - + if 'username' not in login_session: + return redirect('/login') editedItem = session.query(MenuItem).filter_by(id = menu_id).one() restaurant = session.query(Restaurant).filter_by(id = restaurant_id).one() if request.method == 'POST': @@ -126,6 +282,8 @@ def editMenuItem(restaurant_id, menu_id): #Delete a menu item @app.route('/restaurant//menu//delete', methods = ['GET','POST']) def deleteMenuItem(restaurant_id,menu_id): + if 'username' not in login_session: + return redirect('/login') restaurant = session.query(Restaurant).filter_by(id = restaurant_id).one() itemToDelete = session.query(MenuItem).filter_by(id = menu_id).one() if request.method == 'POST': diff --git a/restaurantmenu.db b/restaurantmenu.db new file mode 100644 index 00000000..4b8eaac7 Binary files /dev/null and b/restaurantmenu.db differ diff --git a/templates/header.html b/templates/header.html index e801252a..4cd3f49d 100644 --- a/templates/header.html +++ b/templates/header.html @@ -3,9 +3,9 @@ Show All Restaurants + sign Out
-

Login Link will go Here

- + Login
\ No newline at end of file diff --git a/templates/logingoogle.html b/templates/logingoogle.html new file mode 100644 index 00000000..6ddf2dac --- /dev/null +++ b/templates/logingoogle.html @@ -0,0 +1,126 @@ + + + + + + + + + + + + + +

Sign with your Google Account

+
+ + + + + + \ No newline at end of file diff --git a/templates/signout.html b/templates/signout.html new file mode 100755 index 00000000..3177b7da --- /dev/null +++ b/templates/signout.html @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + +
+ + +