Token-based Authentication with Cornice for Pyramid - rest

I am using the Resources strategy of developing a RESTful API within a Pyramid App.
http://cornice.readthedocs.io/en/latest/resources.html. However I couldn't find an example of adding authentication for the API. Any guidance is greatly appreciated.

As Antoine Leclair pointed out, Cornice relies on Pyramid. You will have to enable an authorization and an authentication policies during your app initialization. For example (here using pyramid-jwt):
from pyramid.config import Configurator
from pyramid.authorization import ACLAuthorizationPolicy
def main():
config = Configurator()
# Pyramid requires an authorization policy to be active.
config.set_authorization_policy(ACLAuthorizationPolicy())
# Enable JWT authentication.
config.include('pyramid_jwt')
config.set_jwt_authentication_policy('secret')
You can also create your own policy, by inheriting from builtin Pyramid classes in pyramid.authentication:
from pyramid.authentication import CallbackAuthenticationPolicy
from pyramid.interfaces import IAuthenticationPolicy
from zope.interface import implementer
#implementer(IAuthenticationPolicy)
class MyAuthenticationPolicy(CallbackAuthenticationPolicy):
def __init__(self, realm='Realm'):
self.realm = realm
def unauthenticated_userid(self, request):
user_id = self._get_credentials(request)
return user_id
def forget(self, request):
return [('WWW-Authenticate', 'MyAuth realm="%s"' % self.realm)]
def _get_credentials(self, request):
authorization = request.headers.get('Authorization', '')
# your own strategy...
# if valid: return user_id
# else return None
Check out existing projects on awesome-pyramid to see if what you need is already there...

Related

Locust tasks that follow redirects

I'm load testing a local API that will redirect a user based on a few conditions. Locust is not redirecting the simulated users hitting the end points and I know this because the app logs all redirects. If I manually hit the end points using curl, I can see the status is 302 and the Location header is set.
According to the embedded clients.HttpSession.request object, the allow_redirects option is set to True by default.
Any ideas?
We use redirection in our locust test, especially during the login phase. The redirects are handled for us without a hitch. Print the status_code of the response that you get back. Is it 200, 3xx or something worse?
Another suggestion: Don't throw your entire testing workflow into the locust file. That makes it too difficult to debug problems. Instead, create a standalone python script that uses the python requests library directly to simulate your workflow. Iron out any kinks, like redirection problems, in that simple, non-locust test script. Once you have that working, extract what you did into a file or class and have the locust task use the class.
Here is an example of what I mean. FooApplication does the real work. He is consumed by the locust file and a simple test script.
foo_app.py
class FooApplication():
def __init__(self, client):
self.client = client
self.is_logged_in = False
def login(self):
self.client.cookies.clear()
self.is_logged_in = False
name = '/login'
response = self.client.post('/login', {
'user': 'testuser',
'password': '12345'
}, allow_redirects=True, name=name)
if not response.ok:
self.log_failure('Login failed', name, response)
def load_foo(self):
name = '/foo'
response = self.client.get('/foo', name=name)
if not response.ok:
self.log_failure('Foo request failed ', name, response)
def log_failure(self, message, name, response):
pass # add some logging
foo_test_client.py
# Use this test file to iron out kinks in your request workflow
import requests
from locust.clients import HttpSession
from foo_app import FooApplication
client = HttpSession('http://dev.foo.com')
app = FooApplication(client)
app.login()
app.load_foo()
locustfile.py
from foo_app import FooApplication
class FooTaskSet(TaskSet):
def on_start(self):
self.foo = FooApplication(self.client)
#task(1)
def login(self):
if not self.foo.is_logged_in:
self.foo.login()
#task(5) # 5x more likely to load a foo vs logging in again
def load_foo(self):
if self.foo.is_logged_in:
self.load_foo()
else:
self.login()
Since Locust uses the Requests HTTP library for Python, you might find your answer there.
The Response object can be used to evaluate if a redirect has happened and what the history of redirects contains.
is_redirect:
True if this Response is a well-formed HTTP redirect that could have been
processed automatically (by Session.resolve_redirects).
There might be an indication that the redirect is not well-formed.

TastyPie apply authentication only to PUT/POST

How do apply authentication & authorization to PUT and POST methods in TastyPie? I know we can define authentication and authorization in Meta inner class on the resource but how do i let these two methods ONLY to pass thru authentication and authorization layer. I interested in SessionAuthentication and DjangoAuthrization btw.
You can create class based on DjangoAuthrization (add it to Meta then) and override two functions:
def read_list(self, object_list, bundle):
return object_list
def read_detail(self, object_list, bundle):
return True

Restrict access to specific IP in Play Framework (Scala)

How can I restrict access to list of IP in Play Framework using Scala?
I'm using Play Framework 2.2.4
I found solution for Java:
http://feadro.com/simple-ip-access-list-for-play-2-1-with-java/
How should I do it in Scala?
Stick the IPs you want to restrict to in the application.conf.
myapp.ipwhitelist = ["192.168.1.1", ...]
Then make a global filter which is applied to every incoming request, something like:
import scala.collection.JavaConverters._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.iteratee.Iteratee
import play.api.Play.current
import play.api.mvc._
object IPFilter extends EssentialFilter {
def apply(nextFilter: EssentialAction) = new EssentialAction {
def apply(requestHeader: RequestHeader) = {
// read the IPs as a Scala Seq (converting from the Java list)
val ips: Seq[String] = current.configuration.getStringList("myapp.ipwhitelist")
.map(_.asScala).getOrElse(Seq.empty)
// Check we've got an allowed IP, otherwise ignore the
// request body and immediately return a forbidden.
if (ips.contains(requestHeader.remoteAddress)) nextFilter(requestHeader)
else Iteratee.ignore[Array[Byte]]
.map(_ => Results.Forbidden(s"Bad IP! ${requestHeader.remoteAddress}"))
}
}
}
Then enable that in your application Global object:
object Global extends WithFilters(IPFilter) with GlobalSettings
If you want more flexibility you can use the same logic but with Action composition instead of a global filter.
I think that for production HTTP environment requiring more than simple configuration (public, accepting any HTTP request), an HTTP frontend service should be used to dispatch appropriately request to Play.
As for Can I (DNS) map one subdomain to multiple Play Framework entry points , there is advantage of using a service such Apache, Nginx or Varnish to configure HTTP ACL (Access Control List). For example in Varnish:
acl my-acl {
"an-authorized-host";
"1.2.3.4";
}
# Then ...
if (!client.ip ~ my-acl) {
error 405 "Not allowed.";
}

List of Spreadsheets Gdata OAuth2

Getting a list of spreadsheets with spreadsheet api in Gdata,
Oauth1 Way
spreadSheetService = gdata.spreadsheet.service.SpreadsheetsService()
spreadSheetService.SetOAuthInputParameters(gdata.auth.OAuthSignatureMethod.HMAC_SHA1,self.CONSUMER_KEY,self.CONSUMER_SECRET,two_legged_oauth=True, requestor_id=self.requestor_id)
spreadSheetService.GetSpreadsheetsFeed(query = q)
But since spreadSheetService is not available for OAuth2 because of this won't fix issue #594
How do I query for a list of spreadsheets with gdata.spreadsheets.client.SpreadsheetClient ?
(assuming Python)
I was able to use gd_client.auth_token = gdata.gauth.OAuth2TokenFromCredentials(credentials) to take a credentials object created by an OAuth2 flow (using the oauth2client) and use this with the gdata library.
Full example here (for a command-line app):
# Do OAuth2 stuff to create credentials object
from oauth2client.file import Storage
from oauth2client.client import flow_from_clientsecrets
from oauth2client.tools import run
storage = Storage("creds.dat")
credentials = storage.get()
if credentials is None or credentials.invalid:
credentials = run(flow_from_clientsecrets("client_secrets.json", scope=["https://spreadsheets.google.com/feeds"]), storage)
# Use it within gdata
import gdata.spreadsheets.client
import gdata.gauth
gd_client = gdata.spreadsheets.client.SpreadsheetsClient()
gd_client.auth_token = gdata.gauth.OAuth2TokenFromCredentials(credentials)
print gd_client.get_spreadsheets()
If you're specifically looking for 2-legged, the same technique works, but you will need to create a different type of credentials object. See the following recent answer regarding how to create this: Using Spreadsheet API OAuth2 with Certificate Authentication
Here's a variation that writes an OAuth 2.0 Bearer auth header directly to the request and allows you to continue to use the older gdata.spreadsheet.service.SpreadsheetsService style client code:
import httplib2
# Do OAuth2 stuff to create credentials object
from oauth2client.file import Storage
from oauth2client.client import flow_from_clientsecrets
from oauth2client.tools import tools
storage = Storage("creds.dat")
credentials = storage.get()
if credentials is None or credentials.invalid:
flags = tools.argparser.parse_args(args=[])
flow = flow_from_clientsecrets("client_secrets.json", scope=["https://spreadsheets.google.com/feeds"])
credentials = tools.run_flow(flow, storage, flags)
if credentials.access_token_expired:
credentials.refresh(httplib2.Http())
# Use it within old gdata
import gdata.spreadsheet.service
import gdata.service
client = gdata.spreadsheet.service.SpreadsheetsService(
additional_headers={'Authorization' : 'Bearer %s' % credentials.access_token})
#public example
entry = client.GetSpreadsheetsFeed('0AoFkkLP2MB8kdFd4bEJ5VzR2RVdBQkVuSW91WE1zZkE')
print entry.title

building REST API in FLASK

A very basic question. I have a FLASK app which has postgresql behind it. There are no ORM for this application. All requests are done via SQL psycopg2 interface.
Now I want to expose certain API's from this application. What would be the best way to proceed.
1> Just like: http://flask-peewee.readthedocs.org/en/latest/rest-api.html
2> or can I do one without the ORM. It seems that ORM for an RESTful API is very useful but in this case I have to have a separate database elements and copy data from the postgres model to the ORM.
any suggestion would be welcome.
I have a similar setup Flask + Postgres and PsycoPG2.
I followed the following tutorials to design and implement the API
I handle errors manually and respond with the appropriate HTTP code
http://blog.luisrei.com/articles/rest.html { Design API}
http://blog.luisrei.com/articles/flaskrest.html { Implement API}
Looks like Flask-Restless is a better choice. Validations, authentication support are much simpler with it.
For non-trivial applications better use flask-classy. Flask-Restless is somewhat limiting, and flask-restful doesn't really give much over flask-classy, besides being more complex.
I personally used flask-restless for some time before moving to flask-classy.
There is bunch of different frameworks on top of flask now.
http://python-eve.org/index.html
http://www.flaskapi.org/
REST API in FLASK and Postman app:
Code:
from flask_sqlalchemy import SQLAlchemy
from flask import Flask,render_template,request,jsonify,json
import psycopg2
import psycopg2.extras
app = Flask(__name__)
db = SQLAlchemy()
conn = psycopg2.connect("postgresql://postgres:postgres#localhost:5432/country")
cr = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
#app.route('/', methods=['GET'])
def test():
return jsonify({
'message': 'Welcome'
})
#app.errorhandler(404)
def page_not_found(e):
return "<h1>404</h1><p>The resource could not be found.</p>", 404
##### Countries ######
#app.route('/country/all', methods=['GET'])
def country():
cr.execute('select * from country')
country = cr.fetchall()
countries = []
for row in country:
countries.append(dict(row))
return jsonify(countries)
if __name__ == '__main__':
app.run(debug=True, port=8080)
Result:
**