# Python standard libraries import json import os import sqlite3 # Third-party libraries import flask from flask import Flask, redirect, request, url_for from flask_login import ( LoginManager, current_user, login_required, login_user, logout_user, ) from oauthlib.oauth2 import WebApplicationClient import requests # Internal imports from database.db import init_db_command from database.user import User from database.user import dbCalendar import backend.caltojson as caltojson import google.oauth2.credentials import google_auth_oauthlib.flow import googleapiclient.discovery # Configuration CLIENT_SECRETS_FILE = "certificate/client_secret.json" # This OAuth 2.0 access scope allows for full read/write access to the # authenticated user's account and requires requests to use an SSL connection. SCOPES = ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/calendar.readonly", "openid"] API_SERVICE_NAME = 'calendar' API_VERSION = 'v3' GOOGLE_CLIENT_ID ="377787187748-shuvi4iq5bi4gdet6q3ioataimobs4lh.apps.googleusercontent.com" GOOGLE_CLIENT_SECRET = "Hu_YWmKsVKUcLwyeINYzdKfZ" GOOGLE_DISCOVERY_URL = ( "https://accounts.google.com/.well-known/openid-configuration" ) # Flask app setup app = Flask(__name__, static_folder='static', template_folder='template') app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24) # User session management setup # https://flask-login.readthedocs.io/en/latest login_manager = LoginManager() login_manager.init_app(app) # Naive database setup try: init_db_command() except sqlite3.OperationalError: # Assume it's already been created pass # OAuth 2 client setup client = WebApplicationClient(GOOGLE_CLIENT_ID) # Flask-Login helper to retrieve a user from our db @login_manager.user_loader def load_user(user_id): return User.get(user_id) @app.route("/") def account(): return flask.redirect('account') @app.route("/account") def index(): if current_user.is_authenticated: updateCalendars() return (flask.render_template('account.html', username = current_user.name, email = current_user.email, profile_img=current_user.profile_pic ) ) else: return flask.render_template('login.html') def get_google_provider_cfg(): return requests.get(GOOGLE_DISCOVERY_URL).json() class Calendar: def __init__(self, name, toggle='False', color="#000000"): self.name = name self.color = color self.toggle=toggle def calendarsFromDb(): calendars = dbCalendar.getCalendars(current_user.id) pyCalendars = [] for calendar in calendars: name = calendar[2] calId = calendar[1] toggle = calendar[3] color = calendar[4] pyCalendars.append(Calendar(name, toggle, color)) return pyCalendars @app.route("/calendar") @login_required def calendar(): calendars = calendarsFromDb() return flask.render_template('calendar.html', calendars=calendars) def getCalendarJson(): if 'credentials' not in flask.session: return flask.redirect('login/google') # Load credentials from the session. credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) todaysCal = caltojson.generateJsonFromCalendarEntries(credentials) with open('./userinfo/' + current_user.id + '/calendarevents.json', 'w') as outfile: json.dump(todaysCal, outfile) return todaysCal def updateCalendars(): if 'credentials' not in flask.session: return flask.redirect('login/google') # Load credentials from the session. credentials = google.oauth2.credentials.Credentials( **flask.session['credentials']) calendars = caltojson.getCalendarList(credentials) for calendar in calendars: if dbCalendar.getCalendar(current_user.id, calendar.calendarId) == None: dbCalendar.create(current_user.id, calendar.calendarId, calendar.summary, calendar.color) print("updated Calendars") # Save credentials back to session in case access token was refreshed. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. flask.session['credentials'] = credentials_to_dict(credentials) @app.route("/login/google") def login(): ''' # Find out what URL to hit for Google login google_provider_cfg = get_google_provider_cfg() authorization_endpoint = google_provider_cfg["authorization_endpoint"] # Use library to construct the request for Google login and provide # scopes that let you retrieve user's profile from Google request_uri = client.prepare_request_uri( authorization_endpoint, redirect_uri=request.base_url + "/callback", scope=["openid", "email", "profile", "https://www.googleapis.com/auth/calendar.readonly"], ) return redirect(request_uri) ''' # Create flow instance to manage the OAuth 2.0 Authorization Grant Flow steps. flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES) # The URI created here must exactly match one of the authorized redirect URIs # for the OAuth 2.0 client, which you configured in the API Console. If this # value doesn't match an authorized URI, you will get a 'redirect_uri_mismatch' # error. flow.redirect_uri = request.base_url + "/callback" authorization_url, state = flow.authorization_url( # Enable offline access so that you can refresh an access token without # re-prompting the user for permission. Recommended for web server apps. access_type='offline', # Enable incremental authorization. Recommended as a best practice. include_granted_scopes='true') # Store the state so the callback can verify the auth server response. flask.session['state'] = state return flask.redirect(authorization_url) @app.route("/login/google/callback") def callback(): # Specify the state when creating the flow in the callback so that it can # verified in the authorization server response. state = flask.session['state'] flow = google_auth_oauthlib.flow.Flow.from_client_secrets_file( CLIENT_SECRETS_FILE, scopes=SCOPES, state=state) flow.redirect_uri = request.base_url # Use the authorization server's response to fetch the OAuth 2.0 tokens. authorization_response = flask.request.url flow.fetch_token(authorization_response=authorization_response) # Store credentials in the session. # ACTION ITEM: In a production app, you likely want to save these # credentials in a persistent database instead. credentials = flow.credentials flask.session['credentials'] = credentials_to_dict(credentials) session = flow.authorized_session() userinfo = session.get('https://www.googleapis.com/userinfo/v2/me').json() # Create a user in your db with the information provided # by Google user = User( id_=userinfo['id'], name=userinfo['name'], email=userinfo['email'], profile_pic=userinfo['picture'] ) # Doesn't exist? Add it to the database. if not User.get(user.id): User.create(user.id, user.name, user.email, user.profile_pic) # Begin user session by logging the user in login_user(user) return flask.redirect(flask.url_for('index')) @app.route("/logout") @login_required def logout(): logout_user() return redirect(url_for("index")) def credentials_to_dict(credentials): return {'token': credentials.token, 'refresh_token': credentials.refresh_token, 'token_uri': credentials.token_uri, 'client_id': credentials.client_id, 'client_secret': credentials.client_secret, 'scopes': credentials.scopes} @app.route("/userinfo//calendarevents.json") def downloader(user): print(user) path = "/home/raphael/dev/website_ws/website/userinfo/" + user return flask.send_from_directory(path, "calendarevents.json") # POST @app.route('/calendar', methods = ['POST', 'DELETE']) @login_required def user(): if request.method == 'POST': calName = request.json.get('calendar_id') color = request.json.get('color') toggle = request.json.get('toggle') if color != None: dbCalendar.updateCalendar(current_user.id, calName, color=color) if toggle != None: dbCalendar.updateCalendar(current_user.id, calName, toggle=toggle) # toggle specific calendar of user elif request.method == 'DELETE': # do nothing return 'NONE' else: # POST Error 405 print("405") return 'OK' if __name__ == "__main__": context = ('certificate/xip.io.crt', 'certificate/xip.io.key')#certificate and key files app.run('0.0.0.0', 1234, ssl_context=context, debug=True)