def fconnect(): """Handle facebook OAuth login GET /auth/fconnect If user does not exists create a new user. """ # userinfo has email, username, and access token userinfo = json.loads(request.data) user_access_token = userinfo.get('access_token') # To verify user's access token, we need to get our app token first. url = ('https://graph.facebook.com/oauth/access_token?' 'client_id={}&client_secret={}' '&grant_type=client_credentials'.format( config.FACEBOOK_CLIENT_ID, config.FACEBOOK_CLIENT_SECRET)) try: h = httplib2.Http() result = h.request(url, 'GET')[1] app_token = re.search(r'(access_token=)(.+?$)', result).group(2) print app_token # Using app token, we can verify user's access token url = ('https://graph.facebook.com/debug_token' '?input_token={}&access_token={}'.format( user_access_token, app_token)) h = httplib2.Http() result = json.loads(h.request(url, 'GET')[1]) user_data = result.get("data") # If the user's token is valid to the app token, # Facecook api returns the variable 'is_valid' with True if not user_data.get("is_valid"): response = make_response( json.dumps("User access token is not valid"), 401) flash("Facebook connection Error.") response.headers['Content-Type'] = 'application/json' return response email = userinfo.get('email') user = User.get_by_email(session, email.strip()) # Create and store a new user if there is no user exist if not user: user = User(email=email) session.add(user) session.commit() expire_time, token = generate_token(user) flash("Successfully logged in with Facebook") response = make_response(redirect(url_for('basic.showMain')), 200) response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) return response except: response = make_response(json.dumps("User access token is not valid"), 401) flash("Facebook connection Error.") response.headers['Content-Type'] = 'application/json' return response
def deleteItem(item_id): """ GET /item/item id/delete: Render an delete item form page POST /item/item id/delete: Delete the selected item from database """ token = request.cookies.get('token') expire_time = request.cookies.get('expire_time') # Only authorized user can see an edit item page if not token: flash("You are not authorized.") return redirect(url_for('basic.showMain')) if request.method == "GET": # Only authorized user can see a delete item page user_data = validate_token(token, expire_time) if not user_data: flash("You are not authorized.") return redirect(url_for('basic.showMain')) item = Item.get_by_id(session, item_id) return render_template('delete_item.html', item=item, user=user_data) if request.method == "POST": # When user send POST request, # we get a token again from HTTP header, not from cookie token = request.headers.get('Authorization') # Get item to delete item = Item.get_by_id(session, item_id) # Only authorized user can delete this item user_data = validate_token(token, expire_time) if not user_data: response = make_response( json.dumps({ "message": "You are not authorized", "redirect": url_for('basic.showItemDetail', category_id=item.category_id, item_id=item_id) }), 401 ) response.headers['Content-Type'] = 'application/json' return response # Only authorized user can delete an item # Authorized user id must be the same as # the user's id who created the item before. user = User.get_by_id(session, user_data.get("id")) if not User.is_authorized(session, user.id, item_id): response = make_response( json.dumps({ "message": "You are not authorized", "redirect": url_for('basic.showItemDetail', category_id=item.category_id, item_id=item_id) }), 401 ) response.headers['Content-Type'] = 'application/json' return response session.delete(item) session.commit() response = make_response( json.dumps({ "message": "The item was successfully deleted.", "redirect": url_for('basic.showMain') }), 200 ) response.headers['Content-Type'] = 'application/json' return response
def editItem(category_id, item_id): """ GET /category/category id/item/item id/edit: Render an edit item form page POST /category/category id/item/item id/edit: Update the selected item's attributes Fields: title (required) description category (required) """ token = request.cookies.get('token') expire_time = request.cookies.get('expire_time') # Only authorized user can see an edit item page if not token: flash("You are not authorized.") return redirect(url_for('basic.showMain')) if request.method == "GET": # Only authorized user can see an edit item page user_data = validate_token(token, expire_time) if not user_data: flash("You are not authorized.") return redirect(url_for('basic.showMain')) # Only authorized user can see an edit item page # Authorized user id must be the same as # the user's id who created the item before. if not User.is_authorized(session, user_data.get("id"), item_id): flash("You are not authorized.") return redirect(url_for('basic.showMain')) categories = Category.get_all(session) item = Item.get_by_id(session, item_id) return render_template('edit_item.html', categories=categories, item=item) if request.method == "POST": # When user send POST request, # we get a token again from HTTP header, not from cookie token = request.headers.get('Authorization') # Only authorized user can edit this item user_data = validate_token(token, expire_time) if not user_data: response = make_response( json.dumps({ "message": "You are not authorized", "redirect": url_for('basic.showItemDetail', category_id=category_id, item_id=item_id) }), 401 ) response.headers['Content-Type'] = 'application/json' return response item = Item.get_by_id(session, item_id) title = request.form.get('title') description = request.form.get('description') new_category_id = request.form.get('category') # In the form in HTML title field is required. # No title means the user use another way to send POST request if not title: response = make_response( json.dumps({ "message": "Please use the proper way", "redirect": url_for('basic.showItemDetail', category_id=category_id, item_id=item_id) }), 401 ) response.headers['Content-Type'] = 'application/json' return response # Only authorized user can edit item # Authorized user id must be the same as # the user's id who created the item before. user = User.get_by_id(session, user_data.get("id")) if not User.is_authorized(session, user.id, item_id): response = make_response( json.dumps({ "message": "You are not authorized", "redirect": url_for('basic.showItemDetail', category_id=item.category_id, item_id=item_id) }), 401 ) response.headers['Content-Type'] = 'application/json' return response item.title = title item.description = description item.category_id = new_category_id session.add(item) session.commit() response = make_response( json.dumps({ "message": "The item was successfully edited.", "redirect": url_for('basic.showItemDetail', category_id=category_id, item_id=item.id) }), 200 ) response.headers['Content-Type'] = 'application/json' return response
def login(cached_email=None): """Render login page and handle login form data. Requests: GET /auth/login POST /auth/login """ if request.method == 'GET': csrf_token = generate_csrf_token() response = make_response( render_template('login.html', cached_email=cached_email, client_id=CLIENT_ID, csrf_token=csrf_token)) # Store the csrf_token in the browser cookie. response.set_cookie('csrf_token', value=csrf_token) return response # Form fields: # email: user email, required # password: user password, required if request.method == 'POST': # Check csrf token cookie_csrf_token = request.cookies.get('csrf_token') form_csrf_token = request.form.get('_csrf_token') # CSRF attack detected! if cookie_csrf_token != form_csrf_token: flash("Please use proper login.") return render_template('login.html', cached_email=cached_email, client_id=CLIENT_ID, csrf_token="") # Get user data from login form. email = request.form.get('email') password = request.form.get('password') # User must fill the email and password field. if not (email and password): flash("Please fill the form. ") return render_template('login.html', cached_email=email) # Find user in the database by email. user = User.get_by_email(session, email.strip()) # User does not exists. if not user: flash("Invalid email address or password. ") return render_template('login.html', cached_email=email) # User exist, but Password does not. # The user have logged in with OAuth if not user.password: flash("You've signed up with social service. ") return render_template('login.html', cached_email=email) # Password incorrect. if not check_password(password, user.password, user.salt): flash("Invalid email address or password. ") return render_template('login.html', cached_email=email) # Generate JSON web token for user. # As long as client has non-expired and valid token, # they do not need to login again. expire_time, token = generate_token(user) response = make_response(redirect(url_for('basic.showMain'))) # Store the token in the browser cookie. response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) return response
def signup(): """Render login page and handle login form data. Requests: GET /auth/signup POST /auth/signup """ if request.method == 'GET': csrf_token = generate_csrf_token() response = make_response( render_template('signup.html', client_id=CLIENT_ID)) # Store the csrf_token in the browser cookie. response.set_cookie('csrf_token', value=csrf_token) return response # Form fields: # email: user email, required # password: user password, required # confirm: user confirm password, required # User email, and hashed password and salt are stored when login succeed. if request.method == 'POST': # Check csrf token cookie_csrf_token = request.cookies.get('csrf_token') form_csrf_token = request.form.get('_csrf_token') # CSRF attack detected! if cookie_csrf_token != form_csrf_token: flash("Please use proper signup.") return render_template('signup.html', client_id=CLIENT_ID, csrf_token="") # Get user data from login form. email = request.form.get('email') password = request.form.get('password') confirm = request.form.get('confirm') # User must fill the email and password field. if not (email and password and confirm): flash("Please fill the form. ") return render_template('signup.html', cached_email=email) # Password field and confirm fields must be the same. if not (password == confirm): flash("Confirm password has to be the same as password") return render_template('signup.html', cached_email=email) # Find user in the database by email. user = User.get_by_email(session, email.strip()) # User already exist, remind user that. if user: if user.password: flash("Such user already exist. Please login") return render_template('signup.html', cached_email=email) # Create a new user object else: user = User(email=email.strip()) # Store encrypted password and salt in the database user.password, user.salt = encrypt_password(password) session.add(user) session.commit() # Generate JSON web token for user. # As long as client has non-expired and valid token, # they do not need to login again. expire_time, token = generate_token(user) response = make_response(redirect(url_for('basic.showMain'))) # Store the token in the browser cookie. response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) return response
def gconnect(): """Handle Google OAuth login. GET /auth/gconnect If user does not exists create a new user. """ # Check csrf token cookie_csrf_token = request.cookies.get('csrf_token') if request.args.get('_csrf_token') != cookie_csrf_token: flash("Please use proper authentication.") response = make_response(json.dumps('Fail to connect'), 401) response.headers['Content-Type'] = 'application/json' return response # code is a return value from front-end google + oauth API code = request.data try: # Create oauth login flow based on client_secret.json # Please make sure that you have downloaded and placed # client_secret.json properly. Please read README file. oauth_flow = flow_from_clientsecrets('settings/client_secret.json', scope='') oauth_flow.redirect_uri = 'postmessage' credentials = oauth_flow.step2_exchange(code) except FlowExchangeError: flash("Google plus connection Error.") response = make_response(json.dumps('Fail to upgrade'), 401) response.headers['Content-Type'] = 'application/json' return response # Get an access_token from Goolge OAuth provider access_token = credentials.access_token url = ('https://www.googleapis.' 'com/oauth2/v1/tokeninfo?access_token=%s' % access_token) h = httplib2.Http() result = json.loads(h.request(url, 'GET')[1]) if result.get('error') is not None: flash("Google plus connection Error.") response = make_response(json.dumps(result.get('error')), 500) response.headers['Content-Type'] = 'application/json' return response # Get user id stored in Google gplus_id = credentials.id_token['sub'] if result['user_id'] != gplus_id: flash("Google plus connection Error.") response = make_response(json.dumps("Token's user ID doesn't match"), 401) response.headers['Content-Type'] = 'application/json' return response # Make sure client id is correct if result['issued_to'] != CLIENT_ID: response = make_response(json.dumps("Token's client ID doesn't match"), 401) flash("Google plus connection Error.") response.headers['Content-Type'] = 'application/json' return response # Retrieve user info. stored in Google userinfo_url = 'https://www.googleapis.com/oauth2/v1/userinfo' params = {'access_token': credentials.access_token, 'alt': 'json'} answer = requests.get(userinfo_url, params=params) data = json.loads(answer.text) email = data['email'] user = User.get_by_email(session, email.strip()) # If user does not exist, create a new user if not user: user = User(email=email) session.add(user) session.commit() # Generate JSON web token for user. # As long as client has non-expired and valid token, # they do not need to login again. flash("Successfully logged in with Google +") expire_time, token = generate_token(user) response = make_response(redirect(url_for('basic.showMain'))) # Store the JSON web token and Google + access token in the browser cookie. response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) response.set_cookie('gplus_token', value=access_token) return response
def fconnect(): """Handle facebook OAuth login GET /auth/fconnect If user does not exists create a new user. """ # userinfo has email, username, and access token userinfo = json.loads(request.data) user_access_token = userinfo.get('access_token') # To verify user's access token, we need to get our app token first. url = ('https://graph.facebook.com/oauth/access_token?' 'client_id={}&client_secret={}' '&grant_type=client_credentials' .format(config.FACEBOOK_CLIENT_ID, config.FACEBOOK_CLIENT_SECRET)) try: h = httplib2.Http() result = h.request(url, 'GET')[1] app_token = re.search(r'(access_token=)(.+?$)', result).group(2) print app_token # Using app token, we can verify user's access token url = ('https://graph.facebook.com/debug_token' '?input_token={}&access_token={}' .format(user_access_token, app_token)) h = httplib2.Http() result = json.loads(h.request(url, 'GET')[1]) user_data = result.get("data") # If the user's token is valid to the app token, # Facecook api returns the variable 'is_valid' with True if not user_data.get("is_valid"): response = make_response( json.dumps("User access token is not valid"), 401 ) flash("Facebook connection Error.") response.headers['Content-Type'] = 'application/json' return response email = userinfo.get('email') user = User.get_by_email(session, email.strip()) # Create and store a new user if there is no user exist if not user: user = User(email=email) session.add(user) session.commit() expire_time, token = generate_token(user) flash("Successfully logged in with Facebook") response = make_response( redirect(url_for('basic.showMain')), 200 ) response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) return response except: response = make_response( json.dumps("User access token is not valid"), 401 ) flash("Facebook connection Error.") response.headers['Content-Type'] = 'application/json' return response
def login(cached_email=None): """Render login page and handle login form data. Requests: GET /auth/login POST /auth/login """ if request.method == 'GET': csrf_token = generate_csrf_token() response = make_response( render_template('login.html', cached_email=cached_email, client_id=CLIENT_ID, csrf_token=csrf_token) ) # Store the csrf_token in the browser cookie. response.set_cookie('csrf_token', value=csrf_token) return response # Form fields: # email: user email, required # password: user password, required if request.method == 'POST': # Check csrf token cookie_csrf_token = request.cookies.get('csrf_token') form_csrf_token = request.form.get('_csrf_token') # CSRF attack detected! if cookie_csrf_token != form_csrf_token: flash("Please use proper login.") return render_template('login.html', cached_email=cached_email, client_id=CLIENT_ID, csrf_token="") # Get user data from login form. email = request.form.get('email') password = request.form.get('password') # User must fill the email and password field. if not (email and password): flash("Please fill the form. ") return render_template('login.html', cached_email=email) # Find user in the database by email. user = User.get_by_email(session, email.strip()) # User does not exists. if not user: flash("Invalid email address or password. ") return render_template('login.html', cached_email=email) # User exist, but Password does not. # The user have logged in with OAuth if not user.password: flash("You've signed up with social service. ") return render_template('login.html', cached_email=email) # Password incorrect. if not check_password(password, user.password, user.salt): flash("Invalid email address or password. ") return render_template('login.html', cached_email=email) # Generate JSON web token for user. # As long as client has non-expired and valid token, # they do not need to login again. expire_time, token = generate_token(user) response = make_response(redirect(url_for('basic.showMain'))) # Store the token in the browser cookie. response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) return response
def gconnect(): """Handle Google OAuth login. GET /auth/gconnect If user does not exists create a new user. """ # Check csrf token cookie_csrf_token = request.cookies.get('csrf_token') if request.args.get('_csrf_token') != cookie_csrf_token: flash("Please use proper authentication.") response = make_response(json.dumps('Fail to connect'), 401) response.headers['Content-Type'] = 'application/json' return response # code is a return value from front-end google + oauth API code = request.data try: # Create oauth login flow based on client_secret.json # Please make sure that you have downloaded and placed # client_secret.json properly. Please read README file. oauth_flow = flow_from_clientsecrets('settings/client_secret.json', scope='') oauth_flow.redirect_uri = 'postmessage' credentials = oauth_flow.step2_exchange(code) except FlowExchangeError: flash("Google plus connection Error.") response = make_response(json.dumps('Fail to upgrade'), 401) response.headers['Content-Type'] = 'application/json' return response # Get an access_token from Goolge OAuth provider access_token = credentials.access_token url = ('https://www.googleapis.' 'com/oauth2/v1/tokeninfo?access_token=%s' % access_token) h = httplib2.Http() result = json.loads(h.request(url, 'GET')[1]) if result.get('error') is not None: flash("Google plus connection Error.") response = make_response( json.dumps(result.get('error')), 500 ) response.headers['Content-Type'] = 'application/json' return response # Get user id stored in Google gplus_id = credentials.id_token['sub'] if result['user_id'] != gplus_id: flash("Google plus connection Error.") response = make_response( json.dumps("Token's user ID doesn't match"), 401 ) response.headers['Content-Type'] = 'application/json' return response # Make sure client id is correct if result['issued_to'] != CLIENT_ID: response = make_response( json.dumps("Token's client ID doesn't match"), 401 ) flash("Google plus connection Error.") response.headers['Content-Type'] = 'application/json' return response # Retrieve user info. stored in Google userinfo_url = 'https://www.googleapis.com/oauth2/v1/userinfo' params = {'access_token': credentials.access_token, 'alt': 'json'} answer = requests.get(userinfo_url, params=params) data = json.loads(answer.text) email = data['email'] user = User.get_by_email(session, email.strip()) # If user does not exist, create a new user if not user: user = User(email=email) session.add(user) session.commit() # Generate JSON web token for user. # As long as client has non-expired and valid token, # they do not need to login again. flash("Successfully logged in with Google +") expire_time, token = generate_token(user) response = make_response(redirect(url_for('basic.showMain'))) # Store the JSON web token and Google + access token in the browser cookie. response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) response.set_cookie('gplus_token', value=access_token) return response
def signup(): """Render login page and handle login form data. Requests: GET /auth/signup POST /auth/signup """ if request.method == 'GET': csrf_token = generate_csrf_token() response = make_response( render_template('signup.html', client_id=CLIENT_ID) ) # Store the csrf_token in the browser cookie. response.set_cookie('csrf_token', value=csrf_token) return response # Form fields: # email: user email, required # password: user password, required # confirm: user confirm password, required # User email, and hashed password and salt are stored when login succeed. if request.method == 'POST': # Check csrf token cookie_csrf_token = request.cookies.get('csrf_token') form_csrf_token = request.form.get('_csrf_token') # CSRF attack detected! if cookie_csrf_token != form_csrf_token: flash("Please use proper signup.") return render_template('signup.html', client_id=CLIENT_ID, csrf_token="") # Get user data from login form. email = request.form.get('email') password = request.form.get('password') confirm = request.form.get('confirm') # User must fill the email and password field. if not (email and password and confirm): flash("Please fill the form. ") return render_template('signup.html', cached_email=email) # Password field and confirm fields must be the same. if not (password == confirm): flash("Confirm password has to be the same as password") return render_template('signup.html', cached_email=email) # Find user in the database by email. user = User.get_by_email(session, email.strip()) # User already exist, remind user that. if user: if user.password: flash("Such user already exist. Please login") return render_template('signup.html', cached_email=email) # Create a new user object else: user = User(email=email.strip()) # Store encrypted password and salt in the database user.password, user.salt = encrypt_password(password) session.add(user) session.commit() # Generate JSON web token for user. # As long as client has non-expired and valid token, # they do not need to login again. expire_time, token = generate_token(user) response = make_response(redirect(url_for('basic.showMain'))) # Store the token in the browser cookie. response.set_cookie('token', value=token) response.set_cookie('expire_time', value=str(expire_time)) return response
# Get engine and session for dummy data importing engine = create_engine(config.DATABASE_URI) Base.metadata.create_all(engine) DBSession = sessionmaker(bind=engine) session = DBSession() # Create dummy users(10 users) # Example: # Username: [email protected] ~ [email protected] # Password: user1password ~ user10@password for i in range(10): password = "******".format(i + 1) enc, salt = encrypt_password(password) user = User(name="user{}".format(i + 1), email="user{}@email.com".format(i + 1), password=enc, salt=salt) session.add(user) session.commit() # Create dummy categories and items(10 categories, 100 items) # Example: # Category: category1 ~ category10 # Item: item1_c1 ~ item10_c10 for c in range(10): category = Category(name="category{}".format(c + 1)) session.add(category) session.commit() # 10 items in each category for i in range(10):
def deleteItem(item_id): """ GET /item/item id/delete: Render an delete item form page POST /item/item id/delete: Delete the selected item from database """ token = request.cookies.get("token") expire_time = request.cookies.get("expire_time") # Only authorized user can see an edit item page if not token: flash("You are not authorized.") return redirect(url_for("basic.showMain")) if request.method == "GET": # Only authorized user can see a delete item page user_data = validate_token(token, expire_time) if not user_data: flash("You are not authorized.") return redirect(url_for("basic.showMain")) item = Item.get_by_id(session, item_id) return render_template("delete_item.html", item=item, user=user_data) if request.method == "POST": # When user send POST request, # we get a token again from HTTP header, not from cookie token = request.headers.get("Authorization") # Get item to delete item = Item.get_by_id(session, item_id) # Only authorized user can delete this item user_data = validate_token(token, expire_time) if not user_data: response = make_response( json.dumps( { "message": "You are not authorized", "redirect": url_for("basic.showItemDetail", category_id=item.category_id, item_id=item_id), } ), 401, ) response.headers["Content-Type"] = "application/json" return response # Only authorized user can delete an item # Authorized user id must be the same as # the user's id who created the item before. user = User.get_by_id(session, user_data.get("id")) if not User.is_authorized(session, user.id, item_id): response = make_response( json.dumps( { "message": "You are not authorized", "redirect": url_for("basic.showItemDetail", category_id=item.category_id, item_id=item_id), } ), 401, ) response.headers["Content-Type"] = "application/json" return response session.delete(item) session.commit() response = make_response( json.dumps({"message": "The item was successfully deleted.", "redirect": url_for("basic.showMain")}), 200 ) response.headers["Content-Type"] = "application/json" return response
def editItem(category_id, item_id): """ GET /category/category id/item/item id/edit: Render an edit item form page POST /category/category id/item/item id/edit: Update the selected item's attributes Fields: title (required) description category (required) """ token = request.cookies.get("token") expire_time = request.cookies.get("expire_time") # Only authorized user can see an edit item page if not token: flash("You are not authorized.") return redirect(url_for("basic.showMain")) if request.method == "GET": # Only authorized user can see an edit item page user_data = validate_token(token, expire_time) if not user_data: flash("You are not authorized.") return redirect(url_for("basic.showMain")) # Only authorized user can see an edit item page # Authorized user id must be the same as # the user's id who created the item before. if not User.is_authorized(session, user_data.get("id"), item_id): flash("You are not authorized.") return redirect(url_for("basic.showMain")) categories = Category.get_all(session) item = Item.get_by_id(session, item_id) return render_template("edit_item.html", categories=categories, item=item) if request.method == "POST": # When user send POST request, # we get a token again from HTTP header, not from cookie token = request.headers.get("Authorization") # Only authorized user can edit this item user_data = validate_token(token, expire_time) if not user_data: response = make_response( json.dumps( { "message": "You are not authorized", "redirect": url_for("basic.showItemDetail", category_id=category_id, item_id=item_id), } ), 401, ) response.headers["Content-Type"] = "application/json" return response item = Item.get_by_id(session, item_id) title = request.form.get("title") description = request.form.get("description") new_category_id = request.form.get("category") # In the form in HTML title field is required. # No title means the user use another way to send POST request if not title: response = make_response( json.dumps( { "message": "Please use the proper way", "redirect": url_for("basic.showItemDetail", category_id=category_id, item_id=item_id), } ), 401, ) response.headers["Content-Type"] = "application/json" return response # Only authorized user can edit item # Authorized user id must be the same as # the user's id who created the item before. user = User.get_by_id(session, user_data.get("id")) if not User.is_authorized(session, user.id, item_id): response = make_response( json.dumps( { "message": "You are not authorized", "redirect": url_for("basic.showItemDetail", category_id=item.category_id, item_id=item_id), } ), 401, ) response.headers["Content-Type"] = "application/json" return response item.title = title item.description = description item.category_id = new_category_id session.add(item) session.commit() response = make_response( json.dumps( { "message": "The item was successfully edited.", "redirect": url_for("basic.showItemDetail", category_id=category_id, item_id=item.id), } ), 200, ) response.headers["Content-Type"] = "application/json" return response