def create_app(test_config=None): # Load environment variables from dot env file. load_dotenv() app = Flask(__name__) app.secret_key = os.getenv("AUTH0_CLIENT_SECRET") if type(test_config) == dict: app.db = setup_db(app, **test_config) else: app.db = setup_db(app) # Allow CORS for all domains on all routes CORS(app) ''' Auth0 settings ''' oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id = os.getenv("AUTH0_CLIENT_ID"), client_secret = os.getenv("AUTH0_CLIENT_SECRET"), api_base_url = 'https://full.eu.auth0.com', access_token_url = 'https://full.eu.auth0.com/oauth/token', authorize_url = 'https://full.eu.auth0.com/authorize', client_kwargs = { 'scope': 'openid profile email', }, ) ''' Auth0 routes ''' # Auth0 redirects the user to this route after they have authenticated. @app.route('/callback') def callback_handling(): # Handles response from token endpoint auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/restaurants') # This route uses the Authlib client instance to redirect the user to the login page. @app.route('/login') def login(): uri = os.getenv("AUTH0_CALLBACK_URL") return auth0.authorize_redirect(redirect_uri=uri) # Decorator that checks if the user has authenticated. def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): if 'profile' not in session: # Redirect to Login page here return redirect('/') return f(*args, **kwargs) return decorated # This route renders once the user has logged out. @app.route('/') def home(): return render_template('home.html') # Log the user out and clear the data from the session. @app.route('/logout') def logout(): # Clear session stored data session.clear() # Redirect user to logout endpoint params = {'returnTo': url_for('home', _external=True), 'client_id': os.getenv("AUTH0_CLIENT_ID")} return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) ''' ENDPOINTS for Restaurants and Reviews ''' ''' GET /restaurants It requires authentication. It should contain only the Restaurant.short() data representation. On success, this endpoint returns status code 200 and the list of restaurants. On failure, it aborts with a 404 error code. This route also returns the user information stored in the Flask session. ''' @app.route('/restaurants') @requires_auth def get_restaurants(): try: restaurants = Restaurant.query.all() restaurants_short = [] if len(restaurants) != 0: any_restaurants = True for restaurant in restaurants: restaurants_short.append(restaurant.short()) return render_template('dashboard.html', userinfo=session['profile'], userinfo_pretty=json.dumps(session['jwt_payload'], indent=4), success=True, restaurants=restaurants_short) except Exception: abort(404) ''' GET /restaurants/<id> It should be a public endpoint. It should contain the Restaurant.long() data representation. On success, this endpoint returns status code 200, the restaurant data, and a list of reviews on that restaurant. On failure, it aborts with a 404 error code. This route also returns the user information stored in the Flask session. ''' @app.route('/restaurants/<int:id>') @requires_auth def get_restaurant(id): try: restaurant = Restaurant.query.filter(Restaurant.id == id).one_or_none() if restaurant != None: reviews = Review.query.filter(Review.restaurant_id == id).all() if len(reviews) != 0: reviews_format = [] for review in reviews: reviews_format.append(review.format()) else: reviews_format = False return render_template('restaurant.html', success=True, userinfo=session['profile'], restaurant=restaurant.long(), reviews=reviews_format, form=restaurant.to_form()) except Exception: abort(404) ''' GET /restaurants/<id>/new_reviews Renders the template associated with the new_review form. ''' @app.route('/restaurants/<int:id>/new_reviews', methods=['GET']) @requires_auth def create_review_form(id): try: restaurant = Restaurant.query.filter(Restaurant.id == id).one_or_none() if restaurant != None: form = ReviewForm() return render_template('forms/new_review.html', userinfo=session['profile'], restaurant=restaurant, form=form) else: abort(404) except Exception: abort(404) ''' POST /restaurants/<id>/new_reviews Post a new review in db. ''' @app.route('/restaurants/<int:id>/new_reviews', methods=['POST']) @requires_auth def create_review_submission(id): error = False body = {} try: form = request.form review = Review( restaurant_id = id, name = form['name'], date = datetime.now(), rating = form['rating'], comments = form['comments'] ) review.insert() body['name'] = form['name'] except: error = True db.session.rollback() print(sys.exc_info()) finally: db.session.close() if not error: # on successful db insert, flash success flash('Thank you ' + body['name'] + ' for your review.', 'success') else: flash('An error occurred. Review could not be listed.', 'error') return redirect('/restaurants/'+str(id)+'/new_reviews') ''' GET /new_restaurants Renders the template associated with the new_restaurant form. ''' @app.route('/new_restaurants', methods=['GET']) @requires_auth def create_restaurant_form(): try: form = RestaurantForm() return render_template('forms/new_restaurant.html', userinfo=session['profile'], form=form) except Exception: abort(404) ''' POST /new_restaurants Post a new restaurant in db. ''' @app.route('/new_restaurants', methods=['POST']) @requires_auth def create_restaurant_submission(): error = False try: form = RestaurantForm(request.form) if form.validate_on_submit(): op_hours = format_operating_hours(form) if op_hours['success']: restaurant = Restaurant( name = form.name.data, borough = form.borough.data, photograph = form.photograph.data, img_description = form.img_description.data, address = form.address.data, latlng = [float(form.lat.data), float(form.lng.data)], cuisine = form.cuisine.data, operating_hours = op_hours['week_hours'] ) restaurant.insert() else: error = True message = 'Wrong formating of operating hours.' else: error = True message = 'The form contains invalid data.' except: error = True message = 'The restaurant could not be listed.' db.session.rollback() print(sys.exc_info()) finally: db.session.close() if not error: flash(form.name.data + ' was successfully listed.', 'success') return redirect('/new_restaurants') else: flash('An error occurred. ' + message, 'error') return render_template('forms/new_restaurant.html', userinfo=session['profile'], form=form) ''' PATCH /restaurants/<id> Where <id> is the existing model id. It should respond with a 404 error if <id> is not found. It should update the corresponding row for <id> It should require the 'patch:restaurants' permission. It should contain the restaurant.long() data representation. ''' @app.route('/restaurants/<int:id>', methods=['PATCH']) @requires_auth def update_restaurant(id): error = False try: form = RestaurantForm(request.form) restaurant = Restaurant.query.filter(Restaurant.id == id).one_or_none() if restaurant != None: if form.validate_on_submit(): op_hours = format_operating_hours(form) if op_hours['success']: restaurant.name = form.name.data restaurant.borough = form.borough.data restaurant.photograph = form.photograph.data restaurant.img_description = form.img_description.data restaurant.address = form.address.data restaurant.latlng = [float(form.lat.data), float(form.lng.data)] restaurant.cuisine = form.cuisine.data restaurant.operating_hours = op_hours['week_hours'] restaurant.update() else: error = True message = 'Wrong formating of operating hours.' else: error = True message = 'The form contains invalid data.' else: abort(404) except Exception: error = True message = 'The restaurant could not be updated.' db.session.rollback() print(sys.exc_info()) if not error: flash(form.name.data + ' was successfully updated.', 'success') return jsonify({ "success": True, "restaurant": restaurant.long() }) else: flash('An error occurred. ' + message, 'error') if form.errors: for field, f_errors in form.errors.items(): if f_errors: for error in f_errors: flash(field + ': ' + error, 'error') abort(404) ''' DELETE /restaurants/<id> Where <id> is the existing model id. It should respond with a 404 error if <id> is not found. It should delete the corresponding row for <id> It should require the 'delete:restaurants' permission. Returns status code 200 and json {"success": True, "delete": id, "name": restaurant.name} where id is the id of the deleted record or appropriate status code indicating reason for failure. ''' @app.route('/restaurants/<int:id>', methods=['DELETE']) @requires_auth def delete_restaurant(id): try: restaurant = Restaurant.query.filter(Restaurant.id == id).one_or_none() if restaurant != None: reviews = Review.query.filter(Review.restaurant_id == id).all() if len(reviews) != 0: for review in reviews: review.delete() restaurant.delete() flash(restaurant.name + ' was successfully deleted.', 'success') return jsonify({ "success": True, "delete": id, "name": restaurant.name }) except Exception: db.session.rollback() print(sys.exc_info()) flash('An error occurred. The restaurant could not be deleted.', 'error') return abort(404) # Error handler for 401 @app.errorhandler(401) def unauthorized(error): return jsonify({ "success": False, "error": 401, "message": "unauthorized" }), 401 # Error handler for 404 @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(500) def server_error(error): return jsonify({ "success": False, "error": 500, "message": "internal server error" }), 500 return app
return token.to_dict() def update_token(name, token): item = models.OAuth2Token(name=name, user=current_user._get_current_object()).first() item.token_type = token.get("token_type", "Bearer") item.access_token = token.get("access_token") item.refresh_token = token.get("refresh_token") item.expires = datetime.datetime.utcfromtimestamp(token.get("expires_at")) item.save() return item oauth2_client = OAuth() def handle_authorize_google(remote, token, user_info): if not user_info: return redirect(url_for("accounts.login")) user = models.User.objects( me.Q(username=user_info.get("name")) | me.Q(email=user_info.get("email"))).first() if not user: user = models.User( username=user_info.get("name"), email=user_info.get("email"), first_name=user_info.get("given_name"),
def create_authenticated_app(test_config=None): # Create and Configure OKIT Web Designer App app = Flask(__name__, instance_relative_config=True) # Load Config if test_config is None: app.config.from_pyfile('config.py', silent=True) else: app.config.from_mapping(test_config) # Ensure if instance folder exists try: os.makedirs(app.instance_path) except OSError: pass # Add Upload location app.config['UPLOADS_FOLDER'] = '/okit/uploads' # The secret key must be static to be the same for all gunicorn workers app.secret_key = '8980ffsd675747jjjh' idcs_metadata_url = app.config[ 'IDCS_API_BASE_URL'] + '/.well-known/openid-configuration' oauth = OAuth(app) idcs = oauth.register(name='idcs', server_metadata_url=idcs_metadata_url, client_kwargs={'scope': 'openid email profile'}) if 'OKIT_SERVER_BASE' not in app.config: app.config['OKIT_SERVER_BASE'] = 'http://' + socket.getfqdn() @app.route('/favicon.ico') def favicon(): return send_from_directory(os.path.join(app.root_path, 'static'), 'favicon.ico', mimetype='image/vnd.microsoft.icon') from . import okitWebDesigner # Login Step 1 - Redirect to IDCS @okitWebDesigner.bp.route('/login', methods=(['GET', 'POST'])) def login(): return idcs.authorize_redirect(app.config['OKIT_SERVER_BASE'] + url_for('okit.postlogin')) # Login Step 2 - Local local token handling @okitWebDesigner.bp.route('/postlogin', methods=(['GET', 'POST'])) def postlogin(): token = idcs.authorize_access_token() userinfo = idcs.parse_id_token(token) session['username'] = userinfo['user_displayname'] session['home_region'] = app.config['OCI_HOME_REGION'] session['tenant'] = app.config['OCI_TENANT'] logger.info(f"App Config {app.config}") end_session_endpoint = idcs.server_metadata['end_session_endpoint'] logout_redirect_url = { 'post_logout_redirect_url': app.config['OKIT_SERVER_BASE'] + url_for('okit.postlogout'), 'id_token_hint': token['id_token'] } logout_url = end_session_endpoint + '?post_logout_redirect_url=' + str( logout_redirect_url['post_logout_redirect_url'] ) + '&id_token_hint=' + str(logout_redirect_url['id_token_hint']) session['logout'] = logout_url return redirect(url_for('okit.designer'), code=302) # Logout Step 1 - Handled by IDCS # Logout Step 2 - Local cleanup @okitWebDesigner.bp.route('/logout', methods=(['GET', 'POST'])) def logout(): session.pop('username', None) session.pop('logout', None) session.pop('tenant', None) session.pop('home_region', None) return Response(status=200) # Logout Step 3 - Local redirect to home page @okitWebDesigner.bp.route('/postlogout', methods=(['GET', 'POST'])) def postlogout(): session.pop('username', None) session.pop('logout', None) session.pop('tenant', None) session.pop('home_region', None) return redirect(url_for('okit.designer'), code=302) app.register_blueprint(okitWebDesigner.bp) from . import okitPricing app.register_blueprint(okitPricing.bp) from . import okitOci app.register_blueprint(okitOci.bp) from . import okitImport app.register_blueprint(okitImport.bp) @app.route('/') def index(): return login() return app
from flask import Blueprint, render_template, redirect, url_for, current_app, request, flash from werkzeug.security import generate_password_hash, check_password_hash from flask_login import LoginManager, login_user, logout_user, login_required from authlib.integrations.flask_client import OAuth from form import LoginForm, SignupForm from db import User, db, Category, createTutorial from config import OAUTH_CONFIG oauth = OAuth(current_app) bp = Blueprint("auth", __name__, url_prefix="/auth") login_manager = LoginManager() login_manager.init_app(current_app) login_manager.login_view = 'auth.login' error_msgs = { "duplicate_user": "******", "duplicate_email": "이미 존재하는 이메일입니다. 다른 이메일을 사용하세요.", "user_not_exist": "존재하지 않는 이름입니다. 회원가입을 먼저 해주세요.", "wrong_password": "******" } google = oauth.register( name='google', client_id=OAUTH_CONFIG['client_id'], client_secret=OAUTH_CONFIG['client_secret'], access_token_url='https://accounts.google.com/o/oauth2/token', access_token_params=None, authorize_url='https://accounts.google.com/o/oauth2/auth', authorize_params=None, api_base_url='https://www.googleapis.com/oauth2/v1/',
def test_create_client(self): app = Flask(__name__) oauth = OAuth(app) self.assertIsNone(oauth.create_client('dev')) oauth.register('dev', client_id='dev') self.assertIsNotNone(oauth.create_client('dev'))
class Login(object): app = Flask(__name__) oauth = OAuth(app) auth0 = oauth.register( name='google', client_id='YOUR_CLIENT_ID', client_secret='YOUR_CLIENT_SECRET', access_token_url='https://accounts.google.com/o/oauth2/token', access_token_params=None, authorize_url='https://accounts.google.com/o/oauth2/token', authorize_params=None, api_base_url="https://www.googleapis.com/oauth2/v1", client_kwargs={ 'scope': 'openid profile email', }, ) @app.route('/login') def Login(self): return self.auth0.authorize_redirect(redirect_uri='login.html') @app.route('/logout') def Logout(self): # Clear session stored data session.clear() # Redirect user to logout endpoint params = { 'returnTo': url_for('home', _external=True), 'client_id': 'YOUR_CLIENT_ID' } return redirect(self.auth0.api_base_url + '/v2/logout?' + urlencode(params)) @app.route('/callback') def CallbackHandling(self): # Handles response from token endpoint self.auth0.authorize_access_token() resp = self.auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['id'], 'password': userinfo['password'], 'name': userinfo['name'], 'children': userinfo['children'], 'phone number': userinfo['phone number'] } return redirect('/dashboard') def RequiresAuth(f): @wraps(f) def decorated(*args, **kwargs): if 'profile' not in session: # Redirect to Login page here return redirect('/') return f(*args, **kwargs) return decorated @app.route('/dashboard') @RequiresAuth def Dashboard(): return render_template('dashboard.html', userinfo=session['profile'], userinfo_pretty=json.dumps( session['jwt_payload'], indent=4))
from flask import Flask from flask_login import LoginManager from flask_sqlalchemy import SQLAlchemy from authlib.integrations.flask_client import OAuth flask_app = Flask(__name__) flask_app.config.from_object('config') db = SQLAlchemy(flask_app) oauth = OAuth(flask_app) oauth.register( name='google', client_id=flask_app.config.get('GOOGLE_OAUTH_CLIENT_ID'), client_secret=flask_app.config.get('GOOGLE_OAUTH_CLIENT_SECRET'), client_kwargs={'scope': 'https://www.googleapis.com/auth/userinfo.email'}, api_base_url='https://www.googleapis.com/oauth2/v1/', request_token_url=None, access_token_method='POST', access_token_url='https://accounts.google.com/o/oauth2/token', authorize_url='https://accounts.google.com/o/oauth2/auth') login_manager = LoginManager() login_manager.init_app(flask_app) login_manager.login_view = "login" from app import views, models, forms, api, context_processors
def create_app(test_config=None): app = Flask(__name__, static_folder='../client/build', static_url_path='') setup_db(app) oauth = OAuth(app) CORS(app, resources={ r"*": {"origins": [ os.environ["FRONT_END_HOST"], "http://*****:*****@app.route('/') def serve(): try: return send_from_directory(app.static_folder, 'index.html') except Exception as ex: flash("An error occurred.") print(sys.exc_info()) abort(404) @app.route('/callback') def callback_handling(): access_data = auth0.authorize_access_token() # Handles response from token endpoint access_token = access_data['access_token'] id_token = access_data['id_token'] resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'], 'nickname': userinfo['nickname'], } if session['profile']['user_id'] is None: raise ValueError("No defined user from authentication.") matching_observer = Observer.query.filter(Observer.auth0_id == session['profile']['user_id']).first() if matching_observer is None: # this should imply that this is a sign-up operation by a new user matching_observer = add_new_user(session['profile']['user_id']) final_response = make_response(redirect(f"{os.environ['FRONT_END_HOST']}/#/dashboard")) final_response.set_cookie(USER_ACCESS_TOKEN_KEY, access_token) final_response.set_cookie(USER_ID_TOKEN_KEY, id_token) final_response.set_cookie(OBSERVER_DATABASE_ID_KEY, str(matching_observer.id)) final_response.set_cookie(USER_PICTURE_KEY, session['profile']['picture']) final_response.set_cookie(USER_NICKNAME_KEY, session['profile']['nickname']) return final_response def add_new_user(user_auth0_id): nonlocal management_api_access_token conn = http.client.HTTPSConnection(os.environ["AUTH0_DOMAIN"]) if management_api_access_token is None: management_api_access_token = acquire_management_api_access_token() payload = "{ \"roles\": [ \"" + os.environ["DISASTER_REPORTER_ROLE_ID"] + "\" ] }" headers = { 'content-type': "application/json", 'authorization': f"Bearer {management_api_access_token}", 'cache-control': "no-cache" } try: conn.request("POST", f"/api/v2/users/{user_auth0_id}/roles", payload, headers) conn.getresponse() new_observer = Observer(session['profile']['nickname'], user_auth0_id, session['profile']['picture']) # add entry to database new_observer.insert() return new_observer except Exception as ex: flash("An error occurred.") print("\n\n\n") print("ex:", ex) print("\n\n\n") print(sys.exc_info()) def acquire_management_api_access_token(): print("\nAcquiring management API access token...\n") conn = http.client.HTTPSConnection(os.environ['AUTH0_DOMAIN']) payload = "{\"client_id\":\"" + os.environ['AUTH0_CLIENT_ID'] + "\",\"client_secret\":\"" + os.environ['CLIENT_SECRET'] + "\",\"audience\":\"https://" + os.environ['AUTH0_DOMAIN'] + "/api/v2/\",\"grant_type\":\"client_credentials\"}" headers = { 'content-type': "application/json" } conn.request("POST", "/" + os.environ['AUTH0_DOMAIN'] + "/oauth/token", payload, headers) res = conn.getresponse() data = res.read() json_data = json.loads(data) return json_data["access_token"] # update most recent update datetime of corresponding disaster def update_parent_disaster_of_witness_report(disaster_id): disaster_match = Disaster.query.filter( Disaster.id == disaster_id ).first() if disaster_match is None: raise ValueError(f"No matching disaster id: {body.get('disaster_id')}") disaster_match.update() @app.route('/my-login') def login(): return auth0.authorize_redirect(redirect_uri=f"{os.environ['BACK_END_HOST']}/callback", audience=os.environ["API_AUDIENCE"]) @app.route('/my-logout') def logout(): session.clear() # Clear session stored data # params = {'returnTo': f'{os.environ["FRONT_END_HOST"]}/#/404', 'client_id': os.environ["AUTH0_CLIENT_ID"]} # Redirect user to logout endpoint params = {'returnTo': f'{os.environ["FRONT_END_HOST"]}', 'client_id': os.environ["AUTH0_CLIENT_ID"]} # Redirect user to logout endpoint response = make_response(redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params))) response.delete_cookie(USER_ACCESS_TOKEN_KEY) response.delete_cookie(USER_ID_TOKEN_KEY) response.delete_cookie(OBSERVER_DATABASE_ID_KEY) response.delete_cookie(USER_PICTURE_KEY) response.delete_cookie(USER_NICKNAME_KEY) return response @app.route('/api') def get_greeting(): excited = os.environ['EXCITED'] greeting = "Hello" if excited == 'true': greeting = greeting + "!!!!!" return greeting @app.route('/api/coolkids') def be_cool(): return "Be cool, man, be coooool! You're almost an FSND grad!" def get_page_of_resource(arr, page): if page <= 0: raise ValueError("A non-positive page is not recognized.") start = min((page - 1) * PAGE_SIZE, len(arr)) end = min(page * PAGE_SIZE, len(arr)) return arr[start:end] def get_random_report_data(witness_reports): disaster_map = dict() for (disaster_id, observer_id, comment, image_url) in witness_reports: if disaster_id not in disaster_map: disaster_map[disaster_id] = list() disaster_map[disaster_id].append((observer_id, comment, image_url)) random_report_data = dict() for k, v in disaster_map.items(): reports_with_images = list(filter(lambda report_data: report_data[2] is not None, v)) if len(reports_with_images) > 0: random_report_data[k] = reports_with_images[randrange(len(reports_with_images))] else: random_report_data[k] = v[randrange(len(v))] return random_report_data def combine_disaster_data( formatted_disasters: list, formatted_affected_people: list, random_report_data: dict, all_users: list) -> dict: affected_map = {A[0]: (A[1], A[2], A[3], A[4], A[5]) for A in formatted_affected_people} user_map = { user["id"]: { "username": user["username"], "photograph_url": user["photograph_url"] } for user in all_users } for disaster in formatted_disasters: if disaster["id"] in affected_map: disaster["people_affected"] = affected_map[disaster["id"]][0] disaster["average_severity"] = affected_map[disaster["id"]][1] disaster["first_observance"] = affected_map[disaster["id"]][2] disaster["last_observance"] = affected_map[disaster["id"]][3] disaster["num_reports"] = affected_map[disaster["id"]][4] else: disaster["people_affected"] = None disaster["average_severity"] = None disaster["first_observance"] = None disaster["last_observance"] = None disaster["num_reports"] = 0 if disaster["id"] in random_report_data: user_info = random_report_data[disaster["id"]] disaster["random_observer"] = \ user_map[user_info[0]]["username"] disaster["random_observer_url"] = user_map[user_info[0] ]["photograph_url"] disaster["random_comment"] = user_info[1] disaster["random_witness_image"] = user_info[2] else: disaster["random_observer"] = None disaster["random_observer_url"] = None disaster["random_comment"] = None disaster["random_witness_image"] = None return formatted_disasters def combine_single_disaster_data( disaster, formatted_additional_data, formatted_reports, page) -> dict: all_data = copy(disaster) all_data["people_affected"] = formatted_additional_data[1] all_data["average_severity"] = formatted_additional_data[2] all_data["first_observance"] = formatted_additional_data[3] all_data["last_observance"] = formatted_additional_data[4] all_data["num_reports"] = formatted_additional_data[5] all_data["reports"] = get_page_of_resource(formatted_reports, page) for report in all_data["reports"]: del report["disaster_id"] return all_data ''' A GET endpoint to get all disasters or disasters by disaster type. This endpoint takes an optional parameter 'disaster_type' to filter by the disaster_type. If no disaster_type parameter is provided, then the data for all disasters is returned. If the disaster_type parameter is provided but is not one of the recognized enums, a 404 error is raised. The endpoint also has an optional page request parameter corresponding to the paginated page to use. The current default for the size of a page is 10. If the page is non-positive, then a 422 error occurs. The endpoint has another optional parameter: query. This is to match informal_name or official_name. This endpoint does not return any of the witness reports associated with a specific disaster. For each disaster, the data in the disaster table is returned along with a random comment and the author of that comment from a witness of the disaster (if any) and some descriptive data about the disaster reports per disaster (namely, the number of reports, the first observance, the last observance, and the number of people affected). ''' @app.route('/api/disasters') def disasters(): page = int(request.args.get("page", "1")) disaster_type = request.args.get("disaster_type") query_string = request.args.get("query") use_inclusive_search = False try: if disaster_type is not None and disaster_type.upper( ) not in NaturalDisasterEnum.__members__: raise AttributeError("Unrecognized natural disaster type.") formatted_disasters = [] if query_string is not None and disaster_type is None and query_string.upper() in NaturalDisasterEnum.__members__: disaster_type = query_string use_inclusive_search = True if query_string is None and disaster_type is None: disasters = Disaster.query.order_by(Disaster.last_update_datetime.desc()).all() elif query_string is None and disaster_type is not None: disasters = Disaster.query.filter(Disaster.disaster_type == disaster_type.upper()).order_by(Disaster.last_update_datetime.desc()).all() elif query_string is not None and disaster_type is None: disasters = Disaster.query.filter(or_( Disaster.informal_name.ilike('%' + query_string + '%'), Disaster.official_name.ilike('%' + query_string + '%') )).order_by(Disaster.last_update_datetime.desc()).all() elif query_string is not None and disaster_type is not None and not use_inclusive_search: disasters = Disaster.query.filter(and_( or_( Disaster.informal_name.ilike('%' + query_string + '%'), Disaster.official_name.ilike('%' + query_string + '%'), ), Disaster.disaster_type == disaster_type.upper() )).order_by(Disaster.last_update_datetime.desc()).all() else: disasters = Disaster.query.filter(or_( Disaster.informal_name.ilike('%' + query_string + '%'), Disaster.official_name.ilike('%' + query_string + '%'), Disaster.disaster_type == disaster_type.upper() )).order_by(Disaster.last_update_datetime.desc()).all() total_disasters = len(disasters) formatted_disasters = get_page_of_resource( [disaster.format() for disaster in disasters], page) additional_data = WitnessReport.query.with_entities( WitnessReport.disaster_id, func.max(WitnessReport.people_affected), func.avg(WitnessReport.severity), func.min(WitnessReport.event_datetime), func.max(WitnessReport.event_datetime), func.count(WitnessReport.disaster_id), ).group_by(WitnessReport.disaster_id).all() formatted_additional_data = [ (report[0], report[1], float(report[2]) if report[2] else None, report[3], report[4], report[5]) for report in additional_data] # disaster_id, observer_id, comment all_witness_reports = WitnessReport.query.with_entities( WitnessReport.disaster_id, WitnessReport.observer_id, WitnessReport.comment, WitnessReport.image_url).all() # dictionary containing some random report data per disaster random_report_data = get_random_report_data(all_witness_reports) all_users = [user.format() for user in Observer.query.all()] return jsonify( { 'total_disasters': total_disasters, 'disasters': combine_disaster_data( formatted_disasters, formatted_additional_data, random_report_data, all_users ), }) except AttributeError as ex: flash('An error occurred.') print(sys.exc_info()) abort(404) except Exception as ex: flash("An error occurred.") print(sys.exc_info()) abort(422) ''' A GET endpoint to get the details of a single disaster, including: - id (int) - informal_name (str) - official_name (str) - disaster_type (enum str) - is_ongoing (bool) - location (list of pair of floats) - people_affected (maximum over all reports if there exist reports, otherwise None) - average_severity (float) - first_observance (datetime str) - last_observance (datetime str) - num_reports (int; if no reports exist, it is 0) - reports (list) The reports property contains a list of individual witness reports, each including: - id (int) - observer_id (int) - event_datetime (datetime str) - severity (int) - image_url (str) - comment (str) - people_affected (int) - location (list of pair of floats) - username (str) - user_photograph_url (str) A page query parameter allows the page setting of the reports with 10 per page; if the page is invalid, a 422 error is returned; otherwise, if the id provided does not correspond to an existing disaster, a 404 error is returned ''' @app.route('/api/disasters/<disaster_id>') def retrieve_disaster_by_id(disaster_id): page = int(request.args.get("page", "1")) try: if page <= 0: raise ValueError("The request page must be positive.") disaster = Disaster.query.filter( Disaster.id == disaster_id).first() additional_data = WitnessReport.query.filter( WitnessReport.disaster_id == disaster_id).with_entities( WitnessReport.disaster_id, func.max(WitnessReport.people_affected), func.avg(WitnessReport.severity), func.min(WitnessReport.event_datetime), func.max(WitnessReport.event_datetime), func.count(WitnessReport.disaster_id), ).group_by(WitnessReport.disaster_id).first() if additional_data: formatted_additional_data = ( additional_data[0], additional_data[1], float( additional_data[2]) if additional_data[2] else None, additional_data[3], additional_data[4], additional_data[5]) else: formatted_additional_data = (None, None, None, None, None, 0) reports, observer_map = WitnessReport.observer_join(disaster_id) formatted_reports = [report.format() for report in reports] for fr in formatted_reports: fr["username"] = observer_map[fr["observer_id"]][0] fr["user_photograph_url"] = observer_map[fr["observer_id"]][1] return jsonify( combine_single_disaster_data( disaster.format(), formatted_additional_data, formatted_reports, page)) except AttributeError as ex: flash("An attribute error occurred.") print(sys.exc_info()) abort(404) except Exception as ex: flash("An error occurred.") print(sys.exc_info()) abort(422) ''' A GET endpoint to retrieve a page of the set of observers, including the observers' ids, usernames, and the URLs of their user photographs. The page can be specified as a query parameter and if none is provided, it will be assumed to be 1. The use of an invalid page (i.e. a non-positive page) will cause a status 422 error to be returned. ''' @app.route('/api/observers', methods=["GET"]) @requires_auth('get:observers') def get_all_observers(payload): page = int(request.args.get("page", "1")) try: observers = Observer.query.all() page_observers = get_page_of_resource(observers, page) formatted_observers = [observer.format() for observer in page_observers] return jsonify({"observers": formatted_observers}) except Exception as ex: flash("An error occurred.") print(sys.exc_info()) abort(422) ''' A GET endpoint to retrieve a witness report of the set of observers, including: - witness report id - disaster id - observer id - event datetime - severity - image URL - comment - number of peopl affected - location (array of latitude and longitude) When an id (witness_report_id) is provided which does not correspond to an existing witness report, a 404 error is returned. ''' @app.route('/api/witnessreports/<witness_report_id>', methods=["GET"]) def retrieve_witness_report_by_id(witness_report_id): try: witness_report = WitnessReport.query.filter( WitnessReport.id == witness_report_id).first() return jsonify(witness_report.format()) except Exception as ex: flash("An error occurred.") print(sys.exc_info()) abort(404) ''' A POST endpoint to insert a disaster into the database. The body for the request is a dictionary with the following keys: - informal_name (str) - official_name (str, required, must be unique) - disaster_type (str, disaster enum) - is_ongoing (bool, default True) - location_latitude (float, required) - location_longitude (float, required) If the request's disaster data does not meet the conditions of requirement described above, a 400 status code error is returned ''' @app.route('/api/disasters', methods=["POST"]) @requires_auth('post:disasters') def send_disaster(payload): try: body = request.get_json() disaster = Disaster( body.get("informal_name"), body.get("official_name"), body.get("disaster_type").lower(), body.get("is_ongoing"), body.get("location_latitude"), body.get("location_longitude"), ) disaster.insert() return jsonify({"id": disaster.id}) except Exception as ex: flash("An error occurred.") print(f"ex: {ex}") print(sys.exc_info()) abort(400) ''' A POST endpoint to insert a user into the database. THe body for the request is a dictionary with the following keys: - username (str, required, unique) - photograph_url (str) If the request's data does not meet the conditions of requirement described above, then a 400 status code error is returned ''' @app.route('/api/observers', methods=["POST"]) def send_user(): try: body = request.get_json() observer = Observer(body.get("username"), body.get("auth0_id"), body.get("photograph_url")) observer.insert() return jsonify({"id": observer.id}) except Exception as ex: flash("An error occurred.") print(f"ex: {ex}") print(sys.exc_info()) abort(400) ''' A POST endpoint to insert a witness's report into the database. The body for the request is a dictionary with the following keys: - disaster_id (int, required) - observer_id (int, required) - event_datetime (datetime str, required) - severity (int) - image_url (str) - comment (str) - people_affected (int, default = 0) - location_latitude (float) - location_longitude (float) If the request's data does not meet the conditions of requirement described above, then a 400 status code error is returned ''' @app.route('/api/witnessreports', methods=["POST"]) @requires_auth('post:witnessreports') def send_witness_report(payload): try: body = request.get_json() witness_report = WitnessReport( body.get("disaster_id"), body.get("observer_id"), body.get("event_datetime"), body.get("severity"), # optional body.get("image_url"), # optional body.get("comment"), # optional body.get("people_affected"), body.get("location_latitude"), body.get("location_longitude") ) witness_report.insert() # update most recent update datetime of corresponding disaster update_parent_disaster_of_witness_report(body.get('disaster_id')) return jsonify({"id": witness_report.id}) except Exception as ex: flash("An error occurred.") print(sys.exc_info()) abort(400) ''' A PATCH endpoint to update a disaster. The body of the request is a dictionary with the following keys, all of which are optional except for id: - id (int) - informal_name (str) - official_name (str) - disaster_type (str) - is_ongoing (str) - location_latitude (str) - location_longitude (str) If no id is provided, then a 400 status code error is returned. If an id is provided but it does not match that of any disaster in the database, a 404 status code error is returned. Otherwise, if there are any malformed parts of the update data dictionary, then a 422 error is thrown. ''' @app.route('/api/disasters', methods=["PATCH"]) @requires_auth('patch:disasters') def update_disaster(payload): try: body = request.get_json() if "id" not in body: raise AttributeError( "id is not present as a property in the sent data.") disaster = Disaster.query.filter(Disaster.id == body["id"]).first() if disaster is None: raise ValueError("Unrecognized disaster id") if "informal_name" in body: disaster.informal_name = body["informal_name"] if "official_name" in body: disaster.official_name = body["official_name"] if "disaster_type" in body: disaster.disaster_type = body["disaster_type"] if "is_ongoing" in body: disaster.is_ongoing = body["is_ongoing"] if "location_latitude" in body: disaster.location_latitude = body["location_latitude"] if "location_longitude" in body: disaster.location_longitude = body["location_longitude"] disaster.update() return jsonify(disaster.format()) except AttributeError as err: flash(str(err)) print(sys.exc_info()) abort(400) except ValueError as err: flash(str(err)) print(sys.exc_info()) abort(404) except Exception as err: flash("An error occurred.") print(sys.exc_info()) abort(422) ''' A PATCH endpoint to update a witness report of a disaster. The body of the request is a dictionary with the following keys, all of which are optional except for id (all fields except for id represent fields which are being changed): - id (int, required) - event_datetime (datetime str) - severity (int) - image_url (str) - comment (str) - people_affected (int) - location_latitude (str) - location_longitude (str) If no id is provided, then a 400 status code error is returned. If an id is provided but it does not match that of any witness report in the database, a 404 status code error is returned. Otherwise, if there are any malformed parts of the update data dictionary, then a 422 error is thrown. ''' @app.route('/api/witnessreports', methods=["PATCH"]) @requires_auth('patch:witnessreports') def update_witness_report(payload): try: body = request.get_json() if "id" not in body: raise AttributeError( "id is not present as a property in the sent data.") witness_report = WitnessReport.query.filter( WitnessReport.id == body["id"]).first() if witness_report is None: raise ValueError("Unrecognized witness report id") if "event_datetime" in body: witness_report.event_datetime = body["event_datetime"] if "severity" in body: witness_report.severity = body["severity"] if "image_url" in body: witness_report.image_url = body["image_url"] if "comment" in body: witness_report.comment = body["comment"] if "people_affected" in body: witness_report.people_affected = body["people_affected"] if "location_latitude" in body: witness_report.location_latitude = body["location_latitude"] if "location_longitude" in body: witness_report.location_longitude = body["location_longitude"] witness_report.update() # update most recent update datetime of corresponding disaster update_parent_disaster_of_witness_report(witness_report.disaster_id) return jsonify(witness_report.format()) except AttributeError as err: flash(str(err)) print(sys.exc_info()) abort(400) except ValueError as err: flash(str(err)) print(sys.exc_info()) abort(404) except Exception as err: flash("An error occurred.") print(sys.exc_info()) abort(422) ''' Verify that a user can delete a witness report. A user may only delete a witness report if he or she is an admin or the witness report was created by the user. Otherwise, a 403 unauthorized error will be raised. ''' def validate_delete_witness_report(witness_report, user_data_payload): if "post:disasters" in user_data_payload["permissions"]: # user is an admin return True author_observer = Observer.query.filter(Observer.id == witness_report.observer_id).first() if author_observer is None: abort(401) if author_observer.auth0_id == user_data_payload["sub"]: return True else: abort(403) ''' A DELETE endpoint for deleting a witness report. If the listed id does not exist among witness reports, a 400 status error is returned. ''' @app.route('/api/witnessreports/<witness_report_id>', methods=["DELETE"]) @requires_auth('delete:witnessreports') def remove_witness_report(payload, witness_report_id): try: witness_report = WitnessReport.query.filter( WitnessReport.id == witness_report_id).first() if witness_report is None: raise AttributeError("Entry not found") validate_delete_witness_report(witness_report, payload) # update most recent update datetime of corresponding disaster update_parent_disaster_of_witness_report(witness_report.disaster_id) witness_report.delete() return jsonify({ "success": True, "delete": witness_report_id, }) except AttributeError as err: flash(str(err)) print(sys.exc_info()) abort(400) @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "malformed request - " + str(error) }), 400 @app.errorhandler(401) def authorization_header_missing(error): return jsonify({ "success": False, "error": 401, "message": "authorization issue - " + str(error) }), 401 @app.errorhandler(403) def authorization_incorrect_permission(error): return jsonify({ "success": False, "error": 403, "message": "authorization incorrect permission - " + str(error) }), 403 @app.errorhandler(404) def resource_not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found - " + str(error) }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable - " + str(error), }), 422 return app
def create_oauth_app(service_config, name): service_oauth = OAuth(app) service_app = service_oauth.register(name, **service_config) return service_app
import requests from requests.exceptions import HTTPError from flask import session, redirect, url_for, request, render_template from flask import current_app as app from authlib.integrations.flask_client import OAuth import invite0.config as conf import invite0.auth0.management_client as auth0_mgmt from invite0.auth0.exceptions import UserNotLoggedIn, CanNotUnsetFieldError _oauth_client = OAuth(app).register( 'auth0', client_id=conf.AUTH0_CLIENT_ID, client_secret=conf.AUTH0_CLIENT_SECRET, api_base_url=f'https://{conf.AUTH0_DOMAIN}', access_token_url=f'https://{conf.AUTH0_DOMAIN}/oauth/token', authorize_url=f'https://{conf.AUTH0_DOMAIN}/authorize', client_kwargs={'scope': 'openid profile email'}, ) class _CurrentUser: """ An abstraction over the Flask `session` and Auth0 APIs An instance of this class has no state of its own: the Flask `session` holds the state, which the instance merely reflects. There is no need to instantiate this class outside this module -- just import `current_user` (instantiated below) -- but there is no harm in
def create_app(test_config=None): app = Flask(__name__) app.secret_key = SECRET_KEY setup_db(app) CORS(app) @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Origin', '*') response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE,OPTIONS') return response oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url=AUTH0_BASE_URL + '/oauth/token', authorize_url=AUTH0_BASE_URL + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) def requires_auth(permission='', permissions=[]): def requires_auth_decorator(f): @wraps(f) def decorated(*args, **kwargs): if PROFILE_KEY not in session: abort(401) return redirect('/login') if permission not in session['permissions']: abort(401) return f(*args, **kwargs) return decorated return requires_auth_decorator # Controllers API @app.route('/') def home(): return redirect('/login') @app.route('/callback') def callback_handling(): auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() url = 'https://sp31051998.auth0.com/api/v2/users/' + userinfo[ 'sub'] + '/permissions' headers = { "authorization": "Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InBaV1Q1UjJ6QURlcFFKTF91cHM0eCJ9.eyJpc3MiOiJodHRwczovL3NwMzEwNTE5OTguYXV0aDAuY29tLyIsInN1YiI6ImZPT2gyUGVVblRWdGtGcFJtbm9wUGRvNndVbTVydmp2QGNsaWVudHMiLCJhdWQiOiJodHRwczovL3NwMzEwNTE5OTguYXV0aDAuY29tL2FwaS92Mi8iLCJpYXQiOjE1OTI3NTMxMjIsImV4cCI6MTU5MjgzOTUyMiwiYXpwIjoiZk9PaDJQZVVuVFZ0a0ZwUm1ub3BQZG82d1VtNXJ2anYiLCJzY29wZSI6InJlYWQ6Y2xpZW50X2dyYW50cyBjcmVhdGU6Y2xpZW50X2dyYW50cyBkZWxldGU6Y2xpZW50X2dyYW50cyB1cGRhdGU6Y2xpZW50X2dyYW50cyByZWFkOnVzZXJzIHVwZGF0ZTp1c2VycyBkZWxldGU6dXNlcnMgY3JlYXRlOnVzZXJzIHJlYWQ6dXNlcnNfYXBwX21ldGFkYXRhIHVwZGF0ZTp1c2Vyc19hcHBfbWV0YWRhdGEgZGVsZXRlOnVzZXJzX2FwcF9tZXRhZGF0YSBjcmVhdGU6dXNlcnNfYXBwX21ldGFkYXRhIHJlYWQ6dXNlcl9jdXN0b21fYmxvY2tzIGNyZWF0ZTp1c2VyX2N1c3RvbV9ibG9ja3MgZGVsZXRlOnVzZXJfY3VzdG9tX2Jsb2NrcyBjcmVhdGU6dXNlcl90aWNrZXRzIHJlYWQ6Y2xpZW50cyB1cGRhdGU6Y2xpZW50cyBkZWxldGU6Y2xpZW50cyBjcmVhdGU6Y2xpZW50cyByZWFkOmNsaWVudF9rZXlzIHVwZGF0ZTpjbGllbnRfa2V5cyBkZWxldGU6Y2xpZW50X2tleXMgY3JlYXRlOmNsaWVudF9rZXlzIHJlYWQ6Y29ubmVjdGlvbnMgdXBkYXRlOmNvbm5lY3Rpb25zIGRlbGV0ZTpjb25uZWN0aW9ucyBjcmVhdGU6Y29ubmVjdGlvbnMgcmVhZDpyZXNvdXJjZV9zZXJ2ZXJzIHVwZGF0ZTpyZXNvdXJjZV9zZXJ2ZXJzIGRlbGV0ZTpyZXNvdXJjZV9zZXJ2ZXJzIGNyZWF0ZTpyZXNvdXJjZV9zZXJ2ZXJzIHJlYWQ6ZGV2aWNlX2NyZWRlbnRpYWxzIHVwZGF0ZTpkZXZpY2VfY3JlZGVudGlhbHMgZGVsZXRlOmRldmljZV9jcmVkZW50aWFscyBjcmVhdGU6ZGV2aWNlX2NyZWRlbnRpYWxzIHJlYWQ6cnVsZXMgdXBkYXRlOnJ1bGVzIGRlbGV0ZTpydWxlcyBjcmVhdGU6cnVsZXMgcmVhZDpydWxlc19jb25maWdzIHVwZGF0ZTpydWxlc19jb25maWdzIGRlbGV0ZTpydWxlc19jb25maWdzIHJlYWQ6aG9va3MgdXBkYXRlOmhvb2tzIGRlbGV0ZTpob29rcyBjcmVhdGU6aG9va3MgcmVhZDphY3Rpb25zIHVwZGF0ZTphY3Rpb25zIGRlbGV0ZTphY3Rpb25zIGNyZWF0ZTphY3Rpb25zIHJlYWQ6ZW1haWxfcHJvdmlkZXIgdXBkYXRlOmVtYWlsX3Byb3ZpZGVyIGRlbGV0ZTplbWFpbF9wcm92aWRlciBjcmVhdGU6ZW1haWxfcHJvdmlkZXIgYmxhY2tsaXN0OnRva2VucyByZWFkOnN0YXRzIHJlYWQ6dGVuYW50X3NldHRpbmdzIHVwZGF0ZTp0ZW5hbnRfc2V0dGluZ3MgcmVhZDpsb2dzIHJlYWQ6c2hpZWxkcyBjcmVhdGU6c2hpZWxkcyB1cGRhdGU6c2hpZWxkcyBkZWxldGU6c2hpZWxkcyByZWFkOmFub21hbHlfYmxvY2tzIGRlbGV0ZTphbm9tYWx5X2Jsb2NrcyB1cGRhdGU6dHJpZ2dlcnMgcmVhZDp0cmlnZ2VycyByZWFkOmdyYW50cyBkZWxldGU6Z3JhbnRzIHJlYWQ6Z3VhcmRpYW5fZmFjdG9ycyB1cGRhdGU6Z3VhcmRpYW5fZmFjdG9ycyByZWFkOmd1YXJkaWFuX2Vucm9sbG1lbnRzIGRlbGV0ZTpndWFyZGlhbl9lbnJvbGxtZW50cyBjcmVhdGU6Z3VhcmRpYW5fZW5yb2xsbWVudF90aWNrZXRzIHJlYWQ6dXNlcl9pZHBfdG9rZW5zIGNyZWF0ZTpwYXNzd29yZHNfY2hlY2tpbmdfam9iIGRlbGV0ZTpwYXNzd29yZHNfY2hlY2tpbmdfam9iIHJlYWQ6Y3VzdG9tX2RvbWFpbnMgZGVsZXRlOmN1c3RvbV9kb21haW5zIGNyZWF0ZTpjdXN0b21fZG9tYWlucyB1cGRhdGU6Y3VzdG9tX2RvbWFpbnMgcmVhZDplbWFpbF90ZW1wbGF0ZXMgY3JlYXRlOmVtYWlsX3RlbXBsYXRlcyB1cGRhdGU6ZW1haWxfdGVtcGxhdGVzIHJlYWQ6bWZhX3BvbGljaWVzIHVwZGF0ZTptZmFfcG9saWNpZXMgcmVhZDpyb2xlcyBjcmVhdGU6cm9sZXMgZGVsZXRlOnJvbGVzIHVwZGF0ZTpyb2xlcyByZWFkOnByb21wdHMgdXBkYXRlOnByb21wdHMgcmVhZDpicmFuZGluZyB1cGRhdGU6YnJhbmRpbmcgZGVsZXRlOmJyYW5kaW5nIHJlYWQ6bG9nX3N0cmVhbXMgY3JlYXRlOmxvZ19zdHJlYW1zIGRlbGV0ZTpsb2dfc3RyZWFtcyB1cGRhdGU6bG9nX3N0cmVhbXMgY3JlYXRlOnNpZ25pbmdfa2V5cyByZWFkOnNpZ25pbmdfa2V5cyB1cGRhdGU6c2lnbmluZ19rZXlzIHJlYWQ6bGltaXRzIHVwZGF0ZTpsaW1pdHMiLCJndHkiOiJjbGllbnQtY3JlZGVudGlhbHMifQ.lnL_zFiHF1NjkRIK6MBNusoUO9NawuxBXQzZ_ocvFHsu2fz8GFzVevMSfvQL1jp7QE_5Qv6I6Up1yiyogO616G8ZOHfIJnX2bpI3WecMLziVGRU-Lbw-SXx7JoqZeiKHkWGXEDCLepUrAGOHlhBaNFHYkZaXdB4hcxRXSjAFDbP-_fFm4spOjCTOr09kQLlyhAP_f1tPPd1u_jUZr5lDkahodTghP7_fmSncgCtdqbhc6ivFWnDppP2SgDMyft5hXV8YMSMEKIAkLwkB7oSou9bmxqdDcG3YmhRCbwdDTlLTY1-d8bHLXYDhx3SBK1hBNB7yJPhYNXHnP1mfxAKplw" } req = requests.get(url, headers=headers) permissions = [] response = json.loads(req.text) for permission in response: permissions.append(permission['permission_name']) session['permissions'] = permissions session[JWT_PAYLOAD] = userinfo session[PROFILE_KEY] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/dashboard') @app.route('/login') def login(): if session.get(PROFILE_KEY) is not None: return render_template('dashboard.html', permissions=session['permissions']) else: return auth0.authorize_redirect(redirect_uri=AUTH0_CALLBACK_URL, audience=AUTH0_AUDIENCE) @app.route('/logout') def logout(): session.clear() params = { 'returnTo': url_for('home', _external=True), 'client_id': AUTH0_CLIENT_ID } return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) @app.route('/dashboard') def dashboard(): if session.get(PROFILE_KEY) is not None: return render_template('dashboard.html', permissions=session['permissions']) else: return auth0.authorize_redirect(redirect_uri=AUTH0_CALLBACK_URL, audience=AUTH0_AUDIENCE) @app.route('/actors') #@requires_auth('get:actors') def retrieve_actors(): selection = Actor.query.order_by(Actor.id).all() current_actors = [data.format() for data in selection] if len(current_actors) == 0: abort(404) return jsonify({ 'success': True, 'actors': current_actors, 'total_actors': len(Actor.query.all()) }) @app.route('/actors', methods=['POST']) #@requires_auth('post:actors') def create_actor(): body = request.get_json() new_name = body.get('name', None) new_age = body.get('age', None) new_gender = body.get('gender', None) print(new_name, new_age, new_gender) try: actor = Actor(name=new_name, age=new_age, gender=new_gender) actor.insert() return jsonify({ 'success': True, 'created': actor.format(), }) except Exception as e: abort(422) @app.route('/actors/<int:actor_id>', methods=['PATCH']) #@requires_auth('patch:actors') def update_actor(actor_id): body = request.get_json() name = body.get('name', None) gender = body.get('gender', None) age = body.get('age', None) try: actor = Actor.query.filter(Actor.id == actor_id)\ .one_or_none() if actor is None: abort(404) if name is not None: actor.name = name if gender is not None: actor.gender = gender if age is not None: actor.age = age actor.update() return jsonify({ 'success': True, 'actor': actor.format(), }) except Exception as e: abort(422) @app.route('/actors/<int:actor_id>', methods=['DELETE']) #@requires_auth('delete:actors') def delete_actor(actor_id): try: actor = Actor.query.filter(Actor.id == actor_id)\ .one_or_none() actor.delete() return jsonify({ 'success': True, 'deleted': actor_id, }) except Exception as e: abort(404) @app.route('/movies') #@requires_auth('get:movies') def retrieve_movies(): selection = Movie.query.order_by(Movie.id).all() current_movies = [data.format() for data in selection] if len(current_movies) == 0: abort(404) print((current_movies[0]['release_date']).strftime("%x")) return jsonify({ 'success': True, 'movies': current_movies, 'total_movies': len(Movie.query.all()) }) @app.route('/movies', methods=['POST']) #@requires_auth('post:movies') def create_movie(): body = request.get_json() new_title = body.get('title', None) new_release_date = body.get('release_date', None) try: movie = Movie(title=new_title, release_date=new_release_date) movie.insert() return jsonify({ 'success': True, 'created': movie.format(), }) except Exception as e: abort(422) @app.route('/movies/<int:movie_id>', methods=['PATCH']) #@requires_auth('patch:movies') def update_movie(movie_id): body = request.get_json() title = body.get('title', None) release_date = body.get('release_date', None) try: movie = Movie.query.filter(Movie.id == movie_id)\ .one_or_none() if movie is None: abort(404) if title is not None: movie.title = title if release_date is not None: movie.release_date = release_date movie.update() return jsonify({ 'success': True, 'movie': movie.format(), }) except Exception as e: abort(422) @app.route('/movies/<int:movie_id>', methods=['DELETE']) #@requires_auth('delete:movies') def delete_movie(movie_id): try: movie = Movie.query.filter(Movie.id == movie_id)\ .one_or_none() movie.delete() return jsonify({ 'success': True, 'deleted': movie_id, }) except Exception as e: abort(404) @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(405) def not_found(error): return jsonify({ "success": False, "error": 405, "message": "method not allowed" }), 405 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 @app.errorhandler(500) def bad_request(error): return jsonify({ "success": False, "error": 500, "message": "Internal server error" }), 500 @app.errorhandler(401) def user_not_authorized(error): return jsonify({ "success": False, "error": 401, "message": "User not authorized" }), 401 return app
def create_app(test_config=None): # create and configure the app app = Flask(__name__) app.secret_key = constants.SECRET_KEY CORS(app) setup_db(app) @app.errorhandler(Exception) def handle_auth_error(ex): response = jsonify(message=str(ex)) response.status_code = (ex.code if isinstance(ex, HTTPException) else 500) return response oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url=AUTH0_BASE_URL + '/oauth/token', authorize_url=AUTH0_BASE_URL + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) def requires_auth0(f): @wraps(f) def decorated(*args, **kwargs): if constants.PROFILE_KEY not in session: return redirect('/login') return f(*args, **kwargs) return decorated @app.route('/') def home(): return render_template('home.html') @app.route('/callback') def callback_handling(): # Handles response from token endpoint token = auth0.authorize_access_token() session['token'] = token['access_token'] print(session['token']) resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/dashboard') @app.route('/login') def login(): return auth0.authorize_redirect(redirect_uri=AUTH0_CALLBACK_URL, audience=AUTH0_AUDIENCE) @app.route('/logout') def logout(): session.clear() params = { 'returnTo': url_for('home', _external=True), 'client_id': AUTH0_CLIENT_ID } return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) @app.route('/dashboard') @requires_auth0 def dashboard(): return render_template('dashboard.html', userinfo=session[constants.PROFILE_KEY], userinfo_pretty=json.dumps( session[constants.JWT_PAYLOAD], indent=4), token=session['token']) @app.route('/drinks') @requires_auth('get:drinks') def view_drinks(jwt): try: drinks = Drink.query.all() if drinks is None: abort(404) except Exception as e: print(e) return jsonify({ 'success': True, 'drinks': [drink.format() for drink in drinks] }) @app.route('/desserts') @requires_auth('get:desserts') def view_dessert(jwt): try: desserts = Dessert.query.all() if desserts is None: abort(404) except Exception as e: print(e) return jsonify({ 'success': True, 'desserts': [dessert.format() for dessert in desserts] }) @app.route('/drinks', methods=['POST']) @requires_auth('post:drinks') def create_drink(jwt): body = request.get_json() if body is None: abort(400) drink_title = body.get('title') drink = Drink(title=drink_title) drink.insert() return jsonify({'success': True, 'drinks': [drink.format()]}) @app.route('/desserts', methods=['POST']) @requires_auth('post:desserts') def create_dessert(jwt): body = request.get_json() if body is None: abort(400) dessert_title = body.get('title') dessert = Dessert(title=dessert_title) dessert.insert() return jsonify({'success': True, 'desserts': [dessert.format()]}) @app.route('/drinks/<id>', methods=['PATCH']) @requires_auth('patch:drinks') def update_drink(jwt, id): drink = Drink.query.get(id) if drink: try: body = request.get_json() new_title = body.get('title') if new_title: drink.title = new_title drink.update() return jsonify({'success': True, 'drinks': [drink.format()]}) except Exception as e: print(e) abort(422) else: abort(404) @app.route('/desserts/<id>', methods=['PATCH']) @requires_auth('patch:desserts') def update_dessert(jwt, id): dessert = Dessert.query.get(id) if dessert: try: body = request.get_json() new_title = body.get('title') if new_title: dessert.title = new_title dessert.update() return jsonify({ 'success': True, 'desserts': [dessert.format()] }) except Exception as e: print(e) abort(422) else: abort(404) @app.route('/drinks/<id>', methods=['DELETE']) @requires_auth('delete:drinks') def delete_drink(jwt, id): drink = Drink.query.get(id) if drink: try: drink.delete() return jsonify({'success': True, 'delete': id}) except: abort(422) else: abort(404) @app.route('/desserts/<id>', methods=['DELETE']) @requires_auth('delete:desserts') def delete_dessert(jwt, id): dessert = Dessert.query.get(id) if dessert: try: dessert.delete() return jsonify({'success': True, 'delete': id}) except: abort(422) else: abort(404) @app.errorhandler(AuthError) def auth_error(error): return jsonify({ 'success': False, 'error': error.status_code, 'message': error.error }), error.status_code @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 @app.errorhandler(401) def unauthorised(error): return jsonify({ "success": False, "error": 401, "message": "unauthorised" }), 401 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad_request" }), 400 return app
class OAuth2Authentication(BaseAuthentication): """OAuth Authentication Class""" LOGOUT_VIEW = OAUTH2_LOGOUT oauth_obj = OAuth(Flask(__name__)) oauth2_clients = {} oauth2_config = {} def __init__(self): for oauth2_config in config.OAUTH2_CONFIG: OAuth2Authentication.oauth2_config[ oauth2_config['OAUTH2_NAME']] = oauth2_config OAuth2Authentication.oauth2_clients[oauth2_config[ 'OAUTH2_NAME']] = OAuth2Authentication.oauth_obj.register( name=oauth2_config['OAUTH2_NAME'], client_id=oauth2_config['OAUTH2_CLIENT_ID'], client_secret=oauth2_config['OAUTH2_CLIENT_SECRET'], access_token_url=oauth2_config['OAUTH2_TOKEN_URL'], authorize_url=oauth2_config['OAUTH2_AUTHORIZATION_URL'], api_base_url=oauth2_config['OAUTH2_API_BASE_URL'], client_kwargs={ 'scope': oauth2_config.get('OAUTH2_SCOPE', 'email profile') }, ) def get_source_name(self): return OAUTH2 def get_friendly_name(self): return self.oauth2_config[self.oauth2_current_client]['OAUTH2_NAME'] def validate(self, form): return True def login(self, form): profile = self.get_user_profile() if 'email' not in profile or not profile['email']: current_app.logger.exception( "An email id is required to login into pgAdmin. " "Please update your Oauth2 profile.") return False, gettext( "An email id is required to login into pgAdmin. " "Please update your Oauth2 profile.") user, msg = self.__auto_create_user(profile) if user: user = db.session.query(User).filter_by( username=profile['email'], auth_source=OAUTH2).first() current_app.login_manager.logout_view = \ OAuth2Authentication.LOGOUT_VIEW return login_user(user), None return False, msg def get_user_profile(self): session['oauth2_token'] = self.oauth2_clients[ self.oauth2_current_client].authorize_access_token() session['pass_enc_key'] = session['oauth2_token']['access_token'] resp = self.oauth2_clients[self.oauth2_current_client].get( self.oauth2_config[ self.oauth2_current_client]['OAUTH2_USERINFO_ENDPOINT'], token=session['oauth2_token']) resp.raise_for_status() return resp.json() def authenticate(self, form): self.oauth2_current_client = request.form['oauth2_button'] redirect_url = url_for(OAUTH2_AUTHORIZE, _external=True) if self.oauth2_current_client not in self.oauth2_clients: return False, gettext( "Please set the configuration parameters properly.") return False, self.oauth2_clients[ self.oauth2_current_client].authorize_redirect(redirect_url) def __auto_create_user(self, resp): if config.OAUTH2_AUTO_CREATE_USER: user = User.query.filter_by(username=resp['email'], auth_source=OAUTH2).first() if not user: return create_user({ 'username': resp['email'], 'email': resp['email'], 'role': 2, 'active': True, 'auth_source': OAUTH2 }) return True, {'username': resp['email']}
"""Code for extensions used by flask""" from flask_bootstrap import Bootstrap from flask_debugtoolbar import DebugToolbarExtension from flask_ldap3_login import LDAP3LoginManager from flask_login import LoginManager from authlib.integrations.flask_client import OAuth from flask_mail import Mail from scout.adapter import MongoAdapter from .loqus_extension import LoqusDB from .mongo_extension import MongoDB toolbar = DebugToolbarExtension() bootstrap = Bootstrap() store = MongoAdapter() login_manager = LoginManager() ldap_manager = LDAP3LoginManager() oauth_client = OAuth() mail = Mail() loqusdb = LoqusDB() mongo = MongoDB()
from data.models import User bp = Blueprint("auth", __name__, url_prefix="/auth") db = DEFAULT_DATABASE.db def fetch_google_token(): return session.get('google_token') def update_google_token(token): session['google_token'] = token return session['google_token'] oauth = OAuth() # pylint: disable=invalid-name oauth.register( name='google', # nosec api_base_url='https://www.googleapis.com/', access_token_url='https://accounts.google.com/o/oauth2/token', authorize_url='https://accounts.google.com/o/oauth2/auth', fetch_token=fetch_google_token, update_token=update_google_token, client_kwargs={'scope': 'email profile'}) @bp.route("/login", methods=["GET"]) def login(): if is_authenticated(): return redirect("/")
AUTH0_BASE_URL = 'https://' + AUTH0_DOMAIN AUTH0_AUDIENCE = env.get(constants.AUTH0_AUDIENCE) blueprint.config = {} blueprint.config['SECRET_KEY'] = constants.SECRET_KEY blueprint.config['DEBUG'] = True @blueprint.errorhandler(Exception) def handle_auth_error(ex): response = jsonify(message=str(ex)) response.status_code = (ex.code if isinstance(ex, HTTPException) else 500) return response oauth = OAuth(blueprint) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url=AUTH0_BASE_URL + '/oauth/token', authorize_url=AUTH0_BASE_URL + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) config = { 'host': 'AWS_RDS_ENDPOINT',
if refresh_token: item = OAuth2Token.filter_by(name=name, refresh_token=refresh_token).first() elif access_token: item = OAuth2Token.filter_by(name=name, access_token=access_token).first() else: return if not item: return # update old token item.access_token = token['access_token'] item.refresh_token = token.get('refresh_token') item.expires_at = token['expires_at'] db.session.commit() oauth = OAuth(fetch_token=fetch_token, update_token=update_token) github = oauth.register( name='github', access_token_url='https://github.com/login/oauth/access_token', access_token_params=None, authorize_url='https://github.com/login/oauth/authorize', authorize_params=None, api_base_url='https://api.github.com/', client_kwargs={'scope': 'user:email'}, ) google = oauth.register( name='google', access_token_url='https://www.googleapis.com/oauth2/v4/token', access_token_params={'grant_type': 'authorization_code'}, authorize_url='https://accounts.google.com/o/oauth2/v2/auth?access_type=offline', authorize_params=None,
def create_app(test_config=None): # create and configure the app app = Flask(__name__) app.secret_key = constants.SECRET_KEY setup_db(app) CORS(app) ''' Use the after_request decorator to set Access-Control-Allow ''' @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PATCH,POST,DELETE,OPTIONS') return response def requires_login(f): ''' Decorator to enforce endpoints which require the user to be logged in regradless of role ''' @wraps(f) def decorated(*args, **kwargs): if constants.PROFILE_KEY not in session: return redirect('/login') return f(*args, **kwargs) return decorated # OAuth setup and endpoints oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url=AUTH0_BASE_URL + '/oauth/token', authorize_url=AUTH0_BASE_URL + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) @app.route('/callback') def callback_handling(): try: auth = auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() print(userinfo) # store jwt in session session['token'] = auth['access_token'] # store username in session session['user'] = userinfo['name'] session[constants.JWT_PAYLOAD] = userinfo session[constants.PROFILE_KEY] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/dashboard') except Exception as e: print('Error on callback:', e) @app.route('/login') def login(): ''' Manage login flow by redirecting to Auth0 ''' try: return auth0.authorize_redirect(redirect_uri=AUTH0_CALLBACK_URL, audience=AUTH0_AUDIENCE) except Exception as e: print('Error on login:'******'/logout') def logout(): session.clear() params = {'returnTo': url_for('home', _external=True), 'client_id': AUTH0_CLIENT_ID} return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) @requires_login @app.route('/dashboard') def dashboard(): ''' Endpoint to redirect to once logged in ''' return render_template( 'dashboard.html', userinfo=session[constants.PROFILE_KEY], userinfo_pretty=json.dumps(session[constants.JWT_PAYLOAD], indent=4) ) @app.route('/') def home(): return render_template('home.html') @app.route('/actors', methods=['GET']) @requires_auth(permission='get:actors') def get_actors(payload): ''' Get the full list of actors, formatted ''' actors = [actor.format() for actor in Actor.query.all()] if len(actors) == 0: abort(404) return jsonify({ "success": True, 'status_code': 200, "actors": actors }), 200 @app.route('/movies', methods=['GET']) @requires_auth(permission='get:movies') def get_movies(payload): ''' Get the full list of movies, formatted ''' movies = [movie.format() for movie in Movie.query.all()] if len(movies) == 0: abort(404) return jsonify({ "success": True, 'status_code': 200, "movies": movies }), 200 @app.route('/actors', methods=['POST']) @requires_auth(permission='post:actors') def create_actor(payload): body = request.get_json() name = body.get('name') age = body.get('age') gender = body.get('gender') actor = Actor(name=name, age=age, gender=gender) actor.insert() return jsonify({ "success": True, 'status_code': 200 }), 200 @app.route('/movies', methods=['POST']) @requires_auth(permission='post:movies') def create_movie(payload): body = request.get_json() title = body.get('title') release_date = body.get('release_date') movie = Movie(title=title, release_date=release_date) movie.insert() return jsonify({ "success": True, 'status_code': 200 }), 200 @app.route('/actors/<int:id>', methods=['PATCH']) @requires_auth(permission='patch:actors') def edit_actor(payload, id): actor = Actor.query.filter(Actor.id == id).one_or_none() # Check actor exists if not actor: abort(404) body = request.get_json() actor.name = body.get('name', actor.name) actor.age = body.get('age', actor.age) actor.gender = body.get('gender', actor.gender) actor.update() return jsonify({ "success": True, 'status_code': 200, "actor": actor.format() }), 200 @app.route('/movies/<int:id>', methods=['PATCH']) @requires_auth(permission='patch:movies') def edit_movie(payload, id): movie = Movie.query.filter(Movie.id == id).one_or_none() # Check actor exists if not movie: abort(404) body = request.get_json() movie.title = body.get('title', movie.title) movie.release_date = body.get('release_date', movie.release_date) movie.update() return jsonify({ "success": True, 'status_code': 200, "movie": movie.format() }), 200 @app.route('/actors/<int:id>', methods=['DELETE']) @requires_auth(permission='delete:actors') def delete_actor(payload, id): actor = Actor.query.filter(Actor.id == id).one_or_none() # Check actor exists if not actor: abort(404) id = actor.id actor.delete() return jsonify({ "success": True, 'status_code': 200, "delete": id }), 200 @app.route('/movies/<int:id>', methods=['DELETE']) @requires_auth(permission='delete:movies') def delete_movie(payload, id): movie = Movie.query.filter(Movie.id == id).one_or_none() # Check movie exists if not movie: abort(404) id = movie.id movie.delete() return jsonify({ "success": True, 'status_code': 200, "delete": id }), 200 @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "Not found" }), 404 @app.errorhandler(405) def not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "Method Not Allowed" }), 405 @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "Unprocessable Entity" }), 422 @app.errorhandler(500) def internal_error(error): return jsonify({ "success": False, "error": 500, "message": "Internal Server Error" }), 500 @app.errorhandler(AuthError) def auth_error(error): return jsonify({ "success": False, "error": "AuthError", "message": "Authorization error" }), AuthError return app
def create_app(test_config=None): app = Flask(__name__, static_folder="templates/stylesheets") setup_db(app) oauth = OAuth(app) app.secret_key = "temp key" @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization, true') response.headers.add('Access-Control-Allow-Methods', 'GET, POST, DELETE, PATCH') response.headers.add('Access-Control-Allow-Credentials', 'true') return response auth0 = oauth.register( 'auth0', client_id='yzDykpaQCAi0qMfaYnbD3pwH6nQiRgSs', client_secret= 'T5ggzfkhmQMFIDj2TeHnpu75Gqn8n2UEcABKIRImMkq0N8czZ0SYFO7jYbIiTPC-', api_base_url='https://hackthistest.us.auth0.com', access_token_url='https://hackthistest.us.auth0.com/oauth/token', authorize_url='https://hackthistest.us.auth0.com/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) @app.route('/login') def login(): return auth0.authorize_redirect( redirect_uri='https://community-cafe.herokuapp.com/login-results') @app.route('/signup') def signup(): return auth0.authorize_redirect( redirect_uri='https://community-cafe.herokuapp.com/signup-results') @app.route('/login_business') def business_login(): return auth0.authorize_redirect( redirect_uri= 'https://community-cafe.herokuapp.com/login-results-business') @app.route('/signup_business') def business_signup(): return auth0.authorize_redirect( redirect_uri= 'https://community-cafe.herokuapp.com/signup-results-business') @app.route('/login-results') def login_handling(): # Handles response from token endpoint auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/dashboard/display') @app.route('/signup-results') def signup_handling(): # Handles response from token endpoint auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/profile/student/create') @app.route('/login-results-business') def business_login_handling(): # Handles response from token endpoint auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['jwt_payload'] = userinfo session['business_profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/profile/business/display') @app.route('/signup-results-business') def business_signup_handling(): # Handles response from token endpoint auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() # Store the user information in flask session. session['business_jwt_payload'] = userinfo session['business_profile'] = { 'user_id': userinfo['sub'], 'name': userinfo['name'], 'picture': userinfo['picture'] } return redirect('/profile/business/create') def requires_auth(f): @wraps(f) def decorated(*args, **kwargs): if 'profile' not in session: # Redirect to Login page here return redirect('/login') return f(*args, **kwargs) return decorated def requires_business_auth(f): @wraps(f) def decorated(*args, **kwargs): if 'business_profile' not in session: # Redirect to Login page here return redirect('/login_business') return f(*args, **kwargs) return decorated @app.route('/logout') def logout(): # Clear session stored data session.clear() # Redirect user to logout endpoint params = { 'returnTo': url_for('index', _external=True), 'client_id': 'yzDykpaQCAi0qMfaYnbD3pwH6nQiRgSs' } return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) #______________________________student endpoints___________________________________ @app.route('/dashboard/display', methods=['GET']) @requires_auth def display_student_dashboard(): student_id = get_student_id_from_auth_id() student = Student.query.get(student_id) student_name = student.name return render_template('student_dashboard.html', student_id=student_id, student_name=student_name) @app.route('/dashboard', methods=['GET']) @requires_auth def get_student_dashboard(): student_id = get_student_id_from_auth_id() if student_id == 0: abort(400) student = Student.query.get(student_id) if not student: abort(404) student_zip_code = student.zip_code recommended_businesses = Business.query.filter_by( zip_code=student_zip_code).all() if not recommended_businesses: abort(404) data = [] for business in recommended_businesses: data.append(business.name) ''' student_zip_code = student.zip_code search = SearchEngine(simple_zipcode=True) zipcode = search.by_zipcode(student_zip_code) zipcode_json = zipcode.to_json() student_lat = zipcode_json["lat"] student_long = zipcode_json["long"] all_businesses = Business.query.filter(Business.address != None).all() businesses_in_zip_code = [] #geolocator = geocode.GoogleV3(api_key='APIKEY') for business in all_businesses: if (business.zip_code == student.zip_code): #location = geolocator.geocode(business.address, timeout=15) #g = geocoder.google(business.address) #businesses_in_zip_code.append({ 'id': business.id, 'name': business.name, 'latitude': g.latlng[0], 'longitude': g.latlng[1] }) ''' return jsonify({ 'success': True, 'id': student_id, 'name': student.name, 'business_names': student.business_names, 'recommended_count': len(recommended_businesses), 'recommended': data #'student_lat': student_lat, #'student_long': student_long, #'businesses_in_zip_code': businesses_in_zip_code }), 200 @app.route('/profile/student/create', methods=['GET', 'POST']) @requires_auth def create_student_profile(): if request.method == 'GET': return render_template('new_student_form.html', userinfo=session['profile']) body = request.get_json() try: name = body.get('name') email = body.get('email') zip_code = body.get('zip_code') interests = body.get('interests') qualifications = body.get('qualifications') auth_id = body.get('auth_id') student = Student(name=name, email=email, zip_code=zip_code, interests=interests, qualifications=qualifications, auth_id=auth_id) if not student: abort(404) student.insert() return jsonify({ 'success': True, 'name': student.name, 'email': student.email, 'zip_code': student.zip_code, 'interests': student.interests, 'qualifications': student.qualifications, 'auth_id': student.auth_id }), 200 except: abort(422) @app.route('/profile/student/display', methods=['GET']) @requires_auth def display_student_profile(): student_id = get_student_id_from_auth_id() return render_template('my_profile.html', student_id=student_id) @app.route('/profile/student', methods=['GET']) @requires_auth def get_student_profile(): student_id = get_student_id_from_auth_id() if student_id == 0: abort(400) student = Student.query.get(student_id) if not student: abort(404) return jsonify({ 'success': True, 'id': student_id, 'name': student.name, 'email': student.email, 'zip_code': student.zip_code, 'interests': student.interests, 'qualifications': student.qualifications, 'business_names': student.business_names }), 200 @app.route('/profile/student/edit', methods=['GET', 'PATCH']) @requires_auth def update_student_profile(): student_id = get_student_id_from_auth_id() if request.method == 'GET': return render_template('update_student_form.html', student_id=student_id) if student_id == 0: abort(400) student = Student.query.get(student_id) if not student: abort(404) body = request.get_json() try: name = body.get('name') email = body.get('email') zip_code = body.get('zip_code') interests = body.get('interests') qualifications = body.get('qualifications') if name: student.name = name if email: student.email = email if zip_code: student.zip_code = zip_code if interests: student_interests = interests if qualifications: student.qualifications = qualifications if not student: abort(404) student.update() return jsonify({ 'success': True, 'name': student.name, 'email': student.email, 'zip_code': student.zip_code, 'interests': student.interests, 'qualifications': student.qualifications }), 200 except: abort(422) @app.route('/profile/student', methods=['DELETE']) @requires_auth def delete_student_profile(): student_id = get_student_id_from_auth_id() if student_id == 0: abort(400) student = Student.query.get(student_id) if not student: abort(404) student.delete() return jsonify({'success': True, 'id': student_id}), 200 @app.route('/student/search', methods=['GET', 'POST']) @requires_auth def search_businesses(): if request.method == 'GET': return render_template("search.html") body = request.get_json() search_term = body.get('search_term') businesses = Business.query.filter( Business.name.ilike('%{}%'.format(search_term))).all() if len(businesses) == 0: abort(404) data = [] for business in businesses: data.append({ "id": business.id, "name": business.name, "skills": business.skills }) return jsonify({ 'success': True, 'count': len(businesses), 'data': data }) @app.route('/profile/business/<int:business_id>/display', methods=['GET']) @requires_auth def display_business_profile_student_view(business_id): return render_template('business_profile_student_view.html', business_id=business_id) @app.route('/profile/business/<int:business_id>', methods=['GET']) @requires_auth def get_business_profile_student_view(business_id): if business_id == 0: abort(400) business = Business.query.get(business_id) if not business: abort(404) return jsonify({ 'success': True, 'id': business.id, 'name': business.name, 'email': business.email, 'zip_code': business.zip_code, 'goals': business.goals, 'website': business.website, 'address': business.address, 'description': business.description, 'skills': business.skills }), 200 @app.route('/student/login', methods=['GET']) def login_buttons(): return render_template('student_login.html') #for testing purposes ''' @app.route('/listallstudents', methods=['GET']) def listemall(): students = Student.query.all() data = [] for student in students: data.append({ "id": student.id }) return jsonify({ 'success': True, 'data': data }) ''' #_____________________________business endpoints__________________________________ @app.route('/profile/business/create', methods=['GET', 'POST']) @requires_business_auth def create_business_profile(): if request.method == 'GET': return render_template('new_business_form.html', userinfo=session['business_profile']) body = request.get_json() try: name = body.get('name') email = body.get('email') zip_code = body.get('zip_code') goals = body.get('goals') website = body.get('website') address = body.get('address') description = body.get('description') skills = body.get('skills'), auth_id = body.get('auth_id') business = Business(name=name, email=email, zip_code=zip_code, goals=goals, website=website, address=address, description=description, skills=skills, auth_id=auth_id) if not business: abort(404) business.insert() return jsonify({ 'success': True, 'name': business.name, 'email': business.email, 'zip_code': business.zip_code, 'goals': business.goals, 'website': business.website, 'address': business.address, 'description': business.description, 'skills': business.skills, 'auth_id': business.auth_id }), 200 except: abort(422) @app.route('/profile/business/display', methods=['GET']) @requires_business_auth def display_business_profile(): business_id = get_business_id_from_auth_id() return render_template('business_profile_business_view.html', business_id=business_id) @app.route('/profile/business', methods=['GET']) @requires_business_auth def get_business_profile(): business_id = get_business_id_from_auth_id() if business_id == 0: abort(400) business = Business.query.get(business_id) if not business: abort(404) return jsonify({ 'success': True, 'id': business.id, 'name': business.name, 'email': business.email, 'zip_code': business.zip_code, 'goals': business.goals, 'website': business.website, 'address': business.address, 'description': business.description, 'skills': business.skills }), 200 @app.route('/profile/business/edit', methods=['GET', 'PATCH']) @requires_business_auth def update_business_profile(): business_id = get_business_id_from_auth_id() if request.method == 'GET': return render_template('update_business_form.html', business_id=business_id) if business_id == 0: abort(400) business = Business.query.get(business_id) if not business: abort(404) body = request.get_json() try: name = body.get('name') email = body.get('email') zip_code = body.get('zip_code') goals = body.get('goals') website = body.get('website') address = body.get('address') description = body.get('description') skills = body.get('skills') if name: business.name = name if email: business.email = email if zip_code: business.zip_code = zip_code if goals: business.goals = goals if website: business.website = website if address: business.address = address if description: business.description = description if skills: business.skills = skills if not business: abort(404) business.update() return jsonify({ 'success': True, 'id': business.id, 'name': business.name, 'email': business.email, 'zip_code': business.zip_code, 'goals': business.goals, 'website': business.website, 'address': business.address, 'description': business.description, 'skills': business.skills }), 200 except: abort(422) @app.route('/profile/business', methods=['DELETE']) @requires_business_auth def delete_business_profile(): business_id = get_business_id_from_auth_id() if business_id == 0: abort(400) business = Business.query.get(business_id) if not business: abort(404) business.delete() return jsonify({'success': True, 'id': business_id}), 200 @app.route('/business/search', methods=['GET', 'POST']) @requires_business_auth def search_students(): if request.method == 'GET': return render_template('search_business.html') body = request.get_json() search_term = body.get('search_term') students = Student.query.filter( Student.name.ilike('%{}%'.format(search_term))).all() if len(students) == 0: abort(404) data = [] for student in students: data.append({ "id": student.id, "name": student.name, "interests": student.interests }) return jsonify({'success': True, 'count': len(students), 'data': data}) @app.route('/profile/student/<int:student_id>/display', methods=['GET']) @requires_business_auth def display_student_profile_business_view(student_id): return render_template('student_profile_business_view.html', student_id=student_id) @app.route('/profile/student/<int:student_id>', methods=['GET']) @requires_business_auth def get_student_profile_business_view(student_id): if student_id == 0: abort(400) student = Student.query.get(student_id) if not student: abort(404) return jsonify({ 'success': True, 'id': student_id, 'name': student.name, 'email': student.email, 'zip_code': student.zip_code, 'interests': student.interests, 'qualifications': student.qualifications }), 200 @app.route('/business/login', methods=['GET']) def business_login_buttons(): return render_template('business_login.html') #___________________________endpoints for everyone!________________________________ @app.route('/') def index(): return render_template('home.html'), 200 #_______________________________error handlers____________________________________ @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(404) def resource_not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found - PLEASE REFRESH PAGE" }), 404 @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 @app.errorhandler(403) def bad_login(error): return jsonify({ "success": False, "error": 403, "message": "bad login" }), 403 return app
from .config import ( auth_base_url, token_base_url, client_id, client_secret, scope, base_url ) log = logging.getLogger(__name__) def get_spotify_oauth_token(name): try: return session['access_token'] except KeyError: abort(400, 'No access token') oauth = OAuth(fetch_token=get_spotify_oauth_token) oauth.register( name='spotify', client_id=client_id, client_secret=client_secret, access_token_url=token_base_url, authorize_url=auth_base_url, authorize_params={'scope': scope, 'show_dialog': True}, api_base_url=base_url, ) def get_user_id(): log.info('Getting user id') user = oauth.spotify.get('me')
def create_app(test_config=None): # create and configure the app app = Flask(__name__) setup_db(app) CORS(app) migrate = Migrate(app, db) app.secret_key = "udacity" # ------------------------------------------------------------------------ # set Access-Control-allow, API Configuration # ------------------------------------------------------------------------ @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization, true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,POST,PUT,PATCH,DELETE,OPTIONS') return response oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id='4bH07NXNIJ02BMCRkZsN85JYRDkB4sVI', client_secret='udacity', api_base_url='https://dev-mrlzc2vg.us.auth0.com', access_token_url='https://dev-mrlzc2vg.us.auth0.com' + '/oauth/token', authorize_url='https://dev-mrlzc2vg.us.auth0.com' + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) # -------------------------------------------------------------------------- # Custom Functions # -------------------------------------------------------------------------- def paginate_movies(request, selection): page = request.args.get('page', 1, type=int) # determine the movies for each page start = (page - 1) * MOVIES_PER_PAGE end = start + MOVIES_PER_PAGE formatted_movies = [movie.format() for movie in selection] current_movies = formatted_movies[start:end] return current_movies def paginate_actors(request, selection): page = request.args.get('page', 1, type=int) # determine the actors for each page start = (page - 1) * ACTORS_PER_PAGE end = start + ACTORS_PER_PAGE formatted_actors = [actor.format() for actor in selection] current_actors = formatted_actors[start:end] return current_actors @app.route('/') def index(): return render_template('index.html') @app.route('/login_result') def login_result(): return render_template('index.html') @app.route('/logout') def logout(): # Clear session stored data session.clear() # Redirect user to logout endpoint params = { 'returnTo': url_for('index', _external=True), 'client_id': '4bH07NXNIJ02BMCRkZsN85JYRDkB4sVI' } return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) # ------------------------------------------------------------------------- # API endpoints : movies GET/POST/DELETE/PATCH # ------------------------------------------------------------------------- # GET MOVIES @app.route('/movies', methods=['GET']) @requires_auth('read:movie') def get_movies(payload): movies = Movie.query.all() current_movies = paginate_movies(request, movies) # if there are no movies abort 404 if len(current_movies) == 0: abort(404) # return data to view return jsonify({ 'success': True, 'movies': current_movies, 'total_movies': len(movies) }), 200 # CREATE / SEARCH MOVIES @app.route('/movies/search', methods=['POST']) @requires_auth('post:movie') def create_search_movie(payload): # Get request json body = request.get_json() if body is None: abort(400) # load data from body new_title = body.get('title', None) new_release_date = body.get('release_date', None) search = body.get('searchTerm', None) if not search: # ensure all fields have data if not new_title: abort(422) if not new_release_date: abort(422) try: if search: selection = Movie.query.order_by(Movie.id) \ .filter(Movie.title.ilike('%{}%'.format(search))) current_movies = paginate_movies(request, selection) # return data to view return jsonify({ 'success': True, 'movies': current_movies, 'total_movies': len(Movie.query.all()) }), 200 else: # create and insert new movie movie = Movie(title=new_title, release_date=new_release_date) movie.insert() movies = Movie.query.all() formatted_movies = [movie.format() for movie in movies] # return data to view return jsonify({ 'success': True, 'created': movie.id, 'movies': formatted_movies, 'total_movies': len(movies) }), 200 except: abort(401) # DELETE MOVIE @app.route('/movies/<int:movie_id>', methods=['DELETE']) @requires_auth('delete:movie') def delete_movie(payload, movie_id): # Abort if no movie_id has been provided if not movie_id: abort(400) # get the movie should be deleted movie = Movie.query.filter(Movie.id == movie_id).one_or_none() # if no movie found abort 404 if movie is None: abort(404) try: # delete the movie movie.delete() movies = Movie.query.all() formatted_movies = [movie.format() for movie in movies] # return success response return jsonify({ 'success': True, 'deleted': movie_id, 'movies': formatted_movies, 'total_movies': len(movies) }), 200 except: # if problem deleting abort(422) # UPDATE MOVIE @app.route('/movies/<int:movie_id>', methods=['PATCH']) @requires_auth('update:movie') def update_movie(payload, movie_id): body = request.get_json() if not movie_id: abort(400) if not body: abort(400) # get the movie movie = Movie.query.filter(Movie.id == movie_id).one_or_none() # if no movie found abort 404 if movie is None: abort(404) try: if 'title' in body: movie.title = body.get('title') if 'release_date' in body: movie.release_date = body.get('release_date') # update the movie movie.update() movies = Movie.query.all() formatted_movies = [movie.format() for movie in movies] # return success response return jsonify({ 'success': True, 'updated': movie_id, 'movies': formatted_movies, 'total_movies': len(movies) }), 200 except: # if problem updating abort(401) # GET MOVIES BY ACTOR @app.route('/actors/<actor_id>/movies', methods=['GET']) @requires_auth('read:movie') def get_movies_by_actor(payload, actor_id): performances = Performance.query.filter( Performance.actor_id == actor_id).all() if len(performances) == 0: abort(404) movies = {} for movie in performances: movie_actor = Movie.query.filter( Movie.id == movie.movie_id).first() movies[movie.id] = movie_actor.format() return jsonify({ 'success': True, 'movies': movies, 'total_movies_by_actor': len(movies) }), 200 # ------------------------------------------------------------------------ # API endpoints : actors GET/POST/DELETE/PATCH # ------------------------------------------------------------------------ # GET ACTORS @app.route('/actors', methods=['GET']) @requires_auth('read:movie') def get_actors(payload): actors = Actor.query.all() current_actors = paginate_actors(request, actors) # if there are no actors abort 404 if len(current_actors) == 0: abort(404) # return data to view return jsonify({ 'success': True, 'actors': current_actors, 'total_actors': len(actors) }), 200 # CREATE / SEARCH BY NAME @app.route('/actors/search', methods=['POST']) @requires_auth('post:actor') def create_search_actor(payload): # Get request json body = request.get_json() if body is None: abort(400) # load data from body new_name = body.get('name', None) new_age = body.get('age', None) new_gender = body.get('gender', None) search = body.get('searchTerm', None) try: if search: selection = Actor.query.order_by(Actor.id) \ .filter(Actor.name.ilike('%{}%'.format(search))) current_actors = paginate_actors(request, selection) # return data to view return jsonify({ 'success': True, 'actors': current_actors, 'total_actors': len(Actor.query.all()) }), 200 else: # ensure all fields have data if not new_name or not new_age or not new_gender: abort(422) # create and insert new movie actor = Actor(name=new_name, age=new_age, gender=new_gender) actor.insert() actors = Actor.query.all() formatted_actors = [actor.format() for actor in actors] # return data to view return jsonify({ 'success': True, 'created': actor.id, 'actors': formatted_actors, 'total_actors': len(actors) }), 200 except: abort(422) # DELETE ACTOR @app.route('/actors/<int:actor_id>', methods=['DELETE']) @requires_auth('delete:actor') def delete_actor(payload, actor_id): # get the actor actor = Actor.query.filter(Actor.id == actor_id).one_or_none() # if no actor found abort 404 if actor is None: abort(404) try: # delete the actor actor.delete() actors = Actor.query.all() formatted_actors = [actor.format() for actor in actors] # return success response return jsonify({ 'success': True, 'deleted': actor_id, 'actors': formatted_actors, 'total_actors': len(actors) }), 200 except: # if problem deleting abort(422) # UPDATE ACTOR @app.route('/actors/<int:actor_id>', methods=['PATCH']) @requires_auth('update:actor') def update_actor(payload, actor_id): body = request.get_json() if not actor_id: abort(400) if not body: abort(400) # get the actor actor = Actor.query.filter(Actor.id == actor_id).one_or_none() # if no actor found abort 404 if actor is None: abort(404) try: if 'name' in body: actor.name = body.get('name') if 'age' in body: actor.age = int(body.get('age')) if 'gender' in body: actor.gender = body.get('gender') # update the actor actor.update() actors = Actor.query.all() formatted_actors = [actor.format() for actor in actors] # return success response return jsonify({ 'success': True, 'updated': actor_id, 'actors': formatted_actors, 'total_actors': len(actors) }), 200 except: # if problem updating abort(401) # GET ACTORS BY MOVIE @app.route('/movies/<movie_id>/actors', methods=['GET']) @requires_auth('read:actor') def get_actors_by_movie(payload, movie_id): performances = Performance.query.filter( Performance.movie_id == movie_id).all() if len(performances) == 0: abort(404) actors = {} for actor in performances: actor_movie = Actor.query.filter( Actor.id == actor.actor_id).first() actors[actor.id] = actor_movie.format() return jsonify({ 'success': True, 'actors': actors, 'total_actors_by_movie': len(actors) }), 200 # ------------------------------------------------------------------------------------ # Error handler # ------------------------------------------------------------------------------------ @app.errorhandler(404) def not_found(error): return jsonify({ 'success': False, 'error': 404, 'message': 'Resource not found' }), 404 @app.errorhandler(422) def unprocessable(error): return jsonify({ 'success': False, 'error': 422, 'message': 'unprocessable' }), 422 @app.errorhandler(400) def bad_request(error): return jsonify({ 'success': False, 'error': 400, 'message': 'bad request' }), 400 @app.errorhandler(401) def unauthorized_error(error): return jsonify({ 'success': False, 'error': 401, 'message': 'unauthorized' }), 401 @app.errorhandler(403) def unauthorized_error(error): return jsonify({ 'success': False, 'error': 403, 'message': 'Permission not found' }), 403 return app
from six.moves.urllib.parse import urlencode import os import sys import json def create_app(test_config=None): app = Flask(__name__) app.secret_key = os.getenv("FLASK_SECRET_KEY") setup_db(app) CORS(app) return app APP = create_app() oauth = OAuth(APP) auth0_url = 'https://' + os.getenv('AUTH0_DOMAIN') auth0 = oauth.register( 'auth0', client_id=os.getenv("CLIENT_ID"), client_secret=os.getenv('AUTH0_SECRET'), api_base_url=auth0_url, access_token_url=auth0_url + '/oauth/token', authorize_url=auth0_url + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, )
AD_OBSERVATORY_COGNITO_ACCESS_TOKEN_URL = (COGNITO_ACCESS_TOKEN_URL_TEMPLATE % AD_OBSERVATORY_COGNITO_HOST) AD_OBSERVATORY_COGNITO_LOGIN_URL = COGNITO_LOGIN_URL_TEMPLATE % AD_OBSERVATORY_COGNITO_HOST AD_OBSERVATORY_COGNITO_USERINFO_URL = COGNITO_USERINFO_URL_TEMPLATE % AD_OBSERVATORY_COGNITO_HOST # TODO(macpd): centralize setting and use. AD_SCREENER_OAUTH_REDIRECT_URL = os.environ['AD_SCREENER_OAUTH_REDIRECT_URL'] AD_OBSERVATORY_OAUTH_REDIRECT_URL = os.environ[ 'AD_OBSERVATORY_OAUTH_REDIRECT_URL'] AD_OBSERVATORY_AUTHORIZATION_SUCCESS_REDIRECT_URL = ( os.environ['AD_OBSERVATORY_AUTHORIZATION_SUCCESS_REDIRECT_URL']) AD_SCREENER_OAUTH_CLIENT_NAME = 'ad_screener_cognito' AD_OBSERVATORY_OAUTH_CLIENT_NAME = 'ad_observatory_cognito' oauth = OAuth() oauth.register(name=AD_SCREENER_OAUTH_CLIENT_NAME, client_id=os.environ['AD_SCREENER_OAUTH_CLIENT_ID'], client_secret=os.environ['AD_SCREENER_OAUTH_CLIENT_SECRET'], access_token_url=AD_SCREENER_COGNITO_ACCESS_TOKEN_URL, access_token_params={ 'grant_type': 'authorization_code', 'client_id': os.environ['AD_SCREENER_OAUTH_CLIENT_ID'], 'redirect_uri': AD_SCREENER_OAUTH_REDIRECT_URL, 'scope': 'aws.cognito.signin.user.admin email openid profile' }, authorize_url=AD_SCREENER_COGNITO_LOGIN_URL, authorize_params={ 'redirect_uri': AD_SCREENER_OAUTH_REDIRECT_URL,
DEBUG = True # ######################## APP PREP ##################################### # # create and configure the app application = Flask(__name__) if ENV == 'DEV': # load the dev config application.config.from_object(DevelopmentConfig()) else: # load the prod config application.config.from_object(ProductionConfig()) oauth = OAuth(application) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url=AUTH0_BASE_URL + '/oauth/token', authorize_url=AUTH0_BASE_URL + '/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) @application.errorhandler(Exception)
def create_app(test_config=None): # set up environment variables using dotenv ENV_FILE = find_dotenv() if ENV_FILE: load_dotenv(ENV_FILE) # set up Auth0 AUTH0_CALLBACK_URL = env.get(constants.AUTH0_CALLBACK_URL) AUTH0_CLIENT_ID = env.get(constants.AUTH0_CLIENT_ID) AUTH0_CLIENT_SECRET = env.get(constants.AUTH0_CLIENT_SECRET) AUTH0_DOMAIN = env.get(constants.AUTH0_DOMAIN) AUTH0_BASE_URL = 'https://' + AUTH0_DOMAIN AUTH0_AUDIENCE = env.get(constants.AUTH0_AUDIENCE) # set up Flask app app = Flask(__name__) setup_db(app) app.secret_key = constants.SECRET_KEY # set up OAuth oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url=AUTH0_BASE_URL + '/oauth/token', authorize_url=AUTH0_BASE_URL + '/authorize', client_kwargs={ 'scope': 'openid profile email preferred_username', }, ) # set up CORS, allowing all origins CORS(app, resources={'/': {'origins': '*'}}) @app.after_request def after_request(response): ''' Sets access control. ''' response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PUT,PATCH,POST,DELETE,OPTIONS') return response def login_required(f): @wraps(f) def wrap(*args, **kwargs): if 'logged_in' in session: return f(*args, **kwargs) else: flash('You need to sign in first') return render_template('pages/login.html'), 200 return wrap # add login link function to jinja context app.jinja_env.globals.update(create_login_link=create_login_link) # UTILITY FUNCTIONS # plant formatting def format_plants(plants): return [plant.format() for plant in plants] # observation formatting def format_observations(observations): return [observation.format() for observation in observations] # get Auth0 management API token def get_mgmt_token(): data = { 'grant_type': 'client_credentials', 'client_id': AUTH0_CLIENT_ID, 'client_secret': AUTH0_CLIENT_SECRET, 'audience': 'https://plant-survey.auth0.com/api/v2/' } resp = requests.post( 'https://plant-survey.auth0.com/oauth/token', data=data, headers={'content-type': 'application/x-www-form-urlencoded'}) info = resp.json() mgmt_token = info['access_token'] return mgmt_token # check if user is in database def check_if_user_exists(user_id): # look in Users table for user user = User.query.filter_by(user_id=user_id).one_or_none() # if user not found return false if user is None: return False else: return True # add 'Public' role to user def add_public_role(user_id, mgmt_token): # Auth0 public role ID PUBLIC_ROLE_ID = 'rol_X9T29OUlO7kYdItp' # request body data = {"roles": [PUBLIC_ROLE_ID]} # call management API with user_id and role role_resp = requests.post( f'https://plant-survey.auth0.com/api/v2/users/{user_id}/roles', headers={'Authorization': f"Bearer {mgmt_token}"}, json=data) # raises exception for any 4xx or 5xx errors role_resp.raise_for_status() # add user if not in database def create_new_user(user): # create new user new_user = User(name=user['name'], username=user['username'], user_id=user['user_id'], date_added=user['date_added'], role=user['role']) try: # insert new user in Users table new_user.insert() except Exception as e: print('ERROR: ', str(e)) abort(422) return new_user.id # AUTH ROUTES @app.route('/callback') def callback_handling(): token = auth0.authorize_access_token() # print('TOKEN: ', token['access_token']) # get user info and store user id resp = auth0.get('userinfo') userinfo = resp.json() user_id = userinfo['sub'] # print('ID: ', user_id) # store user info user = {'user_id': user_id} # check if user exists in Users table if check_if_user_exists(user_id): # get user info from Users table user_table_id = User.query.filter_by( user_id=user_id).one_or_none().id name = User.query.filter_by(user_id=user_id).one_or_none().name username = User.query.filter_by( user_id=user_id).one_or_none().username date_added = User.query.filter_by( user_id=user_id).one_or_none().date_added role = User.query.filter_by(user_id=user_id).one_or_none().role # add to user info user['user_table_id'] = user_table_id user['name'] = name user['username'] = username user['date_added'] = date_added user['role'] = role # if no, create new user else: # get management API token mgmt_token = get_mgmt_token() # print('MGMT TOKEN: ', mgmt_token) # set new user role to 'Public' on Auth0 add_public_role(user_id, mgmt_token) user['role'] = 'Public' # get additional user info from management api user_resp = requests.get( f'https://plant-survey.auth0.com/api/v2/users/{user_id}', headers={'Authorization': f"Bearer {mgmt_token}"}) id_info = user_resp.json() # get username from response if 'username' in id_info: username = id_info['username'] elif 'name' in id_info: username = id_info['name'] else: username = id_info['email'] user['username'] = username user['name'] = id_info['name'] # set date added to now user['date_added'] = datetime.utcnow() # add new user to Users table, save user_table_id user_table_id = create_new_user(user) user['user_table_id'] = user_table_id # add session variables session['logged_in'] = True session[constants.JWT_PAYLOAD] = userinfo session[constants.JWT] = token['access_token'] session[constants.PROFILE_KEY] = user return redirect('/dashboard') @app.route('/login') def login(): return auth0.authorize_redirect(redirect_uri=AUTH0_CALLBACK_URL, audience=AUTH0_AUDIENCE) @app.route('/logout') def logout(): session.clear() params = { 'returnTo': url_for('home', _external=True), 'client_id': AUTH0_CLIENT_ID } return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) @app.route('/favicon.ico') def favicon(): return redirect('https://img.icons8.com/color/48/000000/flower.png') # ------------------------------------ # ROUTES # ------------------------------------ @app.route('/dashboard') def dashboard(): # if no active jwt, render login page if 'JWT' not in session: return render_template('pages/login.html'), 200 # get user from Users table user_table_id = User.query.filter_by( user_id=session['profile']['user_id']).one_or_none().id # get all plants and observations that match user plants = Plant.query.filter_by(user_id=user_table_id).all() observations = Observation.query.filter_by(user_id=user_table_id).all() # format all plants and observations if (len(plants) != 0): plants = format_plants(plants) if (len(observations) != 0): observations = format_observations(observations) return render_template('/pages/dashboard.html', userinfo=session[constants.PROFILE_KEY], plants=plants, observations=observations) # home page route handler @app.route('/') def home(): return redirect('plants') @app.route('/about') def about(): return render_template('/pages/about.html'), 200 @app.route('/plants') def plants(): ''' Handles GET requests for getting all plants. ''' # get all plants from API response = get_plants_api() data = json.loads(response.data) plants = data['plants'] # return template with plants return render_template('pages/plants.html', plants=plants), 200 @app.route('/plants/<int:id>') def get_plant_by_id(id): # get plant by id from API response = get_plant_by_id_api(id) data = json.loads(response.data) plant = data['plant'] # serve plant page with plant result return render_template('pages/plant.html', plant=plant), 200 @app.route('/plants/new') @login_required @requires_auth('post:plants') def new_plant_form(jwt): ''' Handles GET requests for new plant form page. ''' # return new plant form return render_template('forms/new_plant.html'), 200 @app.route('/plants/<int:id>/edit') @login_required @requires_auth('edit_or_delete:plants') def get_edit_plant_form(*args, **kwargs): ''' Handles GET requests for edit plant form. ''' # get id from kwargs id = kwargs['id'] # get plant by id plant = Plant.query.filter_by(id=id).one_or_none() # return edit plant template with plant info return render_template('forms/edit_plant.html', plant=plant.format()), 200 @app.route('/observations') def get_plant_observations(): ''' Handles GET requests for getting all observations. ''' # get all plant observations from API response = get_observations_api() data = json.loads(response.data) observations = data['observations'] # return template with observations return render_template('pages/observations.html', observations=observations), 200 @app.route('/observations/new') @login_required @requires_auth('post:observations') def new_observation_form(jwt): ''' Handles GET requests for new plant form page. ''' # redirect to observations if plant id not included if 'plant' not in request.args: return redirect('/observations') # get args from request plant_id = request.args.get('plant') # get plant by id plant = Plant.query.filter_by(id=plant_id).one_or_none() # abort 404 if not found if plant is None: abort(404) # return new plant form return render_template('forms/new_observation.html', plant=plant.format()), 200 @app.route('/observations/<int:id>/edit') @login_required @requires_auth('edit_or_delete:observations') def get_edit_observation_form(*args, **kwargs): ''' Handles GET requests for edit observation form. ''' # get id from kwargs id = kwargs['id'] # get plant by id observation = Observation.query.filter_by(id=id).one_or_none() # return edit plant template with plant info return render_template('forms/edit_observation.html', observation=observation.format()), 200 # API ROUTES @app.route('/api/plants') def get_plants_api(): ''' Handles API GET requests for getting all plants. Returns JSON. ''' # get all plants from database plants = Plant.query.all() # 404 if no plants found if len(plants) == 0: abort(404) # format each plant plants = format_plants(plants) # return plants return jsonify({'success': True, 'plants': plants}) @app.route('/api/plants/<int:id>') def get_plant_by_id_api(id): ''' Handles API GET requests for getting plant by ID. Returns JSON. ''' # get plant by ID plant = Plant.query.filter_by(id=id).one_or_none() # 404 if no plants found if plant is None: abort(404) # return formatted plant return jsonify({'success': True, 'plant': plant.format()}) @app.route('/api/plants/new', methods=['POST']) @requires_auth('post:plants') def new_plant_api(jwt): # get request body body = request.get_json() # get user table id from session or jwt if 'profile' in session: user_id = session['profile']['user_table_id'] else: auth0_user_id = jwt['sub'] user_id = User.query.filter_by( user_id=auth0_user_id).one_or_none().id # load plant form data name = body.get('name') latin_name = body.get('latinName') description = body.get('description') image_link = body.get('imageLink') # ensure all fields have data if ((name == "") or (latin_name == "") or (description == "") or (image_link == "")): abort(422) # create a new plant plant = Plant(user_id=user_id, name=name, latin_name=latin_name, description=description, image_link=image_link) try: # add plant to the database plant.insert() except Exception as e: print('ERROR: ', str(e)) abort(422) # flash success message flash(f'Plant {name} successfully created!') return jsonify({'success': True, 'plant': plant.format()}) @app.route('/api/plants/<int:id>/edit', methods=['PATCH', 'DELETE']) @requires_auth('edit_or_delete:plants') def edit_or_delete_plant_api(*args, **kwargs): ''' Handles API PATCH and DELETE requests for plants. ''' # get id from kwargs id = kwargs['id'] # get plant by id plant = Plant.query.filter_by(id=id).one_or_none() # abort 404 if no plant found if plant is None: abort(404) # if PATCH if request.method == 'PATCH': # get request body body = request.get_json() # get all keys from request body body_keys = [] for key in body: body_keys.append(key) # make sure correct keys are present if sorted(body_keys) != sorted( ['name', 'latinName', 'description', 'imageLink']): abort(422) # update plant with data from body if body.get('name'): plant.name = body.get('name') if body.get('latinName'): plant.latin_name = body.get('latinName') if body.get('description'): plant.description = body.get('description') if body.get('imageLink'): plant.image_link = body.get('imageLink') try: # update plant in database plant.insert() except Exception as e: print('ERROR: ', str(e)) abort(422) # flash success message flash(f'Plant {plant.name} successfully updated.') # return plant if success return jsonify({"success": True, "plant": plant.format()}) # if DELETE if request.method == 'DELETE': # save plant name plant_name = plant.name # first delete all observations related to plant observations = Observation.query.filter_by(plant_id=id).all() for observation in observations: observation.delete() try: # delete plant from the database plant.delete() except Exception as e: print('ERROR: ', str(e)) abort(422) # flash success message flash(f'Plant {plant_name} successfully deleted.') # return if successfully deleted return jsonify({ "success": True, "plant_name": plant_name, "plant_id": id }) @app.route('/api/observations') def get_observations_api(): ''' Handles API GET requests for getting all observations. Returns JSON. ''' # get all observations from database observations = Observation.query.all() # 404 if no observations found if len(observations) == 0: abort(404) # format each observation observations = format_observations(observations) # return observations return jsonify({'success': True, 'observations': observations}) @app.route('/api/observations/<int:id>') def get_observation_by_id_api(id): ''' Handles API GET requests for getting observation by id. Returns JSON. ''' # get observation from database by id observation = Observation.query.filter_by(id=id).one_or_none() # 404 if no observation found if observation is None: abort(404) # return formatted observation return jsonify({'success': True, 'observation': observation.format()}) @app.route('/api/observations/new', methods=['POST']) @requires_auth('post:observations') def post_plant_observation_api(jwt): ''' Handles API POST requests for adding new observation. ''' # get request body body = request.get_json() # get user table id from session or jwt if 'profile' in session: user_id = session['profile']['user_table_id'] else: auth0_user_id = jwt['sub'] user_id = User.query.filter_by( user_id=auth0_user_id).one_or_none().id # load observation body data plant_id = body.get('plantID') date = body.get('date') notes = body.get('notes') # ensure required fields have data if ((date == '') or (plant_id == '')): abort(422) # create a new observation observation = Observation(user_id=user_id, date=date, plant_id=plant_id, notes=notes) try: # add observation to the database observation.insert() except Exception as e: print('ERROR: ', str(e)) abort(422) # flash success message flash('Observation successfully created!') # return observation return jsonify({'success': True, 'observation': observation.format()}) @app.route('/api/observations/<int:id>/edit', methods=['PATCH', 'DELETE']) @requires_auth('edit_or_delete:observations') def edit_or_delete_observation_api(*args, **kwargs): ''' Handles PATCH and DELETE requests for observations. ''' # get id from kwargs id = kwargs['id'] # get jwt from args jwt = args[0] # get observation by id observation = Observation.query.filter_by(id=id).one_or_none() # abort 404 if no observation found if observation is None: abort(404) # get user table id from session or jwt if 'profile' in session: user_id = session['profile']['user_table_id'] else: auth0_user_id = jwt['sub'] user_id = User.query.filter_by( user_id=auth0_user_id).one_or_none().id # abort if request user_id doesn't match observation user_id # users can only edit/delete their own observations if observation.user_id != user_id: abort(401) # if PATCH if request.method == 'PATCH': # get request body body = request.get_json() # get all keys from request body body_keys = [] for key in body: body_keys.append(key) # make sure correct keys are present if sorted(body_keys) != sorted(['date', 'notes']): abort(422) # update observation with data from body if body.get('date'): observation.date = body.get('date') if body.get('notes'): observation.notes = body.get('notes') try: # update observation in database observation.insert() except Exception as e: print('ERROR: ', str(e)) abort(422) # flash success message flash('Observation successfully updated!') # return observation if success return jsonify({ "success": True, "observation": observation.format() }) # if DELETE if request.method == 'DELETE': # save observation id observation_id = observation.id try: # delete observation from the database observation.delete() except Exception as e: print('ERROR: ', str(e)) abort(422) # flash success message flash('Observation successfully deleted!') # return success return jsonify({"success": True, "observation_id": observation_id}) @app.route('/api/key') def get_api_key(): ''' Endpoint for getting API key for curl requests and testing ''' return render_template('/pages/api_key.html') # Error Handling ''' Error handling for unprocessable entity ''' @app.errorhandler(422) def unprocessable(error): return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 ''' Error handling for resource not found ''' @app.errorhandler(404) def resource_not_found(error): return jsonify({ "success": False, "error": 404, "message": "resource not found" }), 404 ''' Error handling for method not allowed ''' @app.errorhandler(405) def method_not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "method not allowed" }), 405 ''' Error handling for bad request ''' @app.errorhandler(400) def bad_request(error): return jsonify({ "success": False, "error": 400, "message": "bad request" }), 400 ''' Error handling for AuthError ''' @app.errorhandler(AuthError) def handle_auth_error(ex): message = ex.error['description'] response = jsonify(ex.error) response.status_code = ex.status_code print('AUTH ERROR: ', response.get_data(as_text=True)) flash(f'{message} Please login.') return redirect('/') return app
from authlib.integrations.flask_client import OAuth from statsapp.models.oauth import fetch_token, OAuth2Token oauth = OAuth(fetch_token=fetch_token, update_token=OAuth2Token.update_token) from .withings import WithingsAPI
def create_app(test_config=None): app = Flask(__name__) app.config.from_object('config') db.init_app(app) migrate = Migrate(app, db) # Configuring Auth0 Credentials oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id=os.environ.get('client_id'), client_secret=os.environ.get('client_secret'), api_base_url=os.environ.get('api_base_url'), access_token_url=os.environ.get('access_token_url'), authorize_url=os.environ.get('authorize_url'), client_kwargs={ 'scope': 'openid profile email', }, ) @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PATCH,POST,DELETE,OPTIONS') return response # Endpoints @app.route('/') def index(): """ Returns: The main page """ return render_template('index.html') @app.route('/movies', methods=['GET']) @requires_auth('get:movies') def get_movies(jwt): """ Returns: The list of all movies """ try: movies = Movie.query.all() return jsonify({ 'success': True, "movies": [movie.id for movie in movies] }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/actors', methods=['GET']) @requires_auth('get:actors') def get_actors(jwt): """ Returns: The list of all actors """ try: actors = Actor.query.all() return jsonify({ 'success': True, "actors": [actor.id for actor in actors] }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/movies', methods=['POST']) @requires_auth('post:movies') def post_movie(jwt): """ Returns: The object of newly created movie """ try: title = request.json['title'] release_date = request.json['release_date'] nMovie = Movie(title=title, release_date=release_date) nMovie.insert() return jsonify({ 'success': True, 'movie': nMovie }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/actors', methods=['POST']) @requires_auth('post:actors') def post_actor(jwt): """ Returns: The object of newly created actor """ try: name = request.json['name'] age = request.json['age'] gender = request.json['gender'] nActor = Actor(name=name, age=age, gender=gender) nActor.insert() return jsonify({ 'success': True, 'actor': nActor }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/movies/<int:id>', methods=['PATCH']) @requires_auth('patch:movies') def patch_movie(jwt, id): """ Args The id of the movie to be edited Returns: The edited movie object """ try: body = request.get_json() ntitle = body.get('title', None) nrelease = body.get('release_date', None) movie = Movie.query.filter(Movie.id == id).one_or_none() if not ntitle == 'null': movie.title = ntitle if not nrelease == 'null': movie.release_date = nrelease movie.update() return jsonify({ 'success': True, 'movie': movie }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/actors/<int:id>', methods=['PATCH']) @requires_auth('patch:actors') def patch_actor(jwt, id): """ Args The id of the actor to be edited Returns: The edited actor object """ try: body = request.get_json() nname = body.get('name', None) nage = body.get('age', None) ngender = body.get('gender', None) actor = Actor.query.filter(Actor.id == id).one_or_none() if not nname == 'null': actor.name = nname if not nage == 'null': actor.age = nage if not ngender == 'null': actor.gender = ngender actor.update() return jsonify({ 'success': True, 'actor': actor }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/movies/<int:id>', methods=['DELETE']) @requires_auth('delete:movies') def delete_movie(jwt, id): """ Args The id of the movie to be deleted Returns: The id of the deleted movie """ try: movie = Movie.query.filter(Movie.id == id).one_or_none() movie.delete() return jsonify({ 'success': True, "delete": id }) except Exception as e: return jsonify({ 'success': False, }) @app.route('/actors/<int:id>', methods=['DELETE']) @requires_auth('delete:actors') def delete_actor(jwt, id): """ Args The id of the movie to be deleted Returns: The id of the deleted movie """ try: actor = Actor.query.filter(Actor.id == id).one_or_none() actor.delete() return jsonify({ 'success': True, "delete": id }) except Exception as e: return jsonify({ 'success': False, }) # Error Handling @app.errorhandler(422) def unprocessable(error): """ returns: handling for 422 (unprocessable) Error """ return jsonify({ "success": False, "error": 422, "message": "unprocessable" }), 422 @app.errorhandler(404) def not_found(error): """ returns: handling for 404 (Not found) Error """ return jsonify({ "success": False, "error": 404, "message": "Not found" }), 404 @app.errorhandler(400) def Bad_request(error): """ returns: handling for 400 (Bad request) Error """ return jsonify({ "success": False, "error": 400, "message": "Bad request" }), 400 @app.errorhandler(405) def method_not_allowed(error): """ returns: handling for 405 (method not allowed) Error """ return jsonify({ "success": False, "error": 405, "message": "method not allowed" }), 405 @app.errorhandler(500) def Server_error(error): """ returns: handling for 500 (Server error) Error """ return jsonify({ "success": False, "error": 500, "message": "Server error" }), 500 @app.errorhandler(403) def forbidden(error): """ returns: handling for 403 (forbidden) Error """ return jsonify({ "success": False, "error": 403, "message": "forbidden" }), 403 @app.errorhandler(409) def Duplicate_resource(error): """ returns: handling for 409 (Duplicate resource) Error """ return jsonify({ "success": False, "error": 409, "message": "Duplicate resource" }), 409 @app.errorhandler(AuthError) def Auth_Error(error): """ returns: handling for 401 (Authentication error) Error """ return jsonify({ "success": False, "error": 401, "message": "Authentication error" }), 401 return app
def create_app(test_config=None): #load .env variables for Auth0 ENV_FILE = find_dotenv() if ENV_FILE: load_dotenv(ENV_FILE) AUTH0_CALLBACK_URL = os.getenv('AUTH0_CALLBACK_URL') AUTH0_CLIENT_ID = os.getenv('AUTH0_CLIENT_ID') AUTH0_CLIENT_SECRET = os.getenv('AUTH0_CLIENT_SECRET') AUTH0_DOMAIN = os.getenv('AUTH0_DOMAIN') AUTH0_BASE_URL = 'https://'+str(AUTH0_DOMAIN) AUTH0_AUDIENCE = os.getenv('AUTH0_AUDIENCE') app = Flask(__name__) app.config['SECRET_KEY'] = os.getenv('SECRET_KEY') app.debug = True setup_db(app) migrate = Migrate(app, db) oauth = OAuth(app) auth0 = oauth.register( 'auth0', client_id=AUTH0_CLIENT_ID, client_secret=AUTH0_CLIENT_SECRET, api_base_url=AUTH0_BASE_URL, access_token_url='https://onstagram.eu.auth0.com' + '/oauth/token', authorize_url='https://onstagram.eu.auth0.com' + '/authorize', client_kwargs={'scope': 'openid profile email'} ) # -------------------------------------------------------------------------# # Enable CORS # -------------------------------------------------------------------------# CORS(app) @app.after_request def after_request(response): response.headers.add('Access-Control-Allow-Headers', 'Content-Type,Authorization,true') response.headers.add('Access-Control-Allow-Methods', 'GET,PATCH,POST,DELETE,OPTIONS') return response #callback handling for Auth0 # route handler to log in @app.route('/login', methods=['GET']) @cross_origin() def login(): print('Audience: {}'.format(AUTH0_AUDIENCE)) print('callback: {}'.format(AUTH0_CALLBACK_URL)) return auth0.authorize_redirect( redirect_uri=AUTH0_CALLBACK_URL, audience=AUTH0_AUDIENCE ) # route handler for home page once logged in @app.route('/callback', methods=['GET']) @cross_origin() def post_login(): token = auth0.authorize_access_token() session['token'] = token['access_token'] resp = auth0.get('userinfo') userinfo = resp.json() session[constants.JWT_PAYLOAD] = userinfo session[constants.JWT] = token['access_token'] session[constants.PROFILE_KEY] = { 'user_id': userinfo['sub'], 'name': userinfo['name'] } print(session['token']) return redirect(url_for('profile')) @app.route('/logout') def logout(): try: session.clear() params = {'returnTo': url_for('home', _external=True), 'client_id': AUTH0_CLIENT_ID} return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) except Exception as e: print (e) # -------------------------------------------------------------------------# # Application Routes # -------------------------------------------------------------------------# @app.route('/') @cross_origin() def home(): try: groups = Group.query.all() permission = get_permission() posts = Post.query.order_by(Post.id.desc()) return render_template("index.html", posts=posts, groups=groups, permission=permission), 200 except Exception as e: print(e) abort(400) @app.route("/posts/<int:group_id>") def filter_posts(group_id): groups = Group.query.all() for g in groups: #check if requested group exists if g.id == group_id: group_id = g.id id=group_id break else: id=None if id: try: permission = get_permission() posts = Post.query.filter_by(group_id=id).order_by(Post.id.desc()) return render_template("posts.html", posts=posts, groups=groups, permission=permission),200 except Exception as e: print(f"error: {e}") abort(400) else: return "Group Not Found. Redirecting to home", {"Refresh": "3; url=http://localhost:5000"} @app.route('/profile') @cross_origin() def profile(): try: permission = get_permission() userinfo=session[constants.PROFILE_KEY] token=session['token'] return render_template("profile.html", userinfo = userinfo, token = token, permission = permission),200 except Exception as e: print(f"error: {e}") abort(401) # ----------- POSTS ENDPOINTS ------------ @app.route('/posts/create', methods=['POST', 'GET']) @cross_origin() @requires_auth('post:images') def create_post(payload): form = PostForm(request.form) groups = Group.query.all() form.group.choices = [(g.id, g.name) for g in groups] if request.method == "POST": try: userinfo=session[constants.PROFILE_KEY] author = userinfo.get("name"), post = Post() form.populate_obj(post) post.group_id= request.form.get("group") post.author = author post.insert() return redirect (url_for("home")) except Exception as e: print(e) abort(400) return render_template("new_post.html", form=form) @app.route('/posts/<post_id>/delete', methods=['DELETE']) @cross_origin() @requires_auth('post:images') def delete_post(payload, post_id): post = Post.query.filter_by(id=post_id).one_or_none() post.delete() print(f"post with id {post_id} successfully deleted") return redirect(url_for("home")) @app.route('/posts/<post_id>/edit', methods=['GET','POST']) @cross_origin() @requires_auth('post:images') def edit_post(payload, post_id): groups = Group.query.all() form = PostForm(request.form) form.group.choices = [(g.id, g.name) for g in groups] post = Post.query.filter_by(id=post_id).one_or_none() if request.method == "POST": form.populate_obj(post) post.group_id= request.form.get("group") post.update() return redirect(url_for("home")) else: form = PostForm(request.form) return render_template("edit_post.html", post=post, form=form) # ----------- GROUPS ENDPOINTS ------------ @app.route("/groups/create", methods=['POST', 'GET']) @cross_origin() @requires_auth("post:groups") def create_group(payload): form = GroupForm(request.form) groups = Group.query.all() if request.method == "POST": try: group = Group() form.populate_obj(group) group.insert() return redirect (url_for("home")),200 except Exception as e: print(e) abort(400) return render_template("new_group.html", form=form, groups=groups) @app.route('/groups/<group_id>/edit', methods=['GET','POST']) #TODO: add permission check @cross_origin() @requires_auth('post:groups') def edit_group(payload, group_id): group = Group.query.filter_by(id=group_id).one_or_none() if request.method == "POST": group.name=request.form.get("name") group.update() return redirect(url_for("home")) else: form = GroupForm(request.form) return render_template("edit_group.html", group=group, form=form) # -------------------------------------------------------------------------# # Error handling # -------------------------------------------------------------------------# @app.errorhandler(400) def bad_request(error): return jsonify({ 'success': False, 'error': 400, "message": "bad request" }), 400 @app.errorhandler(401) def unauthorized(error): return jsonify({ 'success': False, 'error': 401, "message": "unauthorized" }), 401 @app.errorhandler(404) def resource_not_found(error): return jsonify({ 'success': False, 'error': 404, 'message': 'resource not found' }), 404 @app.errorhandler(405) def method_not_allowed(error): return jsonify({ 'success': False, 'error': 405, 'message': 'method not allowed' }), 405 @app.errorhandler(422) def unprocessable_entity(error): return jsonify({ 'success': False, 'error': 422, 'message': 'unprocessable entity' }), 422 @app.errorhandler(500) def internal_server_error(error): return jsonify({ 'success': False, 'error': 500, 'message': 'internal server error' }), 422 return app
#!/usr/bin/python3 from authlib.integrations.flask_client import OAuth from flask import Flask, url_for, render_template, redirect import json import os # use loginpass to make OAuth connection simpler app = Flask(__name__) app.secret_key = os.environ.get('SECRET_KEY') or 'pengwingsiscool' oauth = OAuth(app) oauth.register( name='github', client_id=os.getenv('CLIENT_ID') or '10a81c493f912828f09f', client_secret=os.getenv('CLIENT_SECRET') or 'cd6b2f37ab6b2e2991ef2c09bc918a8499d4af11', access_token_url='https://github.com/login/oauth/access_token', access_token_params=None, authorize_url='https://github.com/login/oauth/authorize', authorize_params=None, api_base_url='https://api.github.com/', client_kwargs={'scope': 'user repo'}, ) @app.route('/') def homepage(): return render_template('index.html') @app.route('/login')
def create_app(test_config=None): app = Flask(__name__) oauth = OAuth(app) db.init_app (app) migrate.init_app (app, db) CORS(app) cors = CORS(app, resources={r"/*": {"origins": "*"}}) app.config['SECRET_KEY'] = '5791628bb0b13cefhri3j0c676dtrfefeefde280ba245' app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL'] ###app.config['SQLALCHEMY_DATABASE_URI'] = "postgres://*****:*****@localhost:5432/fitness_api" # ACTIVE SETTING def getJSON(filePathAndName): with open(filePathAndName, 'r') as fp: return json.load(fp) setting_list = getJSON("./settings.json") active_setting_JSON = getJSON("./active_setting.json") active_setting = active_setting_JSON["setting"] for setting in setting_list: if setting["setting"] == active_setting: env = setting #Auth0 auth0 = oauth.register( 'auth0', client_id=env["auth0"]["client_id"], client_secret=env["auth0"]["client_secret"], api_base_url='https://hnc.auth0.com', access_token_url='https://hnc.auth0.com/oauth/token', authorize_url='https://hnc.auth0.com/authorize', client_kwargs={ 'scope': 'openid profile email', }, ) @app.route('/callback') def callback_handling(): # Handles response from token endpoint try: auth0.authorize_access_token() resp = auth0.get('userinfo') userinfo = resp.json() access_token = auth0.token['access_token'] accessinfo = verify_decode_jwt(access_token) # Store the user information in flask session. session['accessinfo'] = accessinfo session['isLogged'] = True return redirect('/exercices') except: print(sys.exc_info()) abort(404) @app.route('/login') def login(): try: return auth0.authorize_redirect(redirect_uri=env["auth0"]["redirect_uri"], audience='fitnessapi') except: print(sys.exc_info()) abort(404) @app.route('/logout') def logout(): try: # Clear session stored data session.clear() # Redirect user to logout endpoint params = {'returnTo': url_for( 'index', _external=True), 'client_id': env["auth0"]["client_id"]} return redirect(auth0.api_base_url + '/v2/logout?' + urlencode(params)) except: print(sys.exc_info()) abort(404) ######################################################### GET ######################################################### GET ########################################### @app.route('/exercices', methods=['GET']) def index(): selection = Exercice.query.all() exercices = [Exercice.format() for Exercice in selection] return jsonify({'exercices' : exercices}) @app.route('/instructors', methods=['GET']) def instructors(): selection = Instructor.query.all() instructors = [Instructor.format() for Instructor in selection] return jsonify({'instructors' : instructors}) @app.route('/instructors/<int:inst_id>', methods=['GET']) @requires_auth('get:instructor') def show_instructor(inst_id): success = False try: instructor = Instructor.query.filter(Instructor.id == inst_id).one_or_none() instructor_json = instructor.format() success = True except: print(sys.exc_info()) abort(404) return jsonify({ 'instructor' : instructor_json, 'success' : success }) ######################################################### POST ######################################################### POST ########################################### @app.route('/exercices', methods=['POST']) @requires_auth('post:workout') def create_exercice(accessinfo): success = False try: body = request.get_json() name = body['name'] difficulty = body['difficulty'] muscles = body['muscles'] requirements = body['requirements'] video_path = body['video_path'] new_exercice = Exercice(name=name, difficulty=difficulty, muscles=muscles, requirements=requirements, video_path=video_path) db.session.add(new_exercice) db.session.commit() success = True except: print(sys.exc_info()) abort(404) return jsonify({ 'success' : success, 'created' : new_exercice.id, 'exercice name' : new_exercice.name }) @app.route('/instructors', methods=['POST']) @requires_auth('post:workout') def create_instructor(accessinfo): success = False try: body = request.get_json() name = body['name'] profile_pic_path = body['profile_pic_path'] age = body['age'] new_instructor = Instructor(name=name, profile_pic_path=profile_pic_path, age=age) db.session.add(new_instructor) db.session.commit() success = True except: print(sys.exc_info()) abort(404) return jsonify({ 'success' : success, 'created' : new_instructor.id, 'exercice name' : new_instructor.name }) ######################################################### DELETE ######################################################### DELETE ########################################### @app.route('/exercices/<int:ex_id>', methods=['DELETE']) @requires_auth('delete:workout') def delete_exercice(accessinfo, ex_id): success = False try: exercice = Exercice.query.filter(Exercice.id == ex_id).one_or_none() exercice.delete() success = True except: print(sys.exc_info()) abort(404) return jsonify({ 'success' : success, 'deleted' : exercice.id }) ######################################################### PATCH ######################################################### PATCH ########################################### @app.route('/exercices/<int:ex_id>', methods=['PATCH']) @requires_auth('patch:workout') def patch_exercice(accessinfo, ex_id): success = False try: exercice = Exercice.query.filter(Exercice.id == ex_id).first() body = request.get_json() exercice.name = body['name'] exercice.difficulty = body['difficulty'] exercice.muscles = body['muscles'] exercice.requirements = body['requirements'] exercice.video_path = body['video_path'] db.session.commit() success = True except: print(sys.exc_info()) abort(404) return jsonify({ 'success' : success, 'updated' : exercice.id, 'exercice name' : exercice.name }) ######################################################### ERRORS ######################################################### ERRORS ########################################### @app.errorhandler(404) def not_found(error): return jsonify({ "success": False, "error": 404, "message": "Not found" }), 404 @app.errorhandler(401) def unauthorized(error): return jsonify({ "success": False, "error": 401, "message": "Unauthorized!" }), 401 @app.errorhandler(405) def method_not_allowed(error): return jsonify({ "success": False, "error": 405, "message": "method not allowed" }), 405 @app.errorhandler(403) def forbidden(error): return jsonify({ "success": False, "error": 403, "message": "forbidden" }), 403 @app.errorhandler(AuthError) def auth_error(error): return jsonify({ "success": False, "error": 401, "message": "Unauthorized!" }), 401 @app.errorhandler(500) def handle_500(e): ''' original = getattr(e, "orig", None) if original is None: # direct 500 error, such as abort(500) return render_template("500.html"), 500 ''' return jsonify({ "success": False, "error": 500, "message": "Internal Server error" }), 500 return app