Multiple Authhentication Policies in Pyramid - python

I have received the task of adding HTTP authentication(BasicAuthAuthenticationPolicy) to a Pyramid application that already have a AuthTktAuthenticationPolicy implemented...
Basically I need to create a RESTful API to authenticate users(Could I use BasicAuthAuthenticationPolicy for that?).
Is there a way to check whether a user is using the web interface, or using the api - to check which Authentication Policy to use?
I have not come across documentation that covers two different Authentication Policies in a single Pyramid application(if its even possible).
PS:
I have come across a blog series that started showing how to create a RESTful API with the pyramid framework... The Blogger reported that there was going to be 6 articles in the sersies, however I only managed to find two of those articles: Building a RESTful API with Pyramid - Setup and Building a RESTful API with Pyramid - Resource and Traversal. I am/was looking forward to his last article: Building a RESTful API with Pyramid - Authentication and ACL, but it doesn't seem like he is going to finish the series.
To recap my questions:
Could I use BasicAuthAuthenticationPolicy for building a RESTful api to authenticate users?
Is there a way to check whether a user is using the web interface, or using the API - to check which Authentication Policy to use?
Any help would be Appreciated.

So what I did was I merged Pyramids BasicAuthAuthenticationPolicy and CallbackAuthenticationPolicy and I ended up with this.
I have modified the callback method to use a redis session
To use this class(HTTPAthentication) you can do something like(this is an example how I implemented it for my usecase):
def _get_userid(details, request):
userre = re.compile("""^([a-zA-Z1-9\.]+):([a-zA-Z1-9\.:-]+)$""", re.I)
userpass = base64.b64decode(details[1])
res = userre.search(userpass)
if res:
return res.group()[0]
def authcheck(username, password, request):
user = Users.by_username(username, enabled=True)
host = request.registry.settings.get("authentication.host", "127.0.0.1")
maxattempts = request.registry.settings.get("authentication.maxattempts",5)
base = _get_userid(request.authorization, request)
if request.redis.exists("pimssess:%s" % base64.b64encode(request.remote_addr+":"+base)):
store = pickle.loads(request.redis.get("pimssess:%s" % base64.b64encode(request.remote_addr+":"+base)))
if store.get("auth.attempts").get(request.remote_addr):
if store["auth.attempts"][request.remote_addr] >= maxattempts:
raise HTTPMethodNotAllowed(body="You have been locked out for 5 minutes")
if user and user.agent and not user.is_http_auth:
raise HTTPMethodNotAllowed(body="You are not permitted http access")
if user and user.agent and user.host != host:
raise HTTPMethodNotAllowed(body="Your host is not permitted http access")
if user and user.agent and not user.validate_password(password):
time.sleep(1.5)
raise HTTPForbidden(body="Failed login, Incorrect password")
return getGroups(username)
The getGroups function retruns a list of groups that is attached to the user , i.e. ['admin', 'reporting']
I followed this example: BasicAuthAuthenticationPolicy(scroll to bottom)
And for the web interface login(CallbackAuthentication), you create a login interface, and create the view to accommodate the template(checking for password and username match, etc.)
Oh I almost forgot... In your project __init__.py, when you call the AuthPolicy, in the def main(...). I did:
authentication = AuthPolicy(secret='##^&*$!DSYUIDSA8321789DS',
hashalg='sha512', check=authcheck, debug=True)
authorization = ACLAuthorizationPolicy()
config = Configurator(settings=settings, root_factory=RootFactory,
authentication_policy=authentication,
authorization_policy=authorization)
I do hope this can help someone.

Pyramid does not make it easy to use different policies for different parts of the app (maybe something to work around with custom decorators) but for multiple policies check out pyramid_authstack. I'm using it with Session and BasicAuth policies for the same purpose as you are.

If it's not straightforward having one pyramid app with two auth policies you can have TWO separate Pyramid apps with a different policy each assembled into a single WSGI stack. Each app can import the same Python code, so, essentially, it'll be two startup files using the same views and everything.
If your apps have different URLs you can use paste.urlmap for this, and if your requirements are more complex you can even write your own router (say, requests with a certain HTTP header are routed to one app and without it to another)

Related

How can I configure a Flask API to allow for token-protected user-specific responses?

If I have a server end point that say does a simple task like initializes an API with a token that I generate client side, and then print the users account info.
Can I initialize() the API globally so the user can do other tasks after printing account info?
and
How does that affect other users initializing() and printing info if they do it at the same time?
I don't understand how this server works and any help would be great. Thank you!
If I'm understanding you correctly, it sounds like the easiest way to accomplish what you're trying to do is to use the flask-login module.
You will want to create an endpoint / Flask route (for example, '/login') that the user will send a POST request to with a username (and usually also a password). When the login is successful, the user's browser will have a cookie set with a token that will allow them to access Flask routes that have a #login_required decorator attached.
In your Python code for those routes, you will be able to access the "current_user", which will allow you to tailor your response to the particular user sending the request.
This way of doing things will allow you to deal with your first question, which seems to be about saving a user's identity across requests, and it will also allow you to deal with your second question, which seems to be about the problem of having different users accessing the same endpoint while expecting different responses.
Nathan's answer points you in the right direction, but I'll make a comment about state and scope because you asked about multiple users.
Firstly, the normal way to do this would be for the server to generate the random token and send it back to the user.
client request 1 > init > create token > store along with init data > return token to user
client request 2 > something(token) > find data related to token > return data to user
When you run Flask (and most webapps) you try to make it so that each request is independent. In your case, the user calls an endpoint and identifies themselves by passing a token. If you make a record of that token somewhere (normally in a database) then you can identify that user's data by looking up the same token they pass on subsequent requests.
Where you choose to store that information is important. As I say a database is the normal recommended approach as it's built to be accessed safely by multiple people at the same time.
You might be tempted to not do the database thing and actually store the token / data information in a global variable inside python. Here's the reason why that's (probably) not going to work:
Flask is a wsgi server, and how it behaves when up and running depends on how it's configured. I generally use uwsgi with several different processes. Each process will have its own state that can't see one another. In this (standard / common) configuration, you don't know which process is going to receive any given request.
request 1 > init(token) > process 1 > store token in global vars
request 2 > other(token) > process 2 > can't find token in global vars
That's why we use a database to store all shared information:
request 1 > init(token) > process 1 > store token in db
request 2 > other(token) > process 2 > can find token db

How to allow access to App Engine from only specified IPs?

I am building a simple POST handler on GAE in Python that will accept a POST and write it to a Cloud SQL database.
I would like to limit access to this app to a limited number of IPs - non-GAE webservers where the POST originates. Essentially, how to allow POSTS from my IPs and disallow all other traffic?
Seems like a simple and common operation, but I haven't found a solution online that seems to fit. Most GAE authentication and routing packages are built around user auth.
Where should I look for a solution here? What Google keywords should I be using? Is this going to be written into the app itself or should I be focused on another component of GCP for IP access and routing?
Thanks!
All credit to Paul Collingwood for alerting me to the existence of request.remote_addr.
Here is my solution as of now:
ALLOWED_IP = ['173.47.xx.xx1', '173.47.xx.xx2']
class PostHandler(webapp2.RequestHandler):
def post(self):
# Read the IP of the incoming request
ip = self.request.remote_addr
# If the IP is allowed, execute our code
if ip in ALLOWED_IP:
# Execute some awesome code
# Otherwise, slam the door!
else:
self.error(403)
I'm not entirely sure that my self.error() usage is appropriate here, but this is working! POST requests made from the allowed IPs are accepted and executed. All others are given a 403.
I'm always eager to hear improvement suggestions.

SPA (Ember) authentication vs backend

I am trying to wrap my head around implementing proper authentication in an SPA with a backend.
I am looking at 2 different frontends:
1. SPA with Ember.js
2. Mobile application
I am trying to design a backend that serves both (either in Rails or in Python, have not decided yet). I want the authentication to take place either via Google or Facebook, i.e. I do not need to maintain separate user registrations but I still need to maintain users as I want to be able to 'merge' authentications from Google and Facebook at the end of the day. I also want the backend to be fully stateless for scalability reasons. Also I would prefer to do as much as possible on the client side to spare the load from the backend.
My questions are as follows:
I will request an authentication token in the SPA or in the mobile app. Should I convert this to an access token on the server side or on the client side? I need to authorize backend requests and I need to do that statelessly. My idea was to do everything on the frontend side and pass along the access token to each and every backend request, to be validated on the server side as well but I am not sure if this is an efficient approach.
I want to be able to merge Google and Facebook authentications, what are the best practices for that? I was planning to maintain an user registry on the server side and check the email addresses coming from the authorized backend requests and merge the users if there is a match in email addresses. What are the best practices and are there any libraries supporting this in Python/Flask or in Ruby or Rails?
Thanks!
I'm not really sure what do you mean by 'stateless'. You obviously need some database to store user's data (otherwise you don't need backend at all). So the database is your state. HTTP protocol is stateless by definition, so you can't really be very stateful by other means than storing data in DB.
I will request an authentication token in the SPA or in the mobile app. Should I convert this to an access token on the server side or on the client side?
If you don't need to use Google/Facebook on behalf of your users (and your wording suggests that you don't), you don't need to convert auth_token to server_token at all.
You just need to call Google/Facebook API with that (Ruby has libraries for both, so it's basically a one line of code) and get social network's user ID and user email.
Then you save that ID+email in your database and give your internal server token (just random string) to your user. You generate that string yourself and give it to the client.
If user logs in from another device (i.e. it gives you auth_token with which you find out that user's email belongs to one of already-registered users), you either return existing internal token, or generate new one and bind it to the existing user (depends on what you prioritize – high security of simplicity of implementation/maintenance).
I want to be able to merge Google and Facebook authentications, what are the best practices for that?
Facebook guarantees that if it gives you user email, then it's ensured that that email belongs to the given user. Google, obviously, does the same. So you just merge them by emails.
You don't need some special libraries for that, as it is simple operation with you code on the language of your choice.
I'd organize all the things in database in the following manner:
Users table
id
email
Authentications table
user_id
email
social_uid # facebook number or google email
social_network # string, 'facebook' or 'google'
device # user agent, e.g. 'android'
ip # last login IP address
token # internal token
When user logs in, Authentication object is created. If there is no user with such email, the user is created. If there is a user, it's bind to the authentication object (both via user_id field).
Notes on access tokens
If you do plan to interact with social network (in other ways than just authenticating user), you should exchange auth_token for server_token. server_token is a 'permanent' (well, kind of) authorization token for accessing APIs of social network, whereas auth_token has a very limited lifespan (and some API calls may be restricted if you didn't obtain server_token).
Still, server_token can expire (or user can recall their access for your application), so you should plan ahead to detect that situation and re-acquire token/authorization if needed.
Key points when building Rails app
In Rails, in order to create tables, you need to write migrations:
gem install rails
rails new my_project
cd my_project
rails generate migration create_users
rails generate migration create_authentications
This will generate project folder structure and two migration files, which you need to fill in:
# db/migrate/xxx_create_users.rb
def change
create_table :users do |t|
t.string :email
end
end
# db/migrate/xxx_create_authentications.rb
def change
create_table :authentications do |t|
t.integer :user_id
t.index :user_id
t.string :social_uid
# etc., all other fields
# ...
end
end
Then you generate 'models' to handle database-related manipulations:
# app/models/user.rb
class User < ActiveRecord::Base
has_many :authentications
end
# app/models/authentication.rb
class Authentication < ActiveRecord::Base
belongs_to :user
before_create :set_token
after_commit :create_user_if_needed
private
def set_token
self.token = SecureRandom.urlsafe_base64(20)
end
def create_user_if_needed
unless self.user.present?
self.user.create(email: self.email)
end
end
end
And write 'controller' to handle request from user with a single method inside of it:
# app/controllers/login_controller.rb
class LoginController < ActionController
# Login via Facebook method
def facebook
token = params.require(:auth_token)
# we will use Koala gem
profile = Koala::Facebook::API.new(token).get_object('me', fields: 'email')
# user is created automatically by model
authentication = Authentication.where(social_network: 'facebook', social_uid: profile['id'], email: profile['email']).first_or_create
authentication.update(...) # set ip and device
render json: {token: authentication.token}
end
# This one you'll have to write yourself as an exercise :)
def google
end
end
Of course, you will need to setup routes for your action:
# config/routes.rb
post "login/:action", controller: 'login'
And add Koala (or whatever else you'll use to manage external APIs for which good Ruby packages already exist) to Gemfile:
# Gemfile
gem 'koala'
Then in your terminal run:
bundle install
rails server
And your app is up and running. Well, you'll need to setup your Facebook and Google applications first, get developer keys, and authorize localhost to accept auth_tokens.
Basically, that's it.

Flask: share sessions between domain.com and username.domain.com

I have a flask running at domain.com
I also have another flask instance on another server running at username.domain.com
Normally user logs in through domain.com
However, for paying users they are suppose to login at username.domain.com
Using flask, how can I make sure that sessions are shared between domain.com and username.domain.com while ensuring that they will only have access to the specifically matching username.domain.com ?
I am concerned about security here.
EDIT:
Later, after reading your full question I noticed the original answer is not what you're looking for.
I've left the original at the bottom of this answer for Googlers, but the revised version is below.
Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.
To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:
SERVER 1:
Flask app for domain.com
SERVER 2:
Flask app for user profiles at username.domain.com
A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.
Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.
The short version for choosing between these two starkly different systems breaks down to the following:
Database
Databases are readily available
It's likely you already have a database implemented
Developers usually have a pre-existing knowledge of their chosen database
Memory (Redis/Memchachd/etc.)
Considerably faster
Systems often offer basic self-management of data
Doesn't incur extra load on existing database
You can find some examples database sessions in flask here and here.
While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.
The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).
Old solution for a single Flask app
Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.
SERVER_NAME = "example.com"
You can read more about this option here.
Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).
Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.
This is done pretty easily:
from flask import Blueprint
from flask.ext.login import current_user
# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")
# Define our route
#deep_blue.route('/')
def user_index(username):
if not current_user.is_authenticated():
# The user needs to log in
return "Please log in"
elif username != current_user.username:
# This is not the correct user.
return "Unauthorized"
# It's the right user!
return "Welcome back!"
The trick here is to make sure the __repr__ for your user object includes a username key. For eg...
class User(db.Model):
username = db.Column(db.String)
def __repr__(self):
return "<User {self.id}, username={self.username}>".format(self=self)
Something to note though is the problem that arises when a username contains special characters (a space, #, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.
If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.
You can do this with the builtin Flask sessions, which are cookie-based client-side sessions. To allow users to login to multiple subdomains in '.domain.com', you need only to specify
app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'
and the client's browser will have a session cookie that allows him to login to every Flask instance that is at 'domain.com'.
This only works if every instance of Flask has the same app.secret_key
For more information, also see
Same Flask login session across two applications

(Session) authentication in a pyramids web application

I am working on a python web application based on the pyramid framework. I am trying to add session authentication to it. By that I understand that:
users can log in/out (security is desirable); user data are kept in a database
authentication is handled via the session (request.session)
First off: Is session authentication a good option or are there better ones?
Secondly: I can't really make heads or tails of the documentation and examples.
So far, I've followed http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/tutorials/wiki2/authorization.html#adding-login-and-logout-views so far that I have a login/logout form. However, my authn_policy is a http://docs.pylonsproject.org/projects/pyramid/en/latest/api/authentication.html#pyramid.authentication.SessionAuthenticationPolicy
As the session factory in pyramid is insecure (see http://docs.pylonsproject.org/projects/pyramid/en/1.3-branch/narr/sessions.html), I use *pyramid_beaker* instead.
The configuration is:
in __init__.py: session_factory = session_factory_from_settings(settings)
in the .ini file:
beaker.session.lock_dir = %(here)s/data/sessions/lock
beaker.session.type = ext:database
beaker.session.sa.url = mysql://user:pass#localhost:3306/db
beaker.session.table_name = user_session
I hope I was able to make my problem clear.
I'd say it depends on what you want to do. Session authentication works fine if you use Beaker, but I like using AuthTktAuthenticationPolicy for the additional timeout and reissue options, and the fact that your authentication doesn't disappear even if you clear the session.

Categories

Resources