How to make facebook app not ask for permission to use friends - django

Hi I use python for my facebook app and want to restrict my app from gaining permissions to the users friends. How can I accomplish this? Some code is conf.py
# Facebook Application ID and Secret.
FACEBOOK_APP_ID = '103297833078853'
FACEBOOK_APP_SECRET = 'd1f7a3dfeb650b6826456a5660134f58'
# Canvas Page name.
FACEBOOK_CANVAS_NAME = 'cyberfaze'
# A random token for use with the Real-time API.
FACEBOOK_REALTIME_VERIFY_TOKEN = 'RANDOM TOKEN'
# The external URL this application is available at where the Real-time API will
# send it's pings.
EXTERNAL_HREF = 'http://cyberfaze.appspot.com/'
# Facebook User IDs of admins. The poor mans admin system.
ADMIN_USER_IDS = ['5526183']
Here is main.py
#!/usr/bin/env python
# coding: utf-8
# Copyright 2011 Facebook, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import os
# dummy config to enable registering django template filters
os.environ[u'DJANGO_SETTINGS_MODULE'] = u'conf'
from google.appengine.dist import use_library
use_library('django', '1.2')
from django.template.defaultfilters import register
from django.utils import simplejson as json
from functools import wraps
from google.appengine.api import urlfetch, taskqueue
from google.appengine.ext import db, webapp
from google.appengine.ext.webapp import util, template
from google.appengine.runtime import DeadlineExceededError
from random import randrange
from uuid import uuid4
import Cookie
import base64
import cgi
import conf
import datetime
import hashlib
import hmac
import logging
import time
import traceback
import urllib
def htmlescape(text):
"""Escape text for use as HTML"""
return cgi.escape(
text, True).replace("'", ''').encode('ascii', 'xmlcharrefreplace')
#register.filter(name=u'get_name')
def get_name(dic, index):
"""Django template filter to render name"""
return dic[index].name
#register.filter(name=u'get_picture')
def get_picture(dic, index):
"""Django template filter to render picture"""
return dic[index].picture
def select_random(lst, limit):
"""Select a limited set of random non Falsy values from a list"""
final = []
size = len(lst)
while limit and size:
index = randrange(min(limit, size))
size = size - 1
elem = lst[index]
lst[index] = lst[size]
if elem:
limit = limit - 1
final.append(elem)
return final
_USER_FIELDS = u'name,email,picture,friends'
class User(db.Model):
user_id = db.StringProperty(required=True)
access_token = db.StringProperty(required=True)
name = db.StringProperty(required=True)
picture = db.StringProperty(required=True)
email = db.StringProperty()
friends = db.StringListProperty()
dirty = db.BooleanProperty()
def refresh_data(self):
"""Refresh this user's data using the Facebook Graph API"""
me = Facebook().api(u'/me',
{u'fields': _USER_FIELDS, u'access_token': self.access_token})
self.dirty = False
self.name = me[u'name']
self.email = me.get(u'email')
self.picture = me[u'picture']
self.friends = [user[u'id'] for user in me[u'friends'][u'data']]
return self.put()
class Run(db.Model):
user_id = db.StringProperty(required=True)
location = db.StringProperty(required=True)
distance = db.FloatProperty(required=True)
date = db.DateProperty(required=True)
#staticmethod
def find_by_user_ids(user_ids, limit=50):
if user_ids:
return Run.gql(u'WHERE user_id IN :1', user_ids).fetch(limit)
else:
return []
#property
def pretty_distance(self):
return u'%.2f' % self.distance
class RunException(Exception):
pass
class FacebookApiError(Exception):
def __init__(self, result):
self.result = result
def __str__(self):
return self.__class__.__name__ + ': ' + json.dumps(self.result)
class Facebook(object):
"""Wraps the Facebook specific logic"""
def __init__(self, app_id=conf.FACEBOOK_APP_ID,
app_secret=conf.FACEBOOK_APP_SECRET):
self.app_id = app_id
self.app_secret = app_secret
self.user_id = None
self.access_token = None
self.signed_request = {}
def api(self, path, params=None, method=u'GET', domain=u'graph'):
"""Make API calls"""
if not params:
params = {}
params[u'method'] = method
if u'access_token' not in params and self.access_token:
params[u'access_token'] = self.access_token
result = json.loads(urlfetch.fetch(
url=u'https://' + domain + u'.facebook.com' + path,
payload=urllib.urlencode(params),
method=urlfetch.POST,
headers={
u'Content-Type': u'application/x-www-form-urlencoded'})
.content)
if isinstance(result, dict) and u'error' in result:
raise FacebookApiError(result)
return result
def load_signed_request(self, signed_request):
"""Load the user state from a signed_request value"""
try:
sig, payload = signed_request.split(u'.', 1)
sig = self.base64_url_decode(sig)
data = json.loads(self.base64_url_decode(payload))
expected_sig = hmac.new(
self.app_secret, msg=payload, digestmod=hashlib.sha256).digest()
# allow the signed_request to function for upto 1 day
if sig == expected_sig and \
data[u'issued_at'] > (time.time() - 86400):
self.signed_request = data
self.user_id = data.get(u'user_id')
self.access_token = data.get(u'oauth_token')
except ValueError, ex:
pass # ignore if can't split on dot
#property
def user_cookie(self):
"""Generate a signed_request value based on current state"""
if not self.user_id:
return
payload = self.base64_url_encode(json.dumps({
u'user_id': self.user_id,
u'issued_at': str(int(time.time())),
}))
sig = self.base64_url_encode(hmac.new(
self.app_secret, msg=payload, digestmod=hashlib.sha256).digest())
return sig + '.' + payload
#staticmethod
def base64_url_decode(data):
data = data.encode(u'ascii')
data += '=' * (4 - (len(data) % 4))
return base64.urlsafe_b64decode(data)
#staticmethod
def base64_url_encode(data):
return base64.urlsafe_b64encode(data).rstrip('=')
class CsrfException(Exception):
pass
class BaseHandler(webapp.RequestHandler):
facebook = None
user = None
csrf_protect = True
def initialize(self, request, response):
"""General initialization for every request"""
super(BaseHandler, self).initialize(request, response)
try:
self.init_facebook()
self.init_csrf()
self.response.headers[u'P3P'] = u'CP=HONK' # iframe cookies in IE
except Exception, ex:
self.log_exception(ex)
raise
def handle_exception(self, ex, debug_mode):
"""Invoked for unhandled exceptions by webapp"""
self.log_exception(ex)
self.render(u'error',
trace=traceback.format_exc(), debug_mode=debug_mode)
def log_exception(self, ex):
"""Internal logging handler to reduce some App Engine noise in errors"""
msg = ((str(ex) or ex.__class__.__name__) +
u': \n' + traceback.format_exc())
if isinstance(ex, urlfetch.DownloadError) or \
isinstance(ex, DeadlineExceededError) or \
isinstance(ex, CsrfException) or \
isinstance(ex, taskqueue.TransientError):
logging.warn(msg)
else:
logging.error(msg)
def set_cookie(self, name, value, expires=None):
"""Set a cookie"""
if value is None:
value = 'deleted'
expires = datetime.timedelta(minutes=-50000)
jar = Cookie.SimpleCookie()
jar[name] = value
jar[name]['path'] = u'/'
if expires:
if isinstance(expires, datetime.timedelta):
expires = datetime.datetime.now() + expires
if isinstance(expires, datetime.datetime):
expires = expires.strftime('%a, %d %b %Y %H:%M:%S')
jar[name]['expires'] = expires
self.response.headers.add_header(*jar.output().split(u': ', 1))
def render(self, name, **data):
"""Render a template"""
if not data:
data = {}
data[u'js_conf'] = json.dumps({
u'appId': conf.FACEBOOK_APP_ID,
u'canvasName': conf.FACEBOOK_CANVAS_NAME,
u'userIdOnServer': self.user.user_id if self.user else None,
})
data[u'logged_in_user'] = self.user
data[u'message'] = self.get_message()
data[u'csrf_token'] = self.csrf_token
data[u'canvas_name'] = conf.FACEBOOK_CANVAS_NAME
self.response.out.write(template.render(
os.path.join(
os.path.dirname(__file__), 'templates', name + '.html'),
data))
def init_facebook(self):
"""Sets up the request specific Facebook and User instance"""
facebook = Facebook()
user = None
# initial facebook request comes in as a POST with a signed_request
if u'signed_request' in self.request.POST:
facebook.load_signed_request(self.request.get('signed_request'))
# we reset the method to GET because a request from facebook with a
# signed_request uses POST for security reasons, despite it
# actually being a GET. in webapp causes loss of request.POST data.
self.request.method = u'GET'
self.set_cookie(
'u', facebook.user_cookie, datetime.timedelta(minutes=1440))
elif 'u' in self.request.cookies:
facebook.load_signed_request(self.request.cookies.get('u'))
# try to load or create a user object
if facebook.user_id:
user = User.get_by_key_name(facebook.user_id)
if user:
# update stored access_token
if facebook.access_token and \
facebook.access_token != user.access_token:
user.access_token = facebook.access_token
user.put()
# refresh data if we failed in doing so after a realtime ping
if user.dirty:
user.refresh_data()
# restore stored access_token if necessary
if not facebook.access_token:
facebook.access_token = user.access_token
if not user and facebook.access_token:
me = facebook.api(u'/me', {u'fields': _USER_FIELDS})
try:
friends = [user[u'id'] for user in me[u'friends'][u'data']]
user = User(key_name=facebook.user_id,
user_id=facebook.user_id, friends=friends,
access_token=facebook.access_token, name=me[u'name'],
email=me.get(u'email'), picture=me[u'picture'])
user.put()
except KeyError, ex:
pass # ignore if can't get the minimum fields
self.facebook = facebook
self.user = user
def init_csrf(self):
"""Issue and handle CSRF token as necessary"""
self.csrf_token = self.request.cookies.get(u'c')
if not self.csrf_token:
self.csrf_token = str(uuid4())[:8]
self.set_cookie('c', self.csrf_token)
if self.request.method == u'POST' and self.csrf_protect and \
self.csrf_token != self.request.POST.get(u'_csrf_token'):
raise CsrfException(u'Missing or invalid CSRF token.')
def set_message(self, **obj):
"""Simple message support"""
self.set_cookie('m', base64.b64encode(json.dumps(obj)) if obj else None)
def get_message(self):
"""Get and clear the current message"""
message = self.request.cookies.get(u'm')
if message:
self.set_message() # clear the current cookie
return json.loads(base64.b64decode(message))
def user_required(fn):
"""Decorator to ensure a user is present"""
#wraps(fn)
def wrapper(*args, **kwargs):
handler = args[0]
if handler.user:
return fn(*args, **kwargs)
handler.redirect(u'/')
return wrapper
class RecentRunsHandler(BaseHandler):
"""Show recent runs for the user and friends"""
def get(self):
if self.user:
friends = {}
for friend in select_random(
User.get_by_key_name(self.user.friends), 30):
friends[friend.user_id] = friend
self.render(u'runs',
friends=friends,
user_recent_runs=Run.find_by_user_ids(
[self.user.user_id], limit=5),
friends_runs=Run.find_by_user_ids(friends.keys()),
)
else:
self.render(u'welcome')
class UserRunsHandler(BaseHandler):
"""Show a specific user's runs, ensure friendship with the logged in user"""
#user_required
def get(self, user_id):
if self.user.friends.count(user_id) or self.user.user_id == user_id:
user = User.get_by_key_name(user_id)
if not user:
self.set_message(type=u'error',
content=u'That user does not use Run with Friends.')
self.redirect(u'/')
return
self.render(u'user',
user=user,
runs=Run.find_by_user_ids([user_id]),
)
else:
self.set_message(type=u'error',
content=u'You are not allowed to see that.')
self.redirect(u'/')
class RunHandler(BaseHandler):
"""Add a run"""
#user_required
def post(self):
try:
location = self.request.POST[u'location'].strip()
if not location:
raise RunException(u'Please specify a location.')
distance = float(self.request.POST[u'distance'].strip())
if distance < 0:
raise RunException(u'Invalid distance.')
date_year = int(self.request.POST[u'date_year'].strip())
date_month = int(self.request.POST[u'date_month'].strip())
date_day = int(self.request.POST[u'date_day'].strip())
if date_year < 0 or date_month < 0 or date_day < 0:
raise RunException(u'Invalid date.')
date = datetime.date(date_year, date_month, date_day)
run = Run(
user_id=self.user.user_id,
location=location,
distance=distance,
date=date,
)
run.put()
title = run.pretty_distance + u' miles #' + location
publish = u'<a onclick=\'publishRun(' + \
json.dumps(htmlescape(title)) + u')\'>Post to facebook.</a>'
self.set_message(type=u'success',
content=u'Added your run. ' + publish)
except RunException, e:
self.set_message(type=u'error', content=unicode(e))
except KeyError:
self.set_message(type=u'error',
content=u'Please specify location, distance & date.')
except ValueError:
self.set_message(type=u'error',
content=u'Please specify a valid distance & date.')
except Exception, e:
self.set_message(type=u'error',
content=u'Unknown error occured. (' + unicode(e) + u')')
self.redirect(u'/')
class RealtimeHandler(BaseHandler):
"""Handles Facebook Real-time API interactions"""
csrf_protect = False
def get(self):
if (self.request.GET.get(u'setup') == u'1' and
self.user and conf.ADMIN_USER_IDS.count(self.user.user_id)):
self.setup_subscription()
self.set_message(type=u'success',
content=u'Successfully setup Real-time subscription.')
elif (self.request.GET.get(u'hub.mode') == u'subscribe' and
self.request.GET.get(u'hub.verify_token') ==
conf.FACEBOOK_REALTIME_VERIFY_TOKEN):
self.response.out.write(self.request.GET.get(u'hub.challenge'))
logging.info(
u'Successful Real-time subscription confirmation ping.')
return
else:
self.set_message(type=u'error',
content=u'You are not allowed to do that.')
self.redirect(u'/')
def post(self):
body = self.request.body
if self.request.headers[u'X-Hub-Signature'] != (u'sha1=' + hmac.new(
self.facebook.app_secret,
msg=body,
digestmod=hashlib.sha1).hexdigest()):
logging.error(
u'Real-time signature check failed: ' + unicode(self.request))
return
data = json.loads(body)
if data[u'object'] == u'user':
for entry in data[u'entry']:
taskqueue.add(url=u'/task/refresh-user/' + entry[u'id'])
logging.info('Added task to queue to refresh user data.')
else:
logging.warn(u'Unhandled Real-time ping: ' + body)
def setup_subscription(self):
path = u'/' + conf.FACEBOOK_APP_ID + u'/subscriptions'
params = {
u'access_token': conf.FACEBOOK_APP_ID + u'|' +
conf.FACEBOOK_APP_SECRET,
u'object': u'user',
u'fields': _USER_FIELDS,
u'callback_url': conf.EXTERNAL_HREF + u'realtime',
u'verify_token': conf.FACEBOOK_REALTIME_VERIFY_TOKEN,
}
response = self.facebook.api(path, params, u'POST')
logging.info(u'Real-time setup API call response: ' + unicode(response))
class RefreshUserHandler(BaseHandler):
"""Used as an App Engine Task to refresh a single user's data if possible"""
csrf_protect = False
def post(self, user_id):
logging.info('Refreshing user data for ' + user_id)
user = User.get_by_key_name(user_id)
if not user:
return
try:
user.refresh_data()
except FacebookApiError:
user.dirty = True
user.put()
def main():
routes = [
(r'/', RecentRunsHandler),
(r'/user/(.*)', UserRunsHandler),
(r'/run', RunHandler),
(r'/realtime', RealtimeHandler),
(r'/task/refresh-user/(.*)', RefreshUserHandler),
]
application = webapp.WSGIApplication(routes,
debug=os.environ.get('SERVER_SOFTWARE', '').startswith('Dev'))
util.run_wsgi_app(application)
if __name__ == u'__main__':
main()

I think, and I'm still working with this example project myself, that if you modify this line AND strip out any "friend" references then it should work:
_USER_FIELDS = u'name,email,picture,friends'
becomes
_USER_FIELDS = u'name,email,picture'
It will be a lot of work to strip out all the "friend" references, but from what I can tell that's the only string that the app uses to request user information from the graph api.

Related

flask_login, login_manager.user_loader is not getting called

I am using flask_login, It worked in local but when i moved to different server it is did not work. On trying to debug i realized login_manager.user_loader is not getting on remote machine but the same worked in my local.
This is issue is specific to okta sso authentication on remote machine only. Post okta authentication, i am calling below to save data in session.
login_user(user)
Post this, i was expecting user_loaded to be called but that is not happening. Tried going through document but could not understand. Can you anyone help me as why user_loader is getting called in my local machine but not on remote machine.
init.py
import os
from flask import Flask
from flask_login import LoginManager
from config import app_config
login_manager = LoginManager()
def create_app(config_name):
app = Flask(__name__,
instance_path=os.path.join(os.path.abspath(os.curdir), 'instance'),
instance_relative_config=True)
app.config.from_object(app_config[config_name])
app.config.from_pyfile('config.py')
login_manager.init_app(app)
login_manager.login_message = 'You must be logged in to access this page'
login_manager.login_view = 'admin.login'
login_manager.login_message_category = 'info'
login_manager.session_protection = "strong"
#login_manager.refresh_view ='admin.login'
#login_manager.needs_refresh_message = (u"To protect your account, please reauthenticate to access this page.")
from .home import home as home_blueprint
app.register_blueprint(home_blueprint)
from .restapi import restapi as restapi_blueprint
app.register_blueprint(restapi_blueprint, url_prefix='/steps/api/v1')
return app
In my blueprint view, i am calling login_user to set current user. I have placed user_loader in one seperate file.
from flask import current_app
from flask_login import UserMixin
from app import login_manager, get_connection
from .encryption import encrypt_json, encrypt_password, decrypt_password # noqa
class User(UserMixin):
"""Custom User class."""
id = None
name = None
email = None
description = None
role = None
def __init__(self, id_, name, email, description, role):
print(name)
self.id = str(id_)
self.name = name
self.email = email
self.description = description
self.role = role
def __repr__(self):
return self.name
def claims(self):
"""Use this method to render all assigned claims on profile page."""
return {'name': self.name,
'email': self.email}.items()
#staticmethod
def get(user_id):
try:
connection = get_connection()
user_sql = "SELECT ID, USER_NAME, DESCRIPTION, EMAIL, ROLE FROM USER WHERE ID = {}"
cursor = connection.cursor()
cursor.execute(user_sql.format(user_id))
data = cursor.fetchone()
if data:
return User(id_=data[0], name=data[1], email=data[3], description=data[2], role=data[4])
else:
return None
except Exception as ex:
print(ex)
finally:
cursor.close()
connection.close()
# print(user_id)
# print(USERS_DB)
# return USERS_DB.get(user_id)
#staticmethod
def getByName(user_name):
try:
connection = get_connection()
user_sql = "SELECT ID, USER_NAME, DESCRIPTION, EMAIL, ROLE FROM USER WHERE USER_NAME = '{}' "
cursor = connection.cursor()
cursor.execute(user_sql.format(user_name))
data = cursor.fetchone()
if data:
return User(id_=data[0], name=data[1], email=data[3], description=data[2], role=data[4])
else:
return None
except Exception as ex:
print(ex)
finally:
cursor.close()
connection.close()
#staticmethod
def getByEmail(emailid):
try:
connection = get_connection()
user_sql = "SELECT ID, USER_NAME, DESCRIPTION, EMAIL, ROLE FROM USER WHERE EMAIL = '{}' "
cursor = connection.cursor(buffered=True)
cursor.execute(user_sql.format(emailid))
data = cursor.fetchone()
print(data)
if data:
return User(id_=data[0], name=data[1], email=data[3], description=data[2], role=data[4])
else:
return None
except Exception as ex:
print(ex)
finally:
cursor.close()
connection.close()
#staticmethod
def verify_password(username, password):
try:
# mysql_hook = MySqlHook(mysql_conn_id="STEPS", schema="STEPS")
# connection = mysql_hook.get_conn()
connection = get_connection()
cursor = connection.cursor()
encFlag, decryptedPassword = decrypt_password(password)
lognReqTuple = (username, password)
user_sql = "SELECT USER_NAME, PASSWORD from USER WHERE USER_NAME = '{}'"
cursor.execute(user_sql.format(username))
all_users = cursor.fetchall()
print(all_users)
for user in all_users:
encFlag, decryptedPassword = decrypt_password(user[1]) if user[1] else (False, password)
existingUserTuple = user[0], decryptedPassword
if existingUserTuple == lognReqTuple:
# logging.info('User {} authenticated.'.format(username))
return 0
# logging.info('Un Authorized access, user authentication failed.')
return 1
except Exception as ex:
# logging.error('Error in verifying login details :{}'.format(ex))
print(ex)
return 1
finally:
cursor.close()
connection.close()
#staticmethod
def create(user_id, name, email):
# USERS_DB[user_id] = User(user_id, name, email)
pass
#login_manager.user_loader
def user_loader(user_id):
print('user loader', type(user_id), user_id)
return User.get(user_id)
Problem is that, same setup is working in my local ubuntu environment but when i moved to production vm on centos, it works for one view where i am using local authentication but not in case of okta.
user_loader is how you obtain the User object. You give it an id and it gives you back the User. See the docs.
In your case you already have the User object so login_user(user) has no need to call the user_loader.
user_loader is likely called wherever you do something like user = ....
Not sure about the issue but i changed the flask app folder structure. changed the way i was using blueprints and issue got solved. All in all, had to change complete application structure.

Cloud Composer non interactive authentication

I've tried a lot before posting this question, I'm not against down votes, at least let me know WHY you are down voting.
I have built an Airflow plugin to fetch data from Cloud composer Airflow environment and accessing the cloud composer is working great from browsers as it needs the user to sign in before accessing any of the Airflow endpoints.
In my use case, I need to trigger the endpoints via code. Is there a way in which I can do this.
Below is the Airflow-Flask plugin being used
from datetime import datetime
import json
import os
import six
import time
from flask import Blueprint, request, Response
from sqlalchemy import or_
from airflow import settings
from airflow.exceptions import AirflowException, AirflowConfigException
from airflow.models import DagBag, DagRun
from airflow.utils.state import State
from airflow.utils.dates import date_range as utils_date_range
from airflow.www.app import csrf
airflow_api_blueprint = Blueprint('airflow_api', __name__, url_prefix='/api/v1')
class ApiInputException(Exception):
pass
class ApiResponse:
def __init__(self):
pass
STATUS_OK = 200
STATUS_BAD_REQUEST = 400
STATUS_UNAUTHORIZED = 401
STATUS_NOT_FOUND = 404
STATUS_SERVER_ERROR = 500
#staticmethod
def standard_response(status, payload):
json_data = json.dumps({
'response': payload
})
resp = Response(json_data, status=status, mimetype='application/json')
return resp
#staticmethod
def success(payload):
return ApiResponse.standard_response(ApiResponse.STATUS_OK, payload)
#staticmethod
def error(status, error):
return ApiResponse.standard_response(status, {
'error': error
})
#staticmethod
def bad_request(error):
return ApiResponse.error(ApiResponse.STATUS_BAD_REQUEST, error)
#staticmethod
def not_found(error='Resource not found'):
return ApiResponse.error(ApiResponse.STATUS_NOT_FOUND, error)
#staticmethod
def unauthorized(error='Not authorized to access this resource'):
return ApiResponse.error(ApiResponse.STATUS_UNAUTHORIZED, error)
#staticmethod
def server_error(error='An unexpected problem occurred'):
return ApiResponse.error(ApiResponse.STATUS_SERVER_ERROR, error)
#airflow_api_blueprint.before_request
def verify_authentication():
authorization = request.headers.get('authorization')
try:
api_auth_key = settings.conf.get('AIRFLOW_API_PLUGIN', 'AIRFLOW_API_AUTH')
except AirflowConfigException:
return
if authorization != api_auth_key:
return ApiResponse.unauthorized("You are not authorized to use this resource")
def format_dag_run(dag_run):
return {
'run_id': dag_run.run_id,
'dag_id': dag_run.dag_id,
'state': dag_run.get_state(),
'start_date': (None if not dag_run.start_date else str(dag_run.start_date)),
'end_date': (None if not dag_run.end_date else str(dag_run.end_date)),
'external_trigger': dag_run.external_trigger,
'execution_date': str(dag_run.execution_date)
}
def find_dag_runs(session, dag_id, dag_run_id, execution_date):
qry = session.query(DagRun)
qry = qry.filter(DagRun.dag_id == dag_id)
qry = qry.filter(or_(DagRun.run_id == dag_run_id, DagRun.execution_date == execution_date))
return qry.order_by(DagRun.execution_date).all()
#airflow_api_blueprint.route('/dags', methods=['GET'])
def dags_index():
dagbag = DagBag()
dags = []
for dag_id in dagbag.dags:
payload = {
'dag_id': dag_id,
'full_path': None,
'is_active': False,
'last_execution': None,
}
dag = dagbag.get_dag(dag_id)
if dag:
payload['full_path'] = dag.full_filepath
payload['is_active'] = (not dag.is_paused)
payload['last_execution'] = str(dag.latest_execution_date)
if request.args.get('dag_id') is not None:
if request.args.get('dag_id') not in payload['dag_id']:
continue
dags.append(payload)
return ApiResponse.success({'dags': dags})
#airflow_api_blueprint.route('/dag_runs', methods=['GET'])
def get_dag_runs():
dag_runs = []
session = settings.Session()
query = session.query(DagRun)
if request.args.get('state') is not None:
query = query.filter(DagRun.state == request.args.get('state'))
if request.args.get('external_trigger') is not None:
# query = query.filter(DagRun.external_trigger == (request.args.get('external_trigger') is True))
query = query.filter(DagRun.external_trigger == (request.args.get('external_trigger') in ['true', 'True']))
if request.args.get('prefix') is not None:
query = query.filter(DagRun.run_id.ilike('{}%'.format(request.args.get('prefix'))))
if request.args.get('dag_id') is not None:
query = query.filter(DagRun.dag_id.ilike('{}%'.format(request.args.get('dag_id'))))
runs = query.order_by(DagRun.execution_date).all()
for run in runs:
dag_runs.append(format_dag_run(run))
session.close()
return ApiResponse.success({'dag_runs': dag_runs})
#csrf.exempt
#airflow_api_blueprint.route('/dag_runs', methods=['POST'])
def create_dag_run():
# decode input
data = request.get_json(force=True)
# ensure there is a dag id
if 'dag_id' not in data or data['dag_id'] is None:
return ApiResponse.bad_request('Must specify the dag id to create dag runs for')
dag_id = data['dag_id']
limit = 500
partial = False
if 'limit' in data and data['limit'] is not None:
try:
limit = int(data['limit'])
if limit <= 0:
return ApiResponse.bad_request('Limit must be a number greater than 0')
if limit > 500:
return ApiResponse.bad_request('Limit cannot exceed 500')
except ValueError:
return ApiResponse.bad_request('Limit must be an integer')
if 'partial' in data and data['partial'] in ['true', 'True', True]:
partial = True
# ensure there is run data
start_date = datetime.now()
end_date = datetime.now()
if 'start_date' in data and data['start_date'] is not None:
try:
start_date = datetime.strptime(data['start_date'], '%Y-%m-%dT%H:%M:%S')
except ValueError:
error = '\'start_date\' has invalid format \'{}\', Ex format: YYYY-MM-DDThh:mm:ss'
return ApiResponse.bad_request(error.format(data['start_date']))
if 'end_date' in data and data['end_date'] is not None:
try:
end_date = datetime.strptime(data['end_date'], '%Y-%m-%dT%H:%M:%S')
except ValueError:
error = '\'end_date\' has invalid format \'{}\', Ex format: YYYY-MM-DDThh:mm:ss'
return ApiResponse.bad_request(error.format(data['end_date']))
# determine run_id prefix
prefix = 'manual_{}'.format(int(time.time()))
if 'prefix' in data and data['prefix'] is not None:
prefix = data['prefix']
if 'backfill' in prefix:
return ApiResponse.bad_request('Prefix cannot contain \'backfill\', Airflow will ignore dag runs using it')
# ensure prefix doesn't have an underscore appended
if prefix[:-1:] == "_":
prefix = prefix[:-1]
conf = None
if 'conf' in data and data['conf'] is not None:
if isinstance(data['conf'], six.string_types):
conf = data['conf']
else:
try:
conf = json.dumps(data['conf'])
except Exception:
return ApiResponse.bad_request('Could not encode specified conf JSON')
try:
session = settings.Session()
dagbag = DagBag('dags')
if dag_id not in dagbag.dags:
return ApiResponse.bad_request("Dag id {} not found".format(dag_id))
dag = dagbag.get_dag(dag_id)
# ensure run data has all required attributes and that everything is valid, returns transformed data
runs = utils_date_range(start_date=start_date, end_date=end_date, delta=dag._schedule_interval)
if len(runs) > limit and partial is False:
error = '{} dag runs would be created, which exceeds the limit of {}.' \
' Reduce start/end date to reduce the dag run count'
return ApiResponse.bad_request(error.format(len(runs), limit))
payloads = []
for exec_date in runs:
run_id = '{}_{}'.format(prefix, exec_date.isoformat())
if find_dag_runs(session, dag_id, run_id, exec_date):
continue
payloads.append({
'run_id': run_id,
'execution_date': exec_date,
'conf': conf
})
results = []
for index, run in enumerate(payloads):
if len(results) >= limit:
break
dag.create_dagrun(
run_id=run['run_id'],
execution_date=run['execution_date'],
state=State.RUNNING,
conf=conf,
external_trigger=True
)
results.append(run['run_id'])
session.close()
except ApiInputException as e:
return ApiResponse.bad_request(str(e))
except ValueError as e:
return ApiResponse.server_error(str(e))
except AirflowException as e:
return ApiResponse.server_error(str(e))
except Exception as e:
return ApiResponse.server_error(str(e))
return ApiResponse.success({'dag_run_ids': results})
#airflow_api_blueprint.route('/dag_runs/<dag_run_id>', methods=['GET'])
def get_dag_run(dag_run_id):
session = settings.Session()
runs = DagRun.find(run_id=dag_run_id, session=session)
if len(runs) == 0:
return ApiResponse.not_found('Dag run not found')
dag_run = runs[0]
session.close()
return ApiResponse.success({'dag_run': format_dag_run(dag_run)})
For programmatic auth to Composer's Airflow webserver, you will have to authenticate through IAP.
https://cloud.google.com/composer/docs/how-to/using/triggering-with-gcf contains a js example of this.
https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/iap/make_iap_request.py is a Python example, but note that it is missing a timeout on the final IAP request (https://github.com/GoogleCloudPlatform/python-docs-samples/issues/1812).

Vimeo 'Replace' API Endpoint Not Changing Thumbnail

I am using Vimeo's API for the users of my app to upload videos, or replace their existing video with a new one. I am using a Vimeo Client to help me make the calls in my Django Application. Uploading works without any issues, but when I try to replace an existing video with a new one, the thumbnail stays as the old video. If you play the video, it will play the new one, but the thumbnail never changes.
Model Method that Uploads/Replaces
def vimeo_upload(self):
media_file = self.video_file
if media_file and os.path.exists(media_file.path):
v = vimeo.VimeoClient(token=settings.VIMEO_ACCESS_TOKEN, key=settings.VIMEO_API_KEY,
secret=settings.VIMEO_API_SECRET)
if self.video_url is None:
try:
video_uri = v.upload(media_file.path)
except AssertionError as exc:
logging.error('Vimeo Error: %s Video: %s' % (exc, media_file.path))
else:
self.video_url = video_uri
else:
try:
v.replace(video_uri=self.video_url, filename=media_file.path)
except Exception as exc:
self.video_url = None
logging.error('Vimeo Replace Error: %s Video: %s' % (exc, media_file.path))
# set the video title, description, etc.
if self.video_url:
try:
# convert locale from en-us form to en
v.patch(self.video_url, data={'description': self.customer.full_name, })
except Exception as exc:
logging.error('Vimeo Patch Error: %s Video: %s' % (exc, media_file.path))
Vimeo Client Model, and UploadVideoMixin
class UploadVideoMixin(object):
"""Handle uploading a new video to the Vimeo API."""
UPLOAD_ENDPOINT = '/me/videos'
REPLACE_ENDPOINT = '{video_uri}/files'
def upload(self, filename, upgrade_to_1080=False):
"""Upload the named file to Vimeo."""
ticket = self.post(
self.UPLOAD_ENDPOINT,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def replace(self, video_uri, filename, upgrade_to_1080=False):
"""Replace the video at the given uri with the named source file."""
uri = self.REPLACE_ENDPOINT.format(video_uri=video_uri)
ticket = self.put(
uri,
data={'type': 'streaming',
'upgrade_to_1080': 'true' if upgrade_to_1080 else 'false'},
params={'fields': 'upload_link,complete_uri'})
return self._perform_upload(filename, ticket)
def _perform_upload(self, filename, ticket):
"""Take an upload ticket and perform the actual upload."""
if ticket.status_code != 201:
raise UploadTicketCreationFailure(ticket, "Failed to create an upload ticket")
ticket = ticket.json()
# Perform the actual upload.
target = ticket['upload_link']
last_byte = 0
# Try to get size of obj by path. If provided obj is not a file path
# find the size of file-like object.
try:
size = os.path.getsize(filename)
with io.open(filename, 'rb') as f:
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
except TypeError:
size = len(filename.read())
f = filename
while last_byte < size:
try:
self._make_pass(target, f, size, last_byte)
except requests.exceptions.Timeout:
# If there is a timeout here, we are okay with it, since
# we'll check and resume.
pass
last_byte = self._get_progress(target, size)
# Perform the finalization and get the location.
finalized_resp = self.delete(ticket['complete_uri'])
if finalized_resp.status_code != 201:
raise VideoCreationFailure(finalized_resp, "Failed to create the video")
return finalized_resp.headers.get('Location', None)
def _get_progress(self, upload_target, filesize):
"""Test the completeness of the upload."""
progress_response = self.put(
upload_target,
headers={'Content-Range': 'bytes */*'})
range_recv = progress_response.headers.get('Range', None)
_, last_byte = range_recv.split('-')
return int(last_byte)
def _make_pass(self, upload_target, f, size, last_byte):
"""Make a pass at uploading.
This particular function may do many things. If this is a large upload
it may terminate without having completed the upload. This can also
occur if there are network issues or any other interruptions. These
can be recovered from by checking with the server to see how much it
has and resuming the connection.
"""
response = self.put(
upload_target,
timeout=None,
headers={
'Content-Length': str(size),
'Content-Range': 'bytes: %d-%d/%d' % (last_byte, size, size)
}, data=f)
if response.status_code != 200:
raise VideoUploadFailure(response, "Unexpected status code on upload")
class VimeoClient(ClientCredentialsMixin, AuthorizationCodeMixin, UploadMixin):
"""Client handle for the Vimeo API."""
API_ROOT = "https://api.vimeo.com"
HTTP_METHODS = set(('head', 'get', 'post', 'put', 'patch', 'options', 'delete'))
ACCEPT_HEADER = "application/vnd.vimeo.*;version=3.2"
USER_AGENT = "pyvimeo 0.3.10; (http://developer.vimeo.com/api/docs)"
def __init__(self, token=None, key=None, secret=None, *args, **kwargs):
"""Prep the handle with the authentication information."""
self.token = token
self.app_info = (key, secret)
self._requests_methods = dict()
# Make sure we have enough info to be useful.
assert token is not None or (key is not None and secret is not None)
# Internally we back this with an auth mechanism for Requests.
#property
def token(self):
return self._token.token
#token.setter
def token(self, value):
self._token = _BearerToken(value) if value else None
def __getattr__(self, name):
"""This is where we get the function for the verb that was just
requested.
From here we can apply the authentication information we have.
"""
if name not in self.HTTP_METHODS:
raise AttributeError("%r is not an HTTP method" % name)
# Get the Requests based function to use to preserve their defaults.
request_func = getattr(requests, name, None)
if request_func is None:
raise AttributeError(
"%r could not be found in the backing lib" % name
)
#wraps(request_func)
def caller(url, jsonify=True, **kwargs):
"""Hand off the call to Requests."""
headers = kwargs.get('headers', dict())
headers['Accept'] = self.ACCEPT_HEADER
headers['User-Agent'] = self.USER_AGENT
if jsonify \
and 'data' in kwargs \
and isinstance(kwargs['data'], (dict, list)):
kwargs['data'] = json.dumps(kwargs['data'])
headers['Content-Type'] = 'application/json'
kwargs['timeout'] = kwargs.get('timeout', (1, 30))
kwargs['auth'] = kwargs.get('auth', self._token)
kwargs['headers'] = headers
if not url[:4] == "http":
url = self.API_ROOT + url
response = request_func(url, **kwargs)
if response.status_code == 429:
raise APIRateLimitExceededFailure(
response, 'Too many API requests'
)
return response
return caller
class _BearerToken(requests.auth.AuthBase):
"""Model the bearer token and apply it to the request."""
def __init__(self, token):
self.token = token
def __call__(self, request):
request.headers['Authorization'] = 'Bearer ' + self.token
return request

Django REST Framework: Per-user throttles

I have users that need really high throttles so they can use the system a lot. Is there an easy way to give them higher throttles than the rest of the users?
I've looked around but haven't found anything.
I figured out a way to do this by extending the UserRateThrottle and adding special users to my settings file.
This class just overrides the allow_request method, adding some special logic to see if usernames are listed in the OVERRIDE_THROTTLE_RATES variable:
class ExceptionalUserRateThrottle(UserRateThrottle):
def allow_request(self, request, view):
"""
Give special access to a few special accounts.
Mirrors code in super class with minor tweaks.
"""
if self.rate is None:
return True
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
self.history = self.cache.get(self.key, [])
self.now = self.timer()
# Adjust if user has special privileges.
override_rate = settings.REST_FRAMEWORK['OVERRIDE_THROTTLE_RATES'].get(
request.user.username,
None,
)
if override_rate is not None:
self.num_requests, self.duration = self.parse_rate(override_rate)
# Drop any requests from the history which have now passed the
# throttle duration
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
return self.throttle_success()
To use this, just set your DEFAULT_THROTTLE_CLASS to this class, then put some special users into OVERRIDE_THROTTLE_RATES like so:
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.AnonRateThrottle',
'cl.api.utils.ExceptionalUserRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'anon': '100/day',
'user': '1000/hour',
},
'OVERRIDE_THROTTLE_RATES': {
'scout': '10000/hour',
'scout_test': '10000/hour',
},
I have found the solution after customized Django REST Throttling,
Its Blocking particular user after 3 login attempts (Block user_id that presents in my application). Block IP address after 6 login attempts for anonymous user.
prevent.py :-
#!/usr/bin/python
from collections import Counter
from rest_framework.throttling import SimpleRateThrottle
from django.contrib.auth.models import User
class UserLoginRateThrottle(SimpleRateThrottle):
scope = 'loginAttempts'
def get_cache_key(self, request, view):
user = User.objects.filter(username=request.data.get('username'))
ident = user[0].pk if user else self.get_ident(request)
return self.cache_format % {
'scope': self.scope,
'ident': ident
}
def allow_request(self, request, view):
"""
Implement the check to see if the request should be throttled.
On success calls `throttle_success`.
On failure calls `throttle_failure`.
"""
if self.rate is None:
return True
self.key = self.get_cache_key(request, view)
if self.key is None:
return True
self.history = self.cache.get(self.key, [])
self.now = self.timer()
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()
if len(self.history) >= self.num_requests:
return self.throttle_failure()
if len(self.history) >= 3:
data = Counter(self.history)
for key, value in data.items():
if value == 2:
return self.throttle_failure()
return self.throttle_success(request)
def throttle_success(self, request):
"""
Inserts the current request's timestamp along with the key
into the cache.
"""
user = User.objects.filter(username=request.data.get('username'))
if user:
self.history.insert(0, user[0].id)
self.history.insert(0, self.now)
self.cache.set(self.key, self.history, self.duration)
return True
Views.py :-
from .prevent import UserLoginRateThrottle
....
....
....
class ObtainAuthToken(auth_views.ObtainAuthToken):
throttle_classes = (UserLoginRateThrottle,)/use this method here your login view
def post(self, request, *args, **kwargs):
....
....
Settings.py :-
Django-rest-framework
REST_FRAMEWORK = {
...
...
...
'DEFAULT_THROTTLE_CLASSES': (
'rest_framework.throttling.UserRateThrottle',
),
'DEFAULT_THROTTLE_RATES': {
'loginAttempts': '6/hr',
'user': '1000/min',
}
}
I know this a pretty old thread and the accepted answer was helpful for me as well. I wanted to show how you can have multiple user rate throttles in place, in this case additional ones for the root users
from rest_framework.settings import api_settings
from django.core.exceptions import ImproperlyConfigured
class RootRateThrottle(UserRateThrottle):
"""
Limits the rate of API calls that may be made by a given user.
The user id will be used as a unique cache key if the user is
authenticated. For anonymous requests, the IP address of the request will
be used.
"""
def get_cache_key(self, request, view):
if request.user.is_authenticated:
ident = request.user.pk
else:
ident = self.get_ident(request)
self.rate = self.get_rate(request)
logger.debug(
"Throttling rate for %s: %s", request.user, self.rate
)
self.num_requests, self.duration = self.parse_rate(self.rate)
return self.cache_format % {
'scope': self.scope,
'ident': ident
}
def get_rate(self, request=None):
"""
Determine the string representation of the allowed request rate.
"""
if not getattr(self, 'scope', None):
msg = ("You must set either `.scope` or `.rate` for '%s' throttle" %
self.__class__.__name__)
raise ImproperlyConfigured(msg)
if request and request.user.is_superuser:
throttle_rates = settings.REST_FRAMEWORK["ROOT_THROTTLE_RATES"]
else:
throttle_rates = api_settings.DEFAULT_THROTTLE_RATES
try:
return throttle_rates[self.scope]
except KeyError:
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg)
class ByMinuteRateThrottle(RootRateThrottle):
scope = 'minute'
class ByHourRateThrottle(RootRateThrottle):
scope = 'hour'
class ByDayRateThrottle(RootRateThrottle):
scope = 'day'
the settings part then looks like this
'DEFAULT_THROTTLE_CLASSES': [
'threedi_api.throttling.ByMinuteRateThrottle',
'threedi_api.throttling.ByHourRateThrottle',
'threedi_api.throttling.ByDayRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'minute': '100/min',
'hour': '1000/hour',
'day': '5000/day',
},
'ROOT_THROTTLE_RATES': {
'minute': '200/min',
'hour': '2000/hour',
'day': '10000/day',
},

rauth/flask: How to login via Twitter?

The provided example in rauth is using the PIN instead of the callback. I don't understand how this should work via web callback.
1) Minor problem:
According to twitter, if oauth_callback URL is passed in, then it should be used instead whatever entry is in the https://dev.twitter.com settings. However this doesn't seem to be true, if I dont set it to http://127.0.0.1:8080/twitter/authorized it would never get to that Url after a successful authorization.
app.add_url_rule('/twitter/login', view_func=views.twitter_login)
app.add_url_rule('/twitter/authorized', 'twitter_authorized', view_func=views.twitter_authorized)
def twitter_login():
request_token, request_token_secret = twitter.get_request_token()
redirect_uri = url_for('twitter_authorized', _external=True)
params = {'oauth_callback': redirect_uri, 'request_token':request_token}
return redirect(twitter.get_authorize_url(**params))
2) Major problem is here:
I can see the request.args has both ['oauth_token'] and ['oauth_verifier'].
But I don't understand how to use them to get the twitter session for obtaining user details such as picture and display name:
def twitter_authorized():
tw_session = twitter.get_auth_session(request_token ??? , request_token_secret ???)
resp = tw_session.get("account/verify_credentials.json", params={'format':'json'})
me = resp.json()
user = User.get_or_create(...)
if user:
login_user(user)
return redirect(url_for('index'))
If someone could shed some light on this, would be highly appreciated.
Here's a working Twitter sign-in examples using Flask based on the Facebook example:
'''
twitter
-------
A simple Flask demo app that shows how to login with Twitter via rauth.
Please note: you must do `from twitter import db; db.create_all()` from
the interpreter before running this example!
'''
from flask import (Flask, flash, request, redirect, render_template, session,
url_for)
from flask.ext.sqlalchemy import SQLAlchemy
from rauth.service import OAuth1Service
from rauth.utils import parse_utf8_qsl
# Flask config
SQLALCHEMY_DATABASE_URI = 'sqlite:///twitter.db'
SECRET_KEY = '\xfb\x12\xdf\xa1#i\xd6>V\xc0\xbb\x8fp\x16#Z\x0b\x81\xeb\x16'
DEBUG = True
TW_KEY = 'oZSbVzKCeyAZTDxw1RKog'
TW_SECRET = 'TuNoqA6NzEBS3Zrb8test7bxQfKTlBfLTXsZ8RaKAo'
# Flask setup
app = Flask(__name__)
app.config.from_object(__name__)
db = SQLAlchemy(app)
# rauth OAuth 1.0 service wrapper
twitter = OAuth1Service(
name='twitter',
consumer_key=TW_KEY,
consumer_secret=TW_SECRET,
request_token_url='https://api.twitter.com/oauth/request_token',
access_token_url='https://api.twitter.com/oauth/access_token',
authorize_url='https://api.twitter.com/oauth/authorize',
base_url='https://api.twitter.com/1.1/')
# models
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
fb_id = db.Column(db.String(120))
def __init__(self, username, fb_id):
self.username = username
self.fb_id = fb_id
def __repr__(self):
return '<User %r>' % self.username
#staticmethod
def get_or_create(username, fb_id):
user = User.query.filter_by(username=username).first()
if user is None:
user = User(username, fb_id)
db.session.add(user)
db.session.commit()
return user
# views
#app.route('/')
def index():
return render_template('login.html')
#app.route('/twitter/login')
def login():
oauth_callback = url_for('authorized', _external=True)
params = {'oauth_callback': oauth_callback}
r = twitter.get_raw_request_token(params=params)
data = parse_utf8_qsl(r.content)
session['twitter_oauth'] = (data['oauth_token'],
data['oauth_token_secret'])
return redirect(twitter.get_authorize_url(data['oauth_token'], **params))
#app.route('/twitter/authorized')
def authorized():
request_token, request_token_secret = session.pop('twitter_oauth')
# check to make sure the user authorized the request
if not 'oauth_token' in request.args:
flash('You did not authorize the request')
return redirect(url_for('index'))
try:
creds = {'request_token': request_token,
'request_token_secret': request_token_secret}
params = {'oauth_verifier': request.args['oauth_verifier']}
sess = twitter.get_auth_session(params=params, **creds)
except Exception, e:
flash('There was a problem logging into Twitter: ' + str(e))
return redirect(url_for('index'))
verify = sess.get('account/verify_credentials.json',
params={'format':'json'}).json()
User.get_or_create(verify['screen_name'], verify['id'])
flash('Logged in as ' + verify['name'])
return redirect(url_for('index'))
if __name__ == '__main__':
db.create_all()
app.run()
Hope that helps!