Skip to content

Commit 6df62e5

Browse files
committed
Merge branch 'dzanin-master'
2 parents 2948c17 + 4d28828 commit 6df62e5

File tree

11 files changed

+476
-1
lines changed

11 files changed

+476
-1
lines changed

README.md

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,11 @@ This folder contains a Flask project that will be used as demo to show how to ad
174174

175175
This folder contains a Pyramid project that will be used as demo to show how to add SAML support to the [Pyramid Web Framework](http://docs.pylonsproject.org/projects/pyramid/en/latest/). ``\_\_init__.py`` is the main file that configures the app and its routes, ``views.py`` is where all the logic and SAML handling takes place, and the templates are stored in the ``templates`` folder. The ``saml`` folder is the same as in the other two demos.
176176

177+
#### demo-tornado ####
178+
179+
This folder contains a Tornado project that will be used as demo to show how to add SAML support to the Tornado Framework. ``views.py`` (with its ``settings.py``) is the main Flask file that has all the code, this file uses the templates stored at the ``templates`` folder. In the ``saml`` folder we found the ``certs`` folder to store the X.509 public and private key, and the SAML toolkit settings (``settings.json`` and ``advanced_settings.json``).
180+
181+
It requires python3.5 (it's using tornado 6.0.3)
177182

178183
#### setup.py ####
179184

@@ -1097,7 +1102,7 @@ For more info, look at the source code. Each method is documented and details ab
10971102
Demos included in the toolkit
10981103
-----------------------------
10991104

1100-
The toolkit includes 2 demos to teach how use the toolkit (A Django and a Flask project), take a look on it.
1105+
The toolkit includes 3 demos to teach how use the toolkit (A Django, Flask and a Tornado project), take a look on it.
11011106
Demos require that SP and IdP are well configured before test it, so edit the settings files.
11021107

11031108
Notice that each python framework has it own way to handle routes/urls and process request, so focus on
@@ -1177,6 +1182,79 @@ First we need to edit the ``saml/settings.json`` file, configure the SP part and
11771182

11781183
Once the SP is configured, the metadata of the SP is published at the ``/metadata`` url. Based on that info, configure the IdP.
11791184

1185+
#### How it works ####
1186+
1187+
1. First time you access to the main view (http://localhost:8000), you can select to login and return to the same view or login and be redirected to ``/?attrs`` (attrs view).
1188+
1189+
2. When you click:
1190+
1191+
2.1 in the first link, we access to ``/?sso`` (index view). An ``AuthNRequest`` is sent to the IdP, we authenticate at the IdP and then a Response is sent through the user's client to the SP, specifically the Assertion Consumer Service view: ``/?acs``. Notice that a ``RelayState`` parameter is set to the url that initiated the process, the index view.
1192+
1193+
2.2 in the second link we access to ``/?attrs`` (attrs view), we will expetience have the same process described at 2.1 with the diference that as ``RelayState`` is set the ``attrs`` url.
1194+
1195+
3. The SAML Response is processed in the ACS ``/?acs``, if the Response is not valid, the process stops here and a message is shown. Otherwise we are redirected to the ``RelayState`` view. a) / or b) ``/?attrs``
1196+
1197+
4. We are logged in the app and the user attributes are showed. At this point, we can test the single log out functionality.
1198+
1199+
The single log out functionality could be tested by 2 ways.
1200+
1201+
5.1 SLO Initiated by SP. Click on the ``logout`` link at the SP, after that a Logout Request is sent to the IdP, the session at the IdP is closed and replies through the client to the SP with a Logout Response (sent to the Single Logout Service endpoint). The SLS endpoint ``/?sls`` of the SP process the Logout Response and if is valid, close the user session of the local app. Notice that the SLO Workflow starts and ends at the SP.
1202+
1203+
5.2 SLO Initiated by IdP. In this case, the action takes place on the IdP side, the logout process is initiated at the IdP, sends a Logout Request to the SP (SLS endpoint, ``/?sls``). The SLS endpoint of the SP process the Logout Request and if is valid, close the session of the user at the local app and send a Logout Response to the IdP (to the SLS endpoint of the IdP). The IdP receives the Logout Response, process it and close the session at of the IdP. Notice that the SLO Workflow starts and ends at the IdP.
1204+
1205+
Notice that all the SAML Requests and Responses are handled at a unique view (index) and how GET parameters are used to know the action that must be done.
1206+
1207+
### Demo Tornado ###
1208+
1209+
You'll need a virtualenv with the toolkit installed on it.
1210+
1211+
First of all you need some packages, execute:
1212+
```
1213+
apt-get install libxml2-dev libxmlsec1-dev libxmlsec1-openssl
1214+
```
1215+
1216+
To run the demo you need to install the requirements first. Load your
1217+
virtualenv and execute:
1218+
```
1219+
pip install -r demo-tornado/requirements.txt
1220+
```
1221+
1222+
1223+
This will install tornado and its dependencies. Once it has finished, you have to complete the configuration
1224+
of the toolkit. You'll find it at `demo-tornado/saml/settings.json`
1225+
1226+
Now, with the virtualenv loaded, you can run the demo like this:
1227+
```
1228+
cd demo-tornado
1229+
python views.py
1230+
```
1231+
1232+
You'll have the demo running at http://localhost:8000
1233+
1234+
#### Content ####
1235+
1236+
The tornado project contains:
1237+
1238+
* ***views.py*** Is the main flask file, where or the SAML handle take place.
1239+
1240+
* ***settings.py*** Contains the base path and the path where is located the ``saml`` folder and the ``template`` folder
1241+
1242+
* ***templates***. Is the folder where tornado stores the templates of the project. It was implemented a base.html template that is extended by index.html and attrs.html, the templates of our simple demo that shows messages, user attributes when available and login and logout links.
1243+
1244+
* ***saml*** Is a folder that contains the 'certs' folder that could be used to store the X.509 public and private key, and the saml toolkit settings (settings.json and advanced_settings.json).
1245+
1246+
#### SP setup ####
1247+
1248+
The Onelogin's Python Toolkit allows you to provide the settings info in 2 ways: Settings files or define a setting dict. In the ``demo-tornado``, it uses the first method.
1249+
1250+
In the ``settings.py`` file we define the ``SAML_PATH``, that will target to the ``saml`` folder. We require it in order to load the settings files.
1251+
1252+
First we need to edit the ``saml/settings.json`` file, configure the SP part and review the metadata of the IdP and complete the IdP info. Later edit the ``saml/advanced_settings.json`` files and configure the how the toolkit will work. Check the settings section of this document if you have any doubt.
1253+
1254+
#### IdP setup ####
1255+
1256+
Once the SP is configured, the metadata of the SP is published at the ``/metadata`` url. Based on that info, configure the IdP.
1257+
11801258
#### How it works ####
11811259

11821260
1. First time you access to the main view (http://localhost:8000), you can select to login and return to the same view or login and be redirected to ``/?attrs`` (attrs view).

demo-tornado/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Tornado Demo #
2+
Fully-working tornado-demo.
3+
4+
### About issues ###
5+
This is only a demo, some issues about session still remain.
6+
Actually the session is global.
7+
8+
### Production ###
9+
Remember to disable debugging in production.

demo-tornado/Settings.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import os
2+
3+
BASE_DIR = os.path.dirname(__file__)
4+
5+
SAML_PATH = os.path.join(BASE_DIR, 'saml')
6+
TEMPLATE_PATH = os.path.join(BASE_DIR, 'templates')

demo-tornado/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
tornado==6.0.3
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
{
2+
"security": {
3+
"nameIdEncrypted": false,
4+
"authnRequestsSigned": false,
5+
"logoutRequestSigned": false,
6+
"logoutResponseSigned": false,
7+
"signMetadata": false,
8+
"wantMessagesSigned": false,
9+
"wantAssertionsSigned": false,
10+
"wantNameId" : true,
11+
"wantNameIdEncrypted": false,
12+
"wantAssertionsEncrypted": false,
13+
"signatureAlgorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
14+
"digestAlgorithm": "http://www.w3.org/2000/09/xmldsig#sha1"
15+
},
16+
"contactPerson": {
17+
"technical": {
18+
"givenName": "technical_name",
19+
"emailAddress": "[email protected]"
20+
},
21+
"support": {
22+
"givenName": "support_name",
23+
"emailAddress": "[email protected]"
24+
}
25+
},
26+
"organization": {
27+
"en-US": {
28+
"name": "sp_test",
29+
"displayname": "SP test",
30+
"url": "http://sp.example.com"
31+
}
32+
}
33+
}

demo-tornado/saml/certs/README

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Take care of this folder that could contain private key. Be sure that this folder never is published.
2+
3+
Onelogin Python Toolkit expects that certs for the SP could be stored in this folder as:
4+
5+
* sp.key Private Key
6+
* sp.crt Public cert
7+
* sp_new.crt Future Public cert
8+
9+
10+
Also you can use other cert to sign the metadata of the SP using the:
11+
12+
* metadata.key
13+
* metadata.crt

demo-tornado/saml/settings.json

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{
2+
"strict": true,
3+
"debug": true,
4+
"sp": {
5+
"entityId": "https://<sp_domain>/metadata/",
6+
"assertionConsumerService": {
7+
"url": "https://<sp_domain>/?acs",
8+
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
9+
},
10+
"singleLogoutService": {
11+
"url": "https://<sp_domain>/?sls",
12+
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
13+
},
14+
"NameIDFormat": "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified",
15+
"x509cert": "",
16+
"privateKey": ""
17+
},
18+
"idp": {
19+
"entityId": "https://app.onelogin.com/saml/metadata/<onelogin_connector_id>",
20+
"singleSignOnService": {
21+
"url": "https://app.onelogin.com/trust/saml2/http-post/sso/<onelogin_connector_id>",
22+
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
23+
},
24+
"singleLogoutService": {
25+
"url": "https://app.onelogin.com/trust/saml2/http-redirect/slo/<onelogin_connector_id>",
26+
"binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
27+
},
28+
"x509cert": "<onelogin_connector_cert>"
29+
}
30+
}

demo-tornado/templates/attrs.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{% extends "base.html" %}
2+
3+
{% block content %}
4+
5+
{% if paint_logout %}
6+
{% if attributes %}
7+
<p>You have the following attributes:</p>
8+
<table class="table table-striped">
9+
<thead>
10+
<th>Name</th><th>Values</th>
11+
</thead>
12+
<tbody>
13+
{% for attr, i in attributes %}
14+
{% if i == 0 %}
15+
<tr><td>{{ attr }}</td>
16+
<td><ul class="list-unstyled">
17+
{% end %}
18+
{% if i == 1 %}
19+
{% for val in attr %}
20+
<li>{{ val }}</li>
21+
{% end %}
22+
{% end %}
23+
</ul></td></tr>
24+
{% end %}
25+
</tbody>
26+
</table>
27+
{% else %}
28+
<div class="alert alert-danger" role="alert">You don't have any attributes</div>
29+
{% end %}
30+
<a href="/?slo" class="btn btn-danger">Logout</a>
31+
{% else %}
32+
<a href="/?sso2" class="btn btn-primary">Login and access again to this page</a>
33+
{% end %}
34+
35+
{% end %}

demo-tornado/templates/base.html

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
6+
<meta name="viewport" content="width=device-width, initial-scale=1">
7+
8+
<title>A Python SAML Toolkit by OneLogin demo</title>
9+
10+
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
11+
12+
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
13+
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
14+
<!--[if lt IE 9]>
15+
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
16+
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
17+
<![endif]-->
18+
</head>
19+
<body>
20+
<div class="container">
21+
<h1>A Python SAML Toolkit by OneLogin demo</h1>
22+
23+
{% block content %}{% end %}
24+
</div>
25+
</body>
26+
</html>

demo-tornado/templates/index.html

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{% extends "base.html" %}
2+
3+
{% block content %}
4+
5+
{% if errors %}
6+
<div class="alert alert-danger" role="alert">
7+
<strong>Errors:</strong>
8+
<ul class="list-unstyled">
9+
{% for err in errors %}
10+
<li>{{err}}</li>
11+
{% end %}
12+
{% if error_reason %}
13+
<span>{{error_reason}}</span>
14+
{% end %}
15+
</ul>
16+
</div>
17+
{% end %}
18+
19+
{% if not_auth_warn %}
20+
<div class="alert alert-danger" role="alert">Not authenticated</div>
21+
{% end %}
22+
23+
{% if success_slo %}
24+
<div class="alert alert-success" role="alert">Successfully logged out</div>
25+
{% end %}
26+
27+
{% if paint_logout %}
28+
{% if attributes %}
29+
<table class="table table-striped">
30+
<thead>
31+
<th>Name</th><th>Values</th>
32+
</thead>
33+
<tbody>
34+
{% for attr in attributes %}
35+
<tr>
36+
<td>{{ attr[0] }}</td>
37+
<td><ul class="list-unstyled">
38+
<!-- <li>{{ attr[1][0] }}</li> -->
39+
{% for elem in attr[1] %}
40+
<li>{{ elem }}</li>
41+
{% end %}
42+
</ul></td>
43+
</tr>
44+
{% end %}
45+
46+
47+
<!-- {% for attr, i in attributes %}
48+
{% if i == 0 %}
49+
<tr><td>{{ attr }}</td>
50+
<td><ul class="list-unstyled">
51+
{% end %}
52+
{% if i == 1 %}
53+
{% for val in attr %}
54+
<li>{{ val }}</li>
55+
{% end %}
56+
{% end %}
57+
</ul></td></tr>
58+
{% end %} -->
59+
</tbody>
60+
</table>
61+
{% else %}
62+
<div class="alert alert-danger" role="alert">You don't have any attributes</div>
63+
{% end %}
64+
<a href="?slo" class="btn btn-danger">Logout</a>
65+
{% else %}
66+
<a href="?sso" class="btn btn-primary">Login</a> <a href="?sso2" class="btn btn-info">Login and access to attrs page</a>
67+
{% end %}
68+
69+
{% end %}

0 commit comments

Comments
 (0)