SAML / Shibb authentication in Django - python

I am newbie to Django, but I know how to create a simple application in python-Django how to add new page , how to link it into url file etc.
Now what I am trying to do, I am trying to create a very simple webapp where On the landing page I will have a login link.
When the user clicks on this link it should go to george washington universities authentication window and then I can enter my university's credential and it should authenticate and come back to a page stating ** Login Successful**
I have gone through many tutorials, but all looks very confusing.
I have installed xmlsec1, pysaml2, djangosaml2 modules but even after that I was clueless what to do next. I never felt so much clueless like I am feeling for this authentication module.
It will be great if anyone can guide me with the process.

You didn't say what web server you were using but, on Apache, I'd recommend you use the mod_shib Apache module in conjunction with the Django authentication middleware.
In broad strokes, you are going to let Apache/mod_shib do the SAML heavy lifting and interact with the IdP and you are going to let Django manage users for you. You are going to connect the two by using a piece of Django authentication middleware that authenticates users using the REMOTE_USER environment variable to communicate between Apache and the Django app.
So, first setup Django using Django authentication as described in the Django documentation. Validate that you can create a user using the admin tools and that you can login and establish a session using the Django authentication methods.
Once you have simple, local login working, install the RemoteUser middleware and validate that, by setting the REMOTE_USER environment variable, you can cause your Django app to log a user in (you can do all this testing using the development server locally on your development machine).
Once you have demonstrated that you can log a user in by having the REMOTE_USER environment set, you need to install the Apache shibboleth module, mod_shib and use it to protect the root of your application.
Assuming your application is located at /mysite the config in your virtualhost section would include:
<Location /mysite>
ShibRequestSetting redirectToSSL 443
AuthType shibboleth
ShibRequestSetting requireSession 1
Require valid-user
</Location>
That configuration will tell apache that the /mysite path requires mod_shib to get involved and forces the communication over ssl/tls.
I will not go through all the configuration steps needed to install and configure shibboleth but, basically, you want to set your application default (shibboleth2.xml file) with REMOTE_USER=eppn (if you want to use another attribute like eptid you would specify that); this tells the module which attribute to stuff in the REMOTE_USER environment variable. Again, the shib doc is pretty clear here so I won't go into detail about how to redirect to your university IdP but, basically, you will create an entry in your Sessions section of the form:
<SSO entityID="https://idp.testshib.org/idp/shibboleth">
Where you would substitute your IdP location for the testshib URL shown above.
Note that we are setting REMOTE_USER to the eppn value and that that value will be interpreted by the Django auth middleware as the user's username; you will need to create Django users with usernames that are the same as their eppn for this to work. You can also allow Django to auto-provision new accounts if, for instance, you deem IdP authentication sufficient evidence to create a new user account but, with auto-provisioning, only the minimal bits get setup; you would still need to go into that account and set first name, last name, phone, etc.
The net effect is that, whenever an unauthenticated user tries to visit a location under /mysite, they will be redirected to your university IdP, they will logon there and be redirected back. The mod_shib module (in conjunction with the shibd daemon running in the background) will handle the attribute unpacking and the session state with the IdP and will set the eppn value in the REMOTE _USER environment variable. Assuming that your Django application was setup correctly with apache, it will be invoked and the RemoteUser middleware will use the eppn value set in the REMOTE_USER environment variable to lookup the user with that username in the authentication database. If it finds a user, it will complete the Django login process (i.e. set the user object in the request, set the session state, etc.)
One more thing. To talk to you university IdP and have it release attributes to your application (i.e. eppn), you will need to do three things:
Import their IdP metadata
Export your SP metadata and have your university identity folks import it
Get your university identity team to release eppn to you
Just be aware that those three steps can be a challenge and may take non-trivial time and homework.
One more one more thing: I would recommend verifying the SAML setup separate from your Django app/middleware integration. Using the simplest mechanism you are comfortable with (simple wsgi app, php script, whatever) create a page that will simply dump the REMOTE_USER environment variable when browsed then protect that first. Once you have that page redirecting to your IdP and dumping the correct eppn in REMOTE_USER on return, then you can move on to the Django bits.

Related

How can I deploy zappa app to both https://www.my_domain.com and https://my_domain.com

I have a Flask app that I use Zappa to deploy. I have successfully deployed it to www.my_domain.com . But a lot of folks have told me "your site's down" and when I followed up it's because they go to https://my_domain.com (no www).
I'd like to redirect from that to the working site. My certificate is via AWS and technically for www.my_domain.com, *.my_domain.com, and my_domain.com (though when I did the email-based approval *.my_domain.com and my_domain.com both were interpreted as my_domain.com).
I've tried changing my zappa_settings.json domain but no change.
For purposes of this answer, I'm assuming that the bare domain is the secondary and the www is the primary. If you choose the opposite, simply reverse the terms while reading.
Redirect one site to the other
Redirection means that the browser can load my_domain.com but receives the
HTTP response of 301 moved permanently.
This means that the browser is redirected to www.my_domain.com and navigates to that site.
The user sees the address bar changed to be www.my_domain.com (although many browsers now hide
the www part of the address), and new page loads.
Redirection requires some type of HTTP server running at my_domain.com. If you already have
an HTTP server running, then it's pretty straightforward to configure the web server to send the redirect.
If you don't have a server, then you can use a static web site service like S3, Github, Netlify to
provide the redirect.
It is kind of annoying to have to set up an entire web server to just send a 301 code. But this method
is well understood and well supported.
Alias one site to the other
Alias means that both my_domain.com and www.my_domain.com work and use only one Zappa project.
It's like two doors that go into the same room.
The browser's address bar doesn't change and the user operates on the site normally. One caveat of
this approach is to be a little more careful if you are using cookies to make sure they are shared
between the two domains.
Aliasing requires some advanced configuration of AWS services.
API Gateway configuration
In the AWS Console, go to the API Gateway service
In the left hand menu, click on Custom domain names
Click on your domain (www.my_domain.com)
Click on API mappings over on the left and make note of the settings for API and Stage
Click on the Create button on the left
Enter the bare domain name (e.g. my_domain.com)
Select Edge optimized
Under ACM Certificate, select the one with the name of the domain you created
Click on Create domain name
Click on the newly created bare domain (my_domain.com)
Select API mappings
Click on Configure API Mappings
Add the same mappings you took note of earlier
Click on Save
Route 53 configuration
In the AWS Console, go to the Route 53 service
On the left menu, select Hosted Zones
Select your domain (e.g. my_domain.com)
Click Create Record
In Record Name, put in nothing
In Record Type, select A record (CNAME if you are doing the reverse)
In Value, type the www domain name (www.my_domain.com)
In TTL, you can put whatever you want but 172800 will cache requests for two days, which is not too bad.
Click Create records
ACM configuration
You'll have to ensure that the certificate covers both my_domain.com and www.my_domain.com.
Zappa configuration
Make sure to add both domain names in ALLOWED_HOSTS otherwise Django will reject the request.
I followed this thread.
Here's the steps that worked (though I'm not sure why):
Go to API Gateway
Create a new custom domain: my_domain.com
Use the records for previously-approved my_domain.com
In the new domain, under "API mappings" copy the settings from www.my_domain.com
back to Route 53
create an A record
leave the prefix blank
copy over the cloudfront url in "Route traffic to" from the new API Gateway domain

How do you make a Redirect URI for a Slack App?

I'm trying to create a Slack App (see here), but I'm having incredible difficulty with how to create a Redirect URI.
Slack states the following:
You must specify at least one redirect URL for OAuth to work. If you
pass a URL in an OAuth request, it must (at least partially) match one
of the URLs you enter here. Learn more
I have a rudimentary understanding of a Redirect URI conceptually, but I have no idea how to go about actually getting this Redirect URI that Slack requires.
I've successfully used all of Slacks Integrations with Python including Real Time Messaging, but setting up a Redirect URI seems to require a special server or a website.
As already mentioned in the comments you will need a publicly reachable webserver to host your script for installing the Slack app. So the redirect URL is the URL to your installation script.
Basically any webserver or script hosting service that runs your favorite script flavor (e.g. PHP or Python) will work. See also this answer on how the OAUTH process can be implemented.
The redirect URL works without SSL, but for security reasons SSL is strongly recommended. Also many other features of Slack requires you to run SSL on your webserver (e.g. Interactive Buttons)
Another option is to run a webserver on your local machine (e.g. WAMP for windows) and open it to the Internet through a secure tunnel (e.g. ngrok). For developing and testing this is actually the better alternative, since you can test and fix your Slack app locally without having to deploy every change on a public server.
However for running a public Slack app (e.g. one that is listed on the Slack App Directory) I would strongly recommend to put the production version of your App on a public webserver.
If you're just trying to get it up so that you can authorize another workspace you can always use 'http://localhost' after authorizing it will try to redirect you there and you wont be able to see anything useful, but the authorization should still have taken place I believe.
of course if you're looking for the api code, you will have to pull it directly from the browser url. ... it's very manual.

How to wrap Plone authentication around a third-party servlet?

We're using Plone to serve up some third-party middle-ware.
Unfortunately the middle-ware has a particular servlet that gets invoked from a Java applet and doesn't do any kind of authentication. I would like to firewall this off and somehow wrap authentication around it, preferably using the existing session that users will have on Plone.
My first idea was to configure nginx (which we're using as the reverse proxy) to check the cookie and only proxy if the user has a valid session (along the lines of this example). However, how to check the session ID against Plone, since it's all stored in the Zope database?
Alternatively we could have a Plone python script that basically passes everything along to the back-end after authenticating, but I'm not sure how to do that.
Any suggestions? Or alternative ideas?
Plone uses a auth_tkt session cookie; any system that supports such session cookies and that is configured with the same secret as Plone can validate the cookies.
Unfortunately, I haven't been able to locate a nginx implementation of auth_tkt; the standard originates with the Apache mod_auth_tkt module, so if Apache is an option you could use that. See Plone & CGI single-sign-on using mod_auth_tkt for details on how to configure such a setup.
You can write your own Plone custom add-on which provides a browser view doing the session check for you inside Plone process. How to involve this from Nginx is a mystery for me.
http://collective-docs.readthedocs.org/en/latest/views/browserviews.html
Checking if the authentication credentials are valid
http://collective-docs.readthedocs.org/en/latest/sessions/login.html?highlight=login#extracting-credentials
More about Plone authentication and cookies
http://collective-docs.readthedocs.org/en/latest/sessions/cookies.html?highlight=cookies#default-plone-cookies

WSGI/Django: pass username back to Apache for access log

My Django app, deployed in mod_wsgi under Apache using Django's standard WSGIHandler, authenticates users via form login on the Django side. So to Apache, the user is anonymous. This makes the Apache access log less useful.
Is there a way to pass the username back through the WSGI wrapper to Apache after handling the request, so that it appears in the Apache access log?
(Versions: Django 1.1.1, mod_wsgi 2.5, Apache 2.2.9)
You can only do it if using embedded mode and only if you use a separate package called apswigpy, which provides a Python binding for original Apache request object. The mod_wsgi package provides an optional mechanism for allowing original Apache request object to be passed as Python CObject reference in WSGI environment. You use that in conjunction with apswigpy something like:
from apache.httpd import request_rec
r = request_rec(environ['apache.request_rec'])
r.user = user
At least I think that will setup the appropriate information which access logging can then use.
You should really take this discussion over to the mod_wsgi mailing list.
You could use mod_auth_tkt. An auth_tkt is a signed cookie with the user id that Apache can understand. Your web application would have to set the cookie when the user logs in and out. Apache can derive a REMOTE_USER from the cookie, pass it to your web app or a non-Django web application running on the same server, include it in logs, whatever.
This probably isn't what you're expecting, but you could use the username in your URL scheme. That way the user will be in the path section of your apache logs.
You'd need to modify your authentication so that auth-required responses are obvious in the apache logs, otherwise when viewing the logs you may attribute unauthenticated requests to authenticated users. E.g. return a temporary redirect to the login page if the request isn't authenticated.
Correct me if I'm wrong, but what's stopping you from creating some custom middleware that sets a cookie equal to the display name of the current user logged in. This middleware will run on every view, so even though technically the user could spoof his username to display whatever he wants it to display, it'll just be reset anyway and it's not like its a security risk because the username itself is just for log purposes, not at all related to the actual user logged in. This seems like a simple enough solution, and then Apache log can access cookies so that gives you easiest access. I know some people wouldn't like the idea of a given user spoofing his own username, but i think this is the most trivial solution that gets the job done. Especially, in my case, when it's an iPhone app and the user doesn't have any direct access to a javascript console or the cookies itself.
for latest (Django 2.x, Apache 2.4) Tested
source https://www.django-rest-framework.org/api-guide/authentication/#apache-mod_wsgi-specific-configuration
you need to add WSGIPassAuthorization on in either server config, virtual host, directory or .htaccess

Control access to WebDav/Apache using Python

I want to give users access to WebDav using Apache, but I want to autenticate them first and give each user access to a specific folder. All authentication must be done against a Django-based database. I can get the Django-authentication working myself, but I need help with the part where I authenticate each user and provide them with a dedicated webdav user-specific area.
Any hints?
First, for you other readers, my authentication was done against Django using a WSGI authentication script.
Then, there's the meat of the question, giving each Django user, in this case, their own WebDav dir separated from other users. Assuming the following WebDAV setup in the Apache virtual sites configuration (customarily in /etc/apache2/sites-enabled/)
<Directory /webdav/root/on/server>
DAV On
# No .htaccess allowed
AllowOverride None
Options Indexes
AuthType Basic
AuthName "Login to your webdav area"
Require valid-user
AuthBasicProvider wsgi
WSGIAuthUserScript /where/is/the/authentication-script.wsgi
</Directory>
Note how there's no public address for WebDav set up yet. This, and the user area thing, is fixed in two lines in the same config file (put these after the ending clause):
RewriteEngine On
RewriteRule ^/webdav-url/(.*?)$ /webdav/root/on/server/%{LA-U:REMOTE_USER}/$1
Now, webdav is accessed on http://my-server.com/webdav-url/ The user gets a login prompt and will then land in a subdirectory to the webdav root, having the same name as their username. LA-U: makes Apache "look ahead" and let the user sign in before determining the mounting path, which is crucial since that path depends on the user name. Without some rewrite-rule there will be no URL, and the user won't get a login prompt. In other words, LA-U avoids a catch-22 for this type of login handling.
Precautions: requires mod_rewrite to be enabled, and user names must be valid as dir names without any modification. Also, the user dirs won't be created automatically by these commands, so their existence must be assured in some other way.
You might find that the apache mod_authn_dbd module gives you what you want. This module lets apache check an SQL database for authentication and authorization. You would use this directive in the <Location>, <Directory> (etc) area that you are trying to protect:
<Directory /usr/www/myhost/private>
# other config ere
# mod_authn_dbd SQL query to authenticate a user
AuthDBDUserPWQuery \
"SELECT password FROM authn WHERE user = %s"
</Directory>
Strictly speaking, this means you're authenticating against Django's database, not against the Django app itself. Note that you have full control over the query, so you CAN combine it with other parameters in any tables to make sure the user is in good standing, or in certain groups, or whatever, before allowing the authentication.
You may need to fuss around a bit to make sure the hashing mechanisms used are the same in both apache and django.
If this doesn't suit, consider moving your authentication out of the django database into, say, an LDAP server. With a custom authentication backend (there are existing LDAP implementations for django out there), django will happily use LDAP... and LDAP auth/auth support in Apache is quite robust.
I know this question is old, but just as an addition... If you are using mod_python, you may also be interested in "Authenticating against Django’s user database from Apache" section of Django documentation.

Categories