The key thing to understand when using the Shibboleth SP is that you are 
protecting a virtual folder. When you land on that folder, which can also 
be a web2py route, the SP checks to see if you are authenticated and if not 
sends you to the IDP. Only once you are authenticated does the end point 
load. Once authenticated, exposed in the header is identity information 
about the user. It is sort of like Active Directory where once 
authenticated with AD, the header contains the users login id. The 
difference is the identify information in the header is defined by what 
identity attributes the IDP wants to release to the SP and can contain any 
user identity information including login id, employee  number, first name, 
last name, email, department, shoe size... really whatever identity 
information the IDP provides and which it wants to release to the SP. 

In the example below, I am getting a variety of header fields that I know 
the SP is getting. An API call is made to another function that looks the 
user up in an external system and if found redirects the user into that 
system with signed url. If not found, the identity information is used to 
add the person on the fly also to that external system which then lands the 
user into their home page. Either way, our Web2Py app is used very narrowly 
to handle the results of authentication pulling the needed information to 
route the user into this external application either as an existing user or 
as a new user. Truth be told, any technology that can read the browser 
header can be used, but in Web2Py it is ridiculously easy.  

To SAMLize your web2py app go 
to https://wiki.shibboleth.net/confluence/display/SHIB2/Installation and 
download and install the SP to the system of your choice. 

One last thing, testshib.org is no more, and they talk about docker 
containers to download working environments; however, it looks like this 
site is able to do the same thing: https://samltest.id/

I am not sure how much the code snippet will help but here you go. Happy 
New Year All! 

'''
Shibboleth Login Functionality
'''

from gluon.storage import Storage
import datetime, requests, json, traceback

def start():
        
# Load Shibboleth Attributes
data = request.env
ISU = Storage()
        
# ISU.university_id = data['HTTP_ISUUNIVERSITYID']
ISU.university_id = data['HTTP_SHIBISUUNIVERSITYID']
ISU.email = data['HTTP_SHIBISUOFFICIALEMAIL']
ISU.name = data['HTTP_SHIBSN']
ISU.firstname = data['HTTP_SHIBGIVENNAME']
ISU.shib_id = data['HTTP_SHIBISULOGONID']
#ISU.university_id = 'foobar'

if (ISU.university_id == '' or ISU.university_id == None) :
    ISU.university_id = ''
# Check if we should view debug
if lweb.enable_debug == True:
response.view = 'login/start.html'
else:
response.view = 'login/redirect.html'
error = None
# Check if we have the university id
if (ISU.university_id == '' or ISU.university_id == None) and (ISU.shib_id 
== '' or  ISU.shib_id == None) :
error = 'No Darn University Id found'
return locals()
# Load the client state from ISU
lwebInfo = None
try:
lwebInfo = Storage(getState(ISU.university_id))
except:
error = 'Error loading Remote State from AbilityLMS:<br />%s' % 
traceback.format_exc().replace("\n", '<br />')
return locals()
# Check for errors from LWEB api
if lwebInfo != None:
if lwebInfo.hasError == True:
error = 'Error from AbilityLMS:<br />%s' % lwebInfo.stateObj['debug']
return locals()
elif lwebInfo.stateObj['Result'] == 'INVALIDDATE':
error = 'Invalid Date sent to AbilityLMS API: %s' % 
lwebInfo.stateObj['URL'].split('!~Redhead!~')[1]
return locals()
elif lwebInfo.stateObj['Result'] == 'INVALIDLEARNER':
xLearner_LoginID = 'Learner_LoginID=' + ISU.university_id
xLearner_EmailAddress = '&Learner_EmailAddress=' + ISU.email
xLearner_FirstName = '&Learner_FirstName=' + ISU.firstname
xLearner_LastName = '&Learner_LastName=' + ISU.name
xLearner_ShibLoginID = '&Shib_LoginID=' + ISU.shib_id
xLoginURL = lweb.AbilityLMS_URL + 
'/Programs/Custom/Control/ISU_Register.wml?' + xLearner_LoginID + 
xLearner_EmailAddress + xLearner_LastName + xLearner_ShibLoginID + 
xLearner_FirstName
redirect(xLoginURL + '&remoteST=%s' % lwebInfo.stateObj['ClientState'])

elif lwebInfo.stateObj['Result'] == 'SUCCESS':
redirect(lweb.AbilityLMS_URL + lweb.landing_path + '?remoteST=%s' % 
lwebInfo.stateObj['ClientState'])
else:
error = 'Unkown Error:<br />%s' % BEAUTIFY(lwebInfo)
return locals()
return locals()



On Saturday, December 8, 2018 at 10:37:13 AM UTC-5, Pbop wrote:
>
> Greetings Fellow Web2Pyers,
>
> It's the season of giving. I hope what I share inspires others to share 
> some of their tips and tricks in Web2Py that others can use! Many thanks to 
> the community for your great help in the past! 
>
> This post assumes the reader has limited exposure to SAML2. The solution 
> here allows any Web2Py app hosted on servers you control to work with any 
> SAML2 IDP (in theory). 
>
> SAML2 is a mark-up language to support federated single-sign services 
> which include Microsoft ADFS and AZURE, Shibboleth, OKTA to name a few. 
> What's cool with federated SSO is your web2py app can support SSO with any 
> of these services with only registration information needing to be shared. 
> Conversely, when you authenticate into any of these providers, you gain 
> access to any other application with the same credentials also registered 
> into the provider. 
>
> For those not familiar with how SAML2 works, there are two pieces of 
> technology needed: an IDP (identity provider) and the SP (service 
> provider).  Microsoft refers to the IDP as Claims Provider and SP as 
> Relaying Party. The IDP is where the user authenticates and contains 
> identity information about the user and what applications the user is 
> allowed to access. The SP is the application (your Web2Py app)  that wants 
> to use the IDP for sign-on services. When the user lands to the SP and is 
> not authenticated, the user is redirected to the IDP. The IDP will present 
> the user with a login form which means the user is authenticating into the 
> IDP, not directly into your application. On submission of credentials, the 
> IDP completes the sign-on process and redirects the authenticated and 
> authorized user back to the SP (your application). When redirecting the 
> user back to the SP, the post back includes whatever identity information 
> the IDP is authorized to release to the SP such as first and last name, 
> email, organization... This is different than CAS or AD which returns only 
> a login name or unique identifier. The IDP can release any information it 
> has about the user which means your app gains access to both authentication 
> and identity management services.  
>
> While there are a number of python based SAML2 implementations including a 
> 5 year old web2py version, it gets fairly deep into details that can be 
> entirely avoided with what I am about to share. 
>
> Shibboleth is a SAML2 implementation you can use to make your Web2Py app 
> SAML2 ready immediately. Shibboleth is used mostly in higher education and 
> includes both IDP and SP software installations. Both installations are 
> open source downloads that you can install to a web server (Unix and IIS), 
> but we're only interested in the service provider installation. The service 
> provider when installed to your server can protect a folder, including a 
> web2py application/controller/function folder. By protecting the folder 
> your web2py app is running against, you instantly gain SAML2 capability and 
> out of the box support to any SAML2 IDP. This is because all of the 
> identity attributes are now available in value pairs in the header (the 
> web2py request.env object) once the user is authenticated. 
>
> To show how easy this is, let's say the folder we want the Shibboleth 
> service provider to protect is welcome\secure where secure is your 
> controller in the welcome app using a default function. When the user lands 
> to that folder, the Shibboleth SP kicks in and redirects the user to the 
> IDP. Your web2py app will not even respond until Shibboleth has 
> authenticated you. The user logs on at the IDP, the IDP determines the user 
> is authenticated and then redirects back to the protected folder. Since 
> Shibboleth has determined you are now authorized to use the folder, your 
> web2py app fires and all of the identity attributes are now available for 
> your web2py application in the request.env object to use as you need. 
>
> Here is a set of headers from a Shibboleth authentication... This 
> represents what the IDP is releasing back to the SP and in turn represent 
> header variables available to your web2py app.  
>
> http_cn : Joe Shmoe
> http_officialemail : jsh...@schmoeland.com
> http_uclalabasuuid : 202e96f3-919f-479e-80e1-9a03f2416b9d
> http_uid : f0007939
>
> For your web2Py app to use this data it is simple variable assignments...
>
> # Load Shibboleth Attributes
> data = request.env
> ucla = Storage()   
> ucla.university_id = data['http_uid']
> ucla.email = data['http_officialemail']
>         ...
>  
> # Onward... 
>
> What identity attributes (header variables) are returned are a function of 
> the Shibboleth SP configuration with whatever IDP you are using and what 
> your app needs. Shibboleth handles producing the metadata the IDP needs, 
> login and logout services, offers comprehensive logging and has an active 
> community. To make your app SAML2 ready, you register the path in the 
> Shibboleth configuration file. You can get started with this approach at 
> www.testshib.org. 
>
> Pay it forward! 
>
>
>

-- 
Resources:
- http://web2py.com
- http://web2py.com/book (Documentation)
- http://github.com/web2py/web2py (Source code)
- https://code.google.com/p/web2py/issues/list (Report Issues)
--- 
You received this message because you are subscribed to the Google Groups 
"web2py-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to web2py+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to