New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing connected social applications like twitter #3629

Merged
merged 2 commits into from May 27, 2017
Jump to file or symbol
Failed to load files and symbols.
+148 −27
Diff settings

Always

Just for now

View
@@ -45,7 +45,7 @@ class FbOAuth(object):
"""Facebook Credentials"""
Fb_AUTH_URI = 'https://www.facebook.com/dialog/oauth'
Fb_TOKEN_URI = 'https://graph.facebook.com/oauth/access_token'
Fb_USER_INFO = 'https://graph.facebook.com/me?fields=email,id,name,picture,bio,last_name,first_name,link'
Fb_USER_INFO = 'https://graph.facebook.com/me?fields=email,id,name,picture,last_name,first_name,link'
SCOPE = ['public_profile', 'email']
@classmethod
@@ -14,20 +14,27 @@
{% block content %}
<div style="display: block;">
{% if fb %}
<div class="row" style="margin-bottom:20px">
<div class="col-md-6 col-xs-6">
<label>Facebook:</label>
<div class="input-group">
<span class="input-group-addon social-addon">facebook.com/</span>
<input type="text" name="facebook" class="form-control"
value="{{ user.user_detail.facebook|default('', true) | replace('https://facebook.com/', '') }}"
value="{{ user.user_detail.facebook|default('', true) | replace('https://www.facebook.com/', '') }}"
style="min-width: 400px"
disabled />
{% if not user_id %}
disabled/>
{% if user.user_detail.facebook %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
style="margin-bottom: 0">{{ _("Connected") }}</a>
<a type="button" class="btn btn-info"
href="/unlink-social/facebook?next=settings/applications/"
style="margin-bottom: 0" title="{{ _('Click to Disconnect') }}" data-toggle="tooltip">{{ _("Connected") }}</a>
</div>
{% else %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
href="/profile/fb_connect"
style="margin-bottom: 0" >{{ _("Connect") }}</a>
</div>
{% endif %}
@@ -36,7 +43,9 @@
</div>
</div>
{% endif %}
{% if twitter %}
<div class="row" style="margin-bottom:20px">
<div class="col-md-6 col-xs-6">
<label>Twitter:</label>
@@ -46,16 +55,26 @@
<input type="text" name="twitter" class="form-control"
value="{{ user.user_detail.twitter|default('', true) | replace('https://twitter.com/', '') }}"
style="min-width: 400px"
disabled />
{% if not user_id %}
disabled/>
{% if user.user_detail.twitter %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
href="/unlink-social/twitter?next=settings/applications/"
style="margin-bottom: 0" title="{{ _('Click to Disconnect') }}" data-toggle="tooltip">{{ _("Connected") }}</a>
</div>
{% else %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
style="margin-bottom: 0">{{ _("Connected") }}</a>
href="/profile/tw_connect"
style="margin-bottom: 0" >{{ _("Connect") }}</a>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{% if insta %}
<div class="row" style="margin-bottom:20px">
<div class="col-md-6 col-xs-6">
<label>Instagram:</label>
@@ -66,16 +85,25 @@
value="{{ user.user_detail.instagram|default('', true) | replace('https://instagram.com/', '') }}"
style="min-width: 400px"
disabled />
{% if not user_id %}
{% if user.user_detail.instagram %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
href="/unlink-social/insta?next=settings/applications/"
style="margin-bottom: 0" title="{{ _('Click to Disconnect') }}" data-toggle="tooltip">{{ _("Connected") }}</a>
</div>
{% else %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
style="margin-bottom: 0">{{ _("Connected") }}</a>
href="/profile/instagram_connect"
style="margin-bottom: 0" >{{ _("Connect") }}</a>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
{% if gplus %}
<div class="row" style="margin-bottom:20px">
<div class="col-md-6 col-xs-6">
<label>Google+:</label>
@@ -86,15 +114,23 @@
value="{{ user.user_detail.google|default('', true) | replace('https://plus.google.com/', '') }}"
style="min-width: 400px"
disabled />
{% if not user_id %}
{% if user.user_detail.google %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
href="/unlink-social/google?next=settings/applications/"
style="margin-bottom: 0" title="{{ _('Click to Disconnect') }}" data-toggle="tooltip">{{ _("Connected") }}</a>
</div>
{% else %}
<div class="input-group-btn">
<a type="button" class="btn btn-info"
style="margin-bottom: 0">{{ _("Connected") }}</a>
href="/profile/google_connect"
style="margin-bottom: 0" >{{ _("Connect") }}</a>
</div>
{% endif %}
</div>
</div>
</div>
{% endif %}
<div class="btn-toolbar" data-role="editor-toolbar"
data-target="#editor">
View
@@ -47,7 +47,7 @@ def edit_view(user_id=None):
@profile.route('/fb_connect/', methods=('GET', 'POST'))
def connect_facebook():
facebook = get_facebook_auth()
fb_auth_url, state = facebook.authorization_url(FbOAuth.get_auth_uri(), access_type='offline')
fb_auth_url, __ = facebook.authorization_url(FbOAuth.get_auth_uri(), access_type='offline')
return redirect(fb_auth_url)
@@ -60,9 +60,15 @@ def connect_twitter():
@profile.route('/instagram_connect/', methods=('GET', 'POST'))
def connect_instagram():
instagram = get_instagram_auth()
instagram_auth_url, state = instagram.authorization_url(InstagramOAuth.get_auth_uri(), access_type='offline')
instagram_auth_url, __ = instagram.authorization_url(InstagramOAuth.get_auth_uri(), access_type='offline')
return redirect(instagram_auth_url)
@profile.route('/google_connect/', methods=('GET', 'POST'))
def google_connect():
google = get_google_auth()
gp_auth_url, __ = google.authorization_url(OAuth.get_auth_uri(), access_type='offline')
return redirect(gp_auth_url)
@profile.route('/<int:user_id>/editfiles/bgimage/', methods=('POST', 'DELETE'))
def bgimage_upload(user_id):
@@ -1,14 +1,15 @@
import unicodedata
from flask import Blueprint, render_template
from flask import request, url_for, redirect, jsonify, flash
from flask import request, url_for, redirect, jsonify, flash, session
from flask.ext import login
from flask.ext.scrypt import generate_password_hash, generate_random_salt
from app.helpers.data import DataManager, save_to_db
from app.helpers.data_getter import DataGetter
from app.models.email_notifications import EmailNotification
from app.views.home import record_user_login_logout
from app.settings import get_settings
def get_or_create_notification_settings(event_id):
@@ -70,8 +71,19 @@ def email_preferences_view():
@settings.route('/applications/')
def applications_view():
user = DataGetter.get_user(login.current_user.id)
settings = get_settings()
no_fb = settings['fb_client_id'] is None or settings['fb_client_secret'] is None
no_twitter = settings['tw_consumer_key'] is None or settings['tw_consumer_secret'] is None
no_insta = settings['in_client_id'] is None or settings['in_client_secret'] is None
no_gplus = settings['google_client_id'] is None or settings['google_client_secret'] is None
session['next_redirect'] = '/settings/applications'
return render_template('gentelella/users/settings/pages/applications.html',
user=user)
user=user,
fb= not no_fb,
twitter= not no_twitter,
insta= not no_insta,
gplus= not no_gplus)
@settings.route('/contact-info/', methods=('POST', 'GET'))
View
@@ -7,7 +7,7 @@
from flask import Blueprint, current_app
from flask import flash
from flask import jsonify, url_for, redirect, request, send_from_directory, \
render_template, make_response
render_template, make_response, session
from flask.ext import login
from flask.ext.migrate import upgrade
from requests.exceptions import HTTPError
@@ -24,10 +24,9 @@
utils_routes = Blueprint('', __name__)
@utils_routes.route('/gCallback/', methods=('GET', 'POST'))
def callback():
if login.current_user is not None and login.current_user.is_authenticated:
if login.current_user is not None and login.current_user.is_authenticated and 'test' in request.url:
return redirect(url_for('admin.index'))
elif 'error' in request.args:
if request.args.get('error') == 'access denied':
@@ -57,6 +56,14 @@ def callback():
resp = google.get(OAuth.get_user_info())
if resp.status_code == 200:
user_data = resp.json()
if login.current_user is not None and login.current_user.is_authenticated:
update_user_details(google=user_data['link'])
try:
if session['next_redirect']:
return redirect(session['next_redirect'])
except Exception:
pass
return redirect(url_for('admin.index'))
email = user_data['email']
user = DataGetter.get_user_by_email(email, no_flash=True)
user = create_user_oauth(user, user_data, token=token, method='Google')
@@ -87,6 +94,11 @@ def facebook_callback():
file_url=user_info['picture']['data']['url'])
except Exception:
pass
try:
if session['next_redirect']:
return redirect(session['next_redirect'])
except Exception:
pass
return redirect(url_for('admin.index'))
elif 'error' in request.args:
if request.args.get('error') == 'access denied':
@@ -119,10 +131,20 @@ def facebook_callback():
return redirect(url_for('admin.login_view'))
def update_user_details(first_name=None, last_name=None, facebook_link=None, twitter_link=None, file_url=None):
def update_user_details(first_name=None,
last_name=None,
facebook_link=None,
twitter_link=None,
file_url=None,
instagram=None,
google=None):
user = login.current_user
if not user.user_detail.facebook:
user.user_detail.facebook = facebook_link
if not user.user_detail.instagram:
user.user_detail.instagram = instagram
if not user.user_detail.google:
user.user_detail.google = google
if not user.user_detail.firstname:
user.user_detail.firstname = first_name
if not user.user_detail.lastname:
@@ -135,6 +157,17 @@ def update_user_details(first_name=None, last_name=None, facebook_link=None, twi
user.user_detail.twitter = twitter_link
save_to_db(user)
def delete_user_social_links(social):
user = login.current_user
if 'facebook' == social:
user.user_detail.facebook = None
if 'twitter' == social:
user.user_detail.twitter = None
if 'insta' == social:
user.user_detail.instagram = None
if 'google' == social:
user.user_detail.google = None
save_to_db(user)
def get_fb_auth():
facebook = get_facebook_auth()
@@ -164,6 +197,8 @@ def twitter_callback():
update_user_details(first_name=user_info['name'],
file_url=user_info['profile_image_url'],
twitter_link="https://twitter.com/" + access_token["screen_name"])
if session['next_redirect']:
return redirect(session['next_redirect'])
return redirect(url_for('profile.index_view'))
@@ -181,13 +216,22 @@ def instagram_callback():
response = instagram.get(
'https://api.instagram.com/v1/users/self/media/recent/?access_token=' + token.get('access_token',
'')).json()
for el in response.get('data'):
filename, uploaded_file = uploaded_file_provided_by_url(el['images']['standard_resolution']['url'])
upload(uploaded_file, '/image/' + filename)
flash("OAuth Authorization error. Please try again later.")
update_user_details(instagram=el['caption']['from']['username'])
if session['next_redirect']:
return redirect(session['next_redirect'])
else:
flash("OAuth Authorization error. Please try again later.")
return redirect(url_for('admin.login_view'))
@utils_routes.route('/unlink-social/<social>')
def unlink_social(social):
if login.current_user is not None and login.current_user.is_authenticated:
delete_user_social_links(social)
return redirect(intended_url())
@utils_routes.route('/pic/<path:filename>')
def send_pic(filename):
View
@@ -53,3 +53,5 @@ pycountry
pytz
diff-match-patch
blinker
httmock
@@ -1,9 +1,28 @@
from flask import url_for
from httmock import urlmatch, response
from app.helpers.data import DataManager, save_to_db
from app.helpers.helpers import get_serializer
@urlmatch(netloc='https://www.googleapis.com/userinfo/v2/me')
def google_profile_mock(url, request):
headers = {'content-type': 'application/json'}
content = {'link':'http://google.com/some_id'}
return response(200, content, headers, None, 5, request)
@urlmatch(netloc=r'(.*\.)?google\.com$')
def google_auth_mock(url, request):
headers = {'content-type': 'application/json'}
content = {
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"Bearer",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
return response(200, content, headers, None, 5, request)
def login(app, email, password):
return app.post('login/',
data=dict(
@@ -1,11 +1,12 @@
import unittest
from oauthlib.oauth2 import WebApplicationClient
from httmock import HTTMock
from app import current_app as app
from app.helpers.data import get_google_auth
from app.helpers.oauth import OAuth
from tests.unittests.auth_helper import login, logout, register
from tests.unittests.auth_helper import login, logout, register, google_profile_mock, google_auth_mock
from tests.unittests.setup_database import Setup
from tests.unittests.utils import OpenEventTestCase
@@ -21,9 +22,10 @@ def test_user_already_logged_in(self):
register(self.app, 'email@gmail.com', 'test')
logout(self.app)
login(self.app, 'email@gmail.com', 'test')
self.assertTrue('Open Event' in self.app.get('/gCallback/?state=dummy_state&code=dummy_code',
with HTTMock(google_auth_mock, google_profile_mock):
self.assertTrue('Open Event' in self.app.get('/gCallback/?state=dummy_state&code=dummy_code',
follow_redirects=True).data)
self.assertEqual(self.app.get('/gCallback/?state=dummy_state&code=dummy_code').status_code, 302)
self.assertEqual(self.app.get('/gCallback/?state=dummy_state&code=dummy_code').status_code, 302)
def test_redirect(self):
"""Tests whether on redirection the user is being redirected to the proper authentication url of Google"""
ProTip! Use n and p to navigate between commits in a pull request.