Commit df2c8be0 authored by Marco Malavolti's avatar Marco Malavolti
Browse files

sps-metadata,API,update req,eccs-loading.git,Unable-To-Check

parent c9cdc525
......@@ -23,60 +23,64 @@
* [Configure](#configure-1)
* [Utility](#utility)
10. [ECCS API JSON](#eccs-api-json)
11. [Utility for web interface](#utility-for-web-interface)
12. [Utility for developers](#utility-for-developers)
11. [User Interface](#user-interface)
* [User interface parameters](#user-interface-parameters)
12. [Utility for web interface](#utility-for-web-interface)
13. [Utility for developers](#utility-for-developers)
* [ECCS API Development Server](#eccs-api-development-server)
13. [Authors](#authors)
14. [Authors](#authors)
# Introduction
## Introduction
The purpose of the eduGAIN Connectivity Check is to identify eduGAIN Identity Providers (IdP) that are not properly configured. In particular it checks if an IdP properly loads and consumes SAML2 metadata which contains the eduGAIN Service Providers (SP). The check results are published on the public eduGAIN Connectivity Check web page (### NOT-AVAILABLE-YET ###). The main purpose is to increase the service overall quality and user experience of the eduGAIN interfederation service by making federation and Identity Provider operators aware of configuration problems.
The purpose of the eduGAIN Connectivity Check is to identify eduGAIN Identity Providers (IdP) that are not properly configured. In particular it checks if an IdP properly loads and consumes SAML2 metadata which contains the eduGAIN Service Providers (SP). The check results are published on the public eduGAIN Connectivity Check web page ([https://technical.edugain.org/eccs](https://technical.edugain.org/eccs)). The main purpose is to increase the service overall quality and user experience of the eduGAIN interfederation service by making Federation and Identity Provider operators aware of configuration problems.
The check is performed by sending a SAML authentication request to each eduGAIN IdP and then follow the various HTTP redirects. The expected result is a login form that allows users to authenticate (typically with username/password) or an error message of some form. For those Identity Providers that output an error message, it can be assumed that they don't consume eduGAIN metadata properly or that they suffer from another configuration problem. There are some cases where the check will generate false positives, therefore IdPs can be excluded from checks as is described below.
The Identity Providers are checked once per day. Therefore, the login requests should not have any significant effect on the log entries/statistics of an Identity Provider. Also, no actual login is performed because the check cannot authenticate users due to missing username and password for the IdPs. Only Identity Providers are checked but not the Service Providers.
# Check Performed on the IdPs
## Check Performed on the IdPs
The check executed by the service follows these steps:
The check follows the steps:
1. It retrieves the eduGAIN IdPs from eduGAIN Operator Team database via a JSON interface
2. For each IdP that is was not manually disabled by the eduGAIN Operations Team, the check creates a Wayfless URL for each SP involved and retrieves the IdP login page. It expects to find the HTML form with a username and password field. Therefore, no complete login will happen at the Identity Provider because the check stops at the login page.
The SPs used for the check are "SP Demo provided by GARR" (https://sp-demo.idem.garr.it/shibboleth) from IDEM GARR AAI and the "AAI Viewer Interfederation Test" (https://attribute-viewer.aai.switch.ch/interfederation-test/shibboleth) from SWITCHaai. These SPs might change in the future if needed.
The SAML authenticatin request is not signed. Therefore, authentication request for any eduGAIN SP could be created because the SP's private key is not needed.
2. For each IdP, that hasn't been disabled manually by the eduGAIN Operations Team or dynamically by "robots.txt" (explained below) and that has a valid SSL certificate on its HTTP-Redirect Location, it performs an IdP-initiated SSO with SAML Authentication Request for two SP belonging two different NREN, members of eduGAIN interfederation, and for one fake SP. It expects to find the HTML form with username and password fields on the "good" SPs and an error or other on the "bad" SP. If an IdP uses frames on its Login page, the check follows only the first one on each redirected pages. If an IdP uses HTTP Basic Authentication, the check searches '401 Unauthorized' string into the web page content presented and establish the correct behaviour of the IdP. Therefore, no complete login will happen at the Identity Provider because the check stops at the login page or at SSL Certificate validation.
The SAML authentication request is not signed. Therefore, an authentication request for any eduGAIN SP could be created because the SP's private key is not needed.
The SPs HTTP-Post Assertion Consumer Service URLs used by the check are retrieved by "sps-metadata.xml" into the "input" directory. The 'validation' method used to validate the "sps-metadata.xml" is a deployer decision, but a solution is provided on the "README-SPS-METADATA.md" file.
# Limitations
3. If the check fails for an IdP the first time, it will be checked again at the end of the execution for a second time before exit.
4. It keeps the results of the last 7 days, but it can be increased by the deployers.
## Limitations
There are some situations where the check cannot work reliably. In those cases it is possible to disable the check for a particular IdP. The so far known cases where the check might generate a false negative are:
* IdP does not support HTTP or HTTPS with at least SSLv3 or TLS1 or newer (these IdPs are insecure anyway)
* IdP is part of a Hub & Spoke federation (some of them manually have to first approve eduGAIN SPs)
* IdP does not use web-based login form (e.g. HTTP Basic Authentication or X.509 login)
* IdP does not use web-based login form (e.g. Account Chooser Authentication or X.509 login)
# Disable Checks
## Disable Checks
In cases where an IdP cannot be reliably checked, it is necessary to create or enrich the `robots.txt` file on the IdP's web root with:
In cases where an IdP cannot be reliably checked, it is necessary to create or enrich the `robots.txt` file on the IdP's web root dir with:
```bash
User-agent: ECCS
Disallow: /
```
If an IdP is not able to create its own `robots.txt` file under the web root directory, it can be disabled by setting the dictionary `IDPS_DISABLED_DICT` into `eccs_properties.py` with a line in the form:
If an IdP is not able to create its own `robots.txt`, it can be disabled by an eduGAIN Operation Team member by setting the dictionary `IDPS_DISABLED_DICT` into `eccs_properties.py` with a line in the form:
'<idp-entity-id>':'<eccs-check-disabling-reason>'
# On-line interface
The test eduGAIN Connectivity Check web pages is available at: https://technical-test.edugain.org/eccs
## On-line interface
The tool uses following status for IdPs:
* ERROR (red):
* The IdP's response contains an HTTP Error or the web page returned does not look like a login page.
* **Invalid-Form**: considers those IdPs that do not load a standard username/password login page and do not return messages like "*No return endpoint available for relying party*" or "*No metadata found for relying party"*.
* **Unable-To-Check**: considers those IdPs that do not load a standard username/password login page and do not return messages like "*No return endpoint available for relying party*" or "*No metadata found for relying party"*.
* **Timeout**: considers those IdPs that do not load a standard username/password login page within 60 seconds.
* **Connection-Error**: considers those IdPs that are not reachable due to a connection problem. View the "Page Source" value to discover which problem the IdP has.
* **IdP-Error**: considers those IdPs that the web page returned does not contain a Login Form and reports an unspecified error such as "*An error occured*". This has been seen on Micrsoft ADFS based IdPs
......@@ -90,7 +94,7 @@ The tool uses following status for IdPs:
* DISABLED (white)
* The IdP is excluded because it cannot be checked reliably. The "*Page Source*" column, when an entity is disabled, shows the reason of the disabling.
# Requirements Hardware
## Requirements Hardware
* OS: Debian 9, CentOS 7.8 (tested)
* HDD: 10 GB
......@@ -98,23 +102,24 @@ The tool uses following status for IdPs:
* CPU: >= 2 vCPU (suggested)
* ARCH: 64 Bit
# Requirements Software
## Requirements Software
* Apache Server + WSGI
* Python 3 (tested with v3.9.1)
* Selenim + Google Chrome Web Brower (tested with v91.0.4472.164)
* Chromedriver (tested with v91.0.4472.101)
* Python 3 (tested with v3.9.1, v3.10.4)
* Selenim + Google Chrome Web Brower (tested with v91.0.4472.164, v100.0.4896.127)
* Chromedriver (tested with v91.0.4472.101, v100.0.4896.60)
* Git
* PHP
# HOWTO Install and Configure
## HOWTO Install and Configure
## Download ECCS Repository
### Download ECCS Repository
* `cd $HOME ; git clone https://gitlab.geant.org/edugain/eccs.git`
## Install Python 3
### Install Python 3
### CentOS 7 requirements
#### CentOS 7 requirements
1. Update the system packages:
* `sudo yum -y update`
......@@ -125,44 +130,58 @@ The tool uses following status for IdPs:
3. Install needed packages to build python:
* `sudo yum-builddep python3`
If you want to use Python 3.10, you need OpenSSL >= 1.1.1:
* `sudo yum install openssl11 openssl11-devel`
* `sudo mkdir /usr/local/openssl11`
* `sudo cd /usr/local/openssl11`
* `sudo ln -s /usr/lib64/openssl11 lib`
* `sudo ln -s /usr/include/openssl11 include`
4. Install Git:
* `sudo yum -y install git`
### Debian requirements
#### Debian requirements
1. Update the system packages:
* `sudo apt update ; sudo apt upgrade -y`
2. Install needed packages to build python3:
* `sudo apt-get build-dep python3`
* `sudo apt-get build-dep python3 libffi-dev libssl-dev zlib-dev`
3. Install Git:
* `sudo apt install git`
### Install
#### Install
1. Download the last version of Python 3 from https://www.python.org/downloads/source/ into your home:
* `wget https://www.python.org/ftp/python/3.9.1/Python-3.9.1.tgz -O $HOME/eccs/Python-3.9.1.tgz`
* `wget https://www.python.org/ftp/python/3.10.4/Python-3.10.4.tgz -O $HOME/eccs/Python-3.10.4.tgz`
2. Extract Python source package:
* `cd $HOME/eccs/`
* `tar xzf Python-3.9.1.tgz`
* `tar xzf Python-3.10.4.tgz`
3. Build Python from the source package:
* `cd $HOME/eccs/Python-3.9.1`
* `./configure --prefix=$HOME/eccs/python`
* `make`
* Debian:
* `cd $HOME/eccs/Python-3.10.4`
* `./configure --prefix=$HOME/eccs/python`
* `make`
* Centos 7:
* `cd $HOME/eccs/Python-3.10.4`
* `./configure --prefix=$HOME/eccs/python --with-openssl=/usr/local/openssl11`
* `make`
4. Install Python 3 under `$HOME/eccs/python`:
* `make install`
* `$HOME/eccs/python/bin/python3 --version`
* `$HOME/eccs/python/bin/python3 -c "import ssl; print (ssl.OPENSSL_VERSION)"`
This will install python3 under your $HOME/eccs directory.
This will install python3 under your $HOME/eccs/python directory.
5. Remove useless things:
* `rm -Rf $HOME/eccs/Python-3.9.1 $HOME/eccs/Python-3.9.1.tgz`
* `rm -Rf $HOME/eccs/Python-3.10.4 $HOME/eccs/Python-3.10.4.tgz`
## Install Google Chrome needed by Selenium
### Install Google Chrome needed by Selenium
* Debian (64 bit):
* `cd $HOME/eccs`
......@@ -174,32 +193,32 @@ The tool uses following status for IdPs:
* `sudo wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm`
* `sudo yum install ./google-chrome-stable_current_x86_64.rpm`
## Install the Chromedriver
### Install the Chromedriver
1. Find out which version of Chromium you are using:
* Debian 9 (stretch):
* `google-chrome -version` => Google Chrome 91.0.4472.164
* `google-chrome -version` => Google Chrome 100.0.4896.127
* CentOS 7.8:
* `google-chrome -version` => Google Chrome 91.0.4472.164
* `google-chrome -version` => Google Chrome 100.0.4896.127
2. Take the Chrome version number, remove the last part, and append the result to URL "`https://chromedriver.storage.googleapis.com/LATEST_RELEASE_`". For example, with Chrome version 73.0.3683.75, you'd get a URL "`https://chromedriver.storage.googleapis.com/LATEST_RELEASE_73.0.3683`".
2. Take the Chrome version number, remove the last part, and append the result to URL "`https://chromedriver.storage.googleapis.com/LATEST_RELEASE_`". For example, with Chrome version 100.0.4896.127, you'd get a URL "`https://chromedriver.storage.googleapis.com/LATEST_RELEASE_100.0.4896`".
3. Use the URL created in the last step to discover the version of ChromeDriver to use. For example, the above URL will get your a file containing "73.0.3683.68".
3. Use the URL created in the last step to discover the version of ChromeDriver to use. For example, the above URL will get your a file containing "100.0.4896.60".
4. Use the version number retrieved from the previous step to construct the URL to download ChromeDriver. With version `72.0.3626.68`, the URL would be "https://chromedriver.storage.googleapis.com/index.html?path=73.0.3683.68/"
4. Use the version number retrieved from the previous step to construct the URL to download ChromeDriver. With version `100.0.4896.60`, the URL would be "https://chromedriver.storage.googleapis.com/index.html?path=100.0.4896.60/"
5. Download the Chromedriver and extract it with:
* `cd $HOME/eccs`
* `wget https://chromedriver.storage.googleapis.com/73.0.3683.68/chromedriver_linux64.zip`
* `wget https://chromedriver.storage.googleapis.com/100.0.4896.60/chromedriver_linux64.zip`
* `unzip chromedriver_linux64.zip`
* `rm chromedriver_linux64.zip google-chrome-stable_current_amd64.deb`
**Note:**
After the initial download, it is recommended that you occasionally go through the above process again to see if there are any bug fix releases.
## ECCS Script
### ECCS Script
### Install and Configure the Virtual Environment
#### Install and Configure the Virtual Environment
* `cd $HOME/eccs`
* `./python/bin/python3 -m pip install virtualenv`
......@@ -208,7 +227,7 @@ After the initial download, it is recommended that you occasionally go through t
* `source eccs-venv/bin/activate` (`deactivate` to exit Virtualenv)
* `python3 -m pip install -r requirements.txt`
### Configure ECCS
#### Configure ECCS
1. Configure ECCS properties:
* `cp $HOME/eccs/eccs_properties.py.template $HOME/eccs/eccs_properties.py`
......@@ -250,7 +269,7 @@ After the initial download, it is recommended that you occasionally go through t
0 4 * * * /bin/bash $HOME/eccs/cleanAndRunEccs.sh > $HOME/eccs/logs/eccs-cron.log 2>&1
```
## Execute
### Execute
* `cd $HOME/eccs`
* `./cleanAndRunEccs.py` (to run a full and clean check)
......@@ -264,9 +283,9 @@ After the initial download, it is recommended that you occasionally go through t
The "--test" parameter will not change the result of ECCS, but will write the output on the `logs/stdout_idp_YYYY-MM-DD.log`,`logs/stderr_idp_YYYY-MM-DD.log` and `logs/failed-cmd-idp.sh` files if the argument "--test" will be used.
# ECCS API Server (uWSGI)
## ECCS API Server (uWSGI)
## Install
### Install
1. Install requirements:
* Debian:
......@@ -280,7 +299,7 @@ After the initial download, it is recommended that you occasionally go through t
* `sudo restorecon -R -v "$HOME/eccs/html/"`
* `sudo setsebool -P httpd_can_network_connect 1`
## Configure
### Configure
1. Add the systemd service to enable ECCS API:
* `cd $HOME/eccs`
......@@ -295,8 +314,10 @@ After the initial download, it is recommended that you occasionally go through t
2. Configure Apache for ECCS web side:
* Debian:
* `sudo cp $HOME/eccs/eccs-debian.conf /etc/apache2/conf-available/eccs.conf`
* `sudo cp $HOME/eccs/eccs-debian.conf /etc/apache2/conf-available/eccs.conf
* `sudo vim /etc/apache2/conf-available/eccs.conf` (and change the file opportunely)
* `sudo a2enconf eccs.conf`
* `sudo a2enmod proxy_uwsgi`
* `sudo chgrp www-data $HOME ; sudo chmod g+rx $HOME` (Apache needs permission to access the $HOME dir)
* `sudo systemctl restart apache2.service`
* CentOS:
......@@ -313,13 +334,13 @@ After the initial download, it is recommended that you occasionally go through t
0 3 * * * /usr/bin/touch $HOME/eccs/eccs.ini
```
## Utility
### Utility
To perform a restart after an API change use the following command:
* `touch $HOME/eccs/eccs.ini`
# ECCS API JSON
## ECCS API JSON
* `/api/eccsresults` (Return the results of the last check ready for ECCS web interface)
* `/api/eccsresults?<parameter1>=<value1>&<parameter2>=<value2>`:
......@@ -332,38 +353,22 @@ To perform a restart after an API change use the following command:
* `check_result=`
* `OK`
* `Timeout`
* `Invalid-Form`
* `Connection-Error`
* `IdP-Error`
* `No-eduGAIN-Metadata`
* `SSL-Error`
* `Unable-To-Check`
* `DISABLED`
* `reg_auth=https://reg.auth.example.org` (select a specific Registration Authority)
* `format=simple` (retrieve results in a simple format)
* `/api/fedstats`
* `/api/fedstats?reg_auth=https://reg.auth.example.org`:
# Utility for web interface
The available dates are provided by the first and the last file created into the `output/` directory,
remember to change its path into `web/eccs.php` file.
## Clean old results
To clean the ECCS results from files older than last 7 days use (modify it on your needs):
* `crontab -e`
```bash
SHELL=/bin/bash
0 10 * * * /bin/bash $HOME/eccs/clean7daysOldFiles.sh > $HOME/eccs/logs/clean7daysOldFiles.log 2>&1
```
## User interface
The eduGAIN Connectivity Check Service web page is available at https://technical-test.edugain.org/eccs
## User interface parameters
### User interface parameters
| Parameter name | Example |
| -------------- | -------------------------------------------- |
......@@ -377,24 +382,41 @@ The eduGAIN Connectivity Check Service web page is available at https://technica
`https://technical-test.edugain.org/eccs?reg_auth=http://www.idem.garr.it/&check_result=SSL-Error`
# Utility for developers
## Utility for web interface
The available dates are provided by the first and the last file created into the `output/` directory,
remember to change its path into `web/eccs.php` file.
### Clean old results
To clean the ECCS results from files older than last 7 days use (modify it on your needs):
* `crontab -e`
```bash
SHELL=/bin/bash
0 10 * * * /bin/bash $HOME/eccs/clean7daysOldFiles.sh > $HOME/eccs/logs/clean7daysOldFiles.log 2>&1
```
## Utility for developers
## ECCS API Development Server
### ECCS API Development Server
* `cd $HOME/eccs ; ./api.py`
## Search files created on the current date
### Search files created on the current date
* `cd $HOME/eccs`
* `find . -name *$(date +%Y-%m-%d)*`
## Delete files created on the current date
### Delete files created on the current date
* `cd $HOME/eccs`
* `rm -rf html/$(date +%Y-%m-%d) output/eccs_$(date +%Y-%m-%d).log logs/*_$(date +%Y-%m-%d).log`
# Authors
## Authors
## Original Author
### Original Author
* Marco Malavolti (marco.malavolti@garr.it)
......@@ -4,10 +4,12 @@ import json
import logging
import re
from eccs_properties import DAY, ECCS_LOGSDIR, ECCS_OUTPUTDIR, ECCS_LISTFEDSURL, ECCS_LISTFEDSFILE, ECCS_RESULTSLOG
import eccs_properties as e_p
#from eccs_properties import DAY, ECCS_LOGSDIR, ECCS_OUTPUTDIR, ECCS_LISTFEDSURL, ECCS_LISTFEDSFILE, ECCS_RESULTSLOG
from flask import Flask, request, jsonify
from flask_restful import Resource, Api
from utils import get_logger, get_list_feds, get_reg_auth_dict
from utils import get_logger, get_list_feds, get_list_eccs_idps, get_reg_auth_dict, generate_login_url
from markupsafe import escape
app = Flask(__name__)
api = Api(app)
......@@ -53,7 +55,7 @@ def getSimpleDict(aux):
"entityID": aux['entityID'],
"registrationAuthority": aux['registrationAuthority'],
"status": aux['status'],
"checkResult": [aux["sp1"]["checkResult"],aux["sp2"]["checkResult"]]
"checkResult": [aux["sp1"]["checkResult"],aux["sp2"]["checkResult"],aux["sp3"]["checkResult"]]
}
return simpleDict
......@@ -70,8 +72,8 @@ class Test(Resource):
class EccsResults(Resource):
def get(self):
file_path = f"{ECCS_OUTPUTDIR}/{ECCS_RESULTSLOG}"
date = DAY
file_path = f"{e_p.ECCS_OUTPUTDIR}/{e_p.ECCS_RESULTSLOG}"
date = e_p.DAY
status = None
idp = None
reg_auth = None
......@@ -83,11 +85,11 @@ class EccsResults(Resource):
eccsDataTable = True
if 'date' in request.args:
date = request.args['date']
file_path = f"{ECCS_OUTPUTDIR}/eccs_{date}.log"
file_path = f"{e_p.ECCS_OUTPUTDIR}/eccs_{date}.log"
if 'status' in request.args:
status = request.args['status'].upper()
if (status not in ['OK','DISABLED','ERROR']):
return jsonify(error="Incorrect status provided. It can be 'ok','disabled','error'")
return jsonify(error="Incorrect status provided. It can be 'OK','DISABLED','ERROR'")
if 'idp' in request.args:
idp = request.args['idp']
if (not existsInFile(file_path, idp, "entityID", eccsDataTable, date)):
......@@ -101,8 +103,8 @@ class EccsResults(Resource):
simple = True
if 'check_result' in request.args:
check_result = request.args['check_result']
if (check_result not in ['OK','Timeout','Invalid-Form','Connection-Error','No-eduGAIN-Metadata','SSL-Error','IdP-Error','DISABLED']):
return jsonify(error="Incorrect check_result value provided. It can be 'OK','Timeout','Invalid-Form','Connection-Error','No-eduGAIN-Metadata','SSL-Error','IdP-Error' or 'DISABLED'")
if (check_result not in ['OK','Timeout','Unable-To-Check','Connection-Error','No-eduGAIN-Metadata','SSL-Error','IdP-Error','DISABLED']):
return jsonify(error="Incorrect check_result value provided. It can be 'OK','Timeout','Unable-To-Check','Connection-Error','No-eduGAIN-Metadata','SSL-Error','IdP-Error' or 'DISABLED'")
lines = []
results = []
......@@ -154,6 +156,13 @@ class EccsResults(Resource):
results.append(auxsimple)
else:
results.append(aux)
elif (status and check_result):
if (status == aux['status'] and (check_result == aux['sp1']['checkResult'] or check_result == aux['sp2']['checkResult'])):
if (simple):
auxsimple = getSimpleDict(aux)
results.append(auxsimple)
else:
results.append(aux)
elif (idp):
if (idp == aux['entityID']):
if (simple):
......@@ -195,17 +204,17 @@ class EccsResults(Resource):
# /api/fedstats
class FedStats(Resource):
def get(self):
list_feds = get_list_feds(ECCS_LISTFEDSURL, ECCS_LISTFEDSFILE)
list_feds = get_list_feds(e_p.ECCS_LISTFEDSURL, e_p.ECCS_LISTFEDSFILE)
regAuthDict = get_reg_auth_dict(list_feds)
file_path = f"{ECCS_OUTPUTDIR}/{ECCS_RESULTSLOG}"
date = DAY
file_path = f"{e_p.ECCS_OUTPUTDIR}/{e_p.ECCS_RESULTSLOG}"
date = e_p.DAY
reg_auth = None
eccsDataTable = False
if ('date' in request.args):
date = request.args['date']
file_path = f"{ECCS_OUTPUTDIR}/eccs_{date}.log"
file_path = f"{e_p.ECCS_OUTPUTDIR}/eccs_{date}.log"
if ('reg_auth' in request.args):
reg_auth = request.args['reg_auth']
if (not existsInFile(file_path, reg_auth, "registrationAuthority", eccsDataTable, date)):
......@@ -271,17 +280,34 @@ class Help(Resource):
def get(self):
return { 'ECCS JSON Interface': 'https://wiki.geant.org/display/eduGAIN/eduGAIN+Connectivity+Check+2#eduGAINConnectivityCheck2-JSONinterface' }
@app.route('/getsamlreq')
def getSamlReq():
# Setup list_eccs_idps
list_eccs_idps = get_list_eccs_idps(e_p.ECCS_LISTIDPSURL, e_p.ECCS_LISTIDPSFILE)
entityid_idp = request.args.get('idp')
entityid_sp = request.args.get('sp')
for idp in list_eccs_idps:
if (idp['entityID'] == entityid_idp):
for sp in e_p.ECCS_SPS:
if (sp['entityID'] == entityid_sp):
return f"<meta http-equiv='Refresh' content='0; url={generate_login_url(sp['entityID'], sp['http_post_acs_location'], idp['Location'])}' />"
else:
fake_sp_acs_url = f"{entityid_sp.split('shibboleth')[0]}Shibboleth.sso/SAML2/POST"
return f"<meta http-equiv='Refresh' content='0; url={generate_login_url(entityid_sp,fake_sp_acs_url, idp['Location'])}' />"
# Routes
api.add_resource(Test, '/test') # Route_1
api.add_resource(EccsResults, '/eccsresults') # Route_2
api.add_resource(FedStats, '/fedstats') # Route_3
api.add_resource(Help, '/') # Route_4
api.add_resource(Help, '/') # Route_1
api.add_resource(Test, '/test') # Route_2
api.add_resource(EccsResults, '/eccsresults') # Route_3
api.add_resource(FedStats, '/fedstats') # Route_4
if __name__ == '__main__':
# Useful only for API development Server
#app.config['JSON_AS_ASCII'] = True
#app.logger.removeHandler(default_handler)
#app.logger = get_logger("eccs_api.log", ECCS_LOGSDIR, "w", "INFO")
#app.logger = get_logger("eccs_api.log", e_p.ECCS_LOGSDIR, "w", "INFO")
app.run(port='5002')
......@@ -2,6 +2,7 @@
import argparse
import json
import pdb
import sys
import utils
import eccs_properties as e_p
......@@ -9,7 +10,7 @@ import eccs_properties as e_p
from pathlib import Path
"""
The check works with the wayfless url of two SP and successed if the IdP Login Page appears and contains the fields "username" and "password" for each of them.
The check works with the SAML Request generated for three SP (one fake SP and two SP provided by different NREN) and successed if the IdP Login Page appears and contains the fields "username" and "password" for each 'good' SP and not for the fake SP.
It is possible to disable the check by eccs_properties IDP_DISABLE_DICT or by "robots.txt" put on the SAMLRequest endpoint root web dir.
"""
......@@ -36,62 +37,76 @@ def store_eccs_result(idp,sp,check_results,idp_status,test):
if (test):
sys.stdout.write("\nECCS:")
sys.stdout.write('{"displayName":"%s","entityID":"%s","registrationAuthority":"%s","contacts":{"technical":"%s","support":"%s"},"status":"%s","sp1":{"wayflessUrl":"%s","checkTime":"%s","checkResult":"%s"},"sp2":{"wayflessUrl":"%s","checkTime":"%s","checkResult":"%s"}}\n' % (
sys.stdout.write('{"displayName":"%s","entityID":"%s","registrationAuthority":"%s","contacts":{"technical":"%s","support":"%s"},"status":"%s","sp1":{"entityID":"%s","checkTime":"%s","checkResult":"%s"},"sp2":{"entityID":"%s","checkTime":"%s","checkResult":"%s"},"sp3":{"entityID":"%s","checkTime":"%s","checkResult":"%s"}}\n' % (
get_display_name(idp['displayname']), # IdP-DisplayName
idp['entityID'], # IdP-entityID
idp['registrationAuthority'], # IdP-RegAuth
str_technical_contacts, # IdP-TechCtcsList
str_support_contacts, # IdP-SuppCtcsList
idp_status, # IdP-ECCS-Status
check_results[0][1], # SP-wayfless-url-1
check_results[0][1], # SP-entityID-1
check_results[0][2], # SP-check-time-1
check_results[0][3], # SP-check-result-1
check_results[1][1], # SP-wayfless-url-2
check_results[1][1], # SP-entityID-2
check_results[1][2], # SP-check-time-2
check_results[1][3])) # SP-check-result-2
check_results[1][3], # SP-check-result-3
check_results[2][1], # SP-entityID-3
check_results[2][2], # SP-check-time-3
check_results[2][3] # SP-check-result-3
)
)
else:
# IdP-DisplayName;IdP-entityID;IdP-RegAuth;IdP-tech-ctc-1,IdP-tech-ctc-2;IdP-supp-ctc-1,IdP-supp-ctc-2;IdP-ECCS-Status;SP-wayfless-url-1;SP-check-time-1;SP-result-1;SP-wayfless-url-2;SP-check-time-2;SP-result-2
# IdP-DisplayName;IdP-entityID;IdP-RegAuth;IdP-tech-ctc-1,IdP-tech-ctc-2;IdP-supp-ctc-1,IdP-supp-ctc-2;IdP-ECCS-Status;SP-entityID-1;SP-check-time-1;SP-result-1;SP-entityID-2;SP-check-time-2;SP-result-2;SP-entityID-3;SP-check-time-3;SP-result-3
with open(f"{e_p.ECCS_OUTPUTDIR}/{e_p.ECCS_RESULTSLOG}", 'a') as f:
try:
f.write('{"displayName":"%s","entityID":"%s","registrationAuthority":"%s","contacts":{"technical":"%s","support":"%s"},"status":"%s","sp1":{"wayflessUrl":"%s","checkTime":"%s","checkResult":"%s"},"sp2":{"wayflessUrl":"%s","checkTime":"%s","checkResult":"%s"}}\n' % (
f.write('{"displayName":"%s","entityID":"%s","registrationAuthority":"%s","contacts":{"technical":"%s","support":"%s"},"status":"%s","sp1":{"entityID":"%s","checkTime":"%s","checkResult":"%s"},"sp2":{"entityID":"%s","checkTime":"%s","checkResult":"%s"},"sp3":{"entityID":"%s","checkTime":"%s","checkResult":"%s"}}\n' % (
get_display_name(idp['displayname']), # IdP-DisplayName
idp['entityID'], # IdP-entityID
idp['registrationAuthority'], # IdP-RegAuth
str_technical_contacts, # IdP-TechCtcsList
str_support_contacts, # IdP-SuppCtcsList
idp_status, # IdP-ECCS-Status
check_results[0][1], # SP-wayfless-url-1
check_results[0][1], # SP-entityID-1
check_results[0][2], # SP-check-time-1
check_results[0][3], # SP-check-result-1
check_results[1][1], # SP-wayfless-url-2
check_results[1][1], # SP-entityID-2
check_results[1][2], # SP-check-time-2
check_results[1][3] # SP-check-result-2
check_results[1][3], # SP-check-result-2
check_results[2][1], # SP-entityID-3
check_results[2][2], # SP-check-time-3
check_results[2][3] # SP-check-result-3
)
)
except IOError:
sys.stderr.write(f"Failed writing result on output file for {idp['entityID']} with {utils.get_label(sp)}.\n\nRun {e_p.ECCS_DIR}/runEccs.py --idp {idp['entityID']} --replace\n")
sys.stderr.write(f"Failed writing result on output file for {idp['entityID']} with {utils.get_label(sp['entityID'])}.\n\nRun {e_p.ECCS_DIR}/runEccs.py --idp {idp['entityID']} --replace\n")
sys.exit(1)
# Check an IdP with 2 SPs.
# Check an IdP with 2 SPs + Fake SP.
def check(idp,test):
check_results = []
for sp in e_p.ECCS_SPS:
result = utils.check_idp_response_selenium(sp,idp,test)
if (result):
check_results.append(result)
else:
sys.stderr.write(f"\nCheck failed for {idp['entityID']} with {utils.get_label(sp)}.\n\nRun {e_p.ECCS_DIR}/runEccs.py --idp {idp['entityID']} --replace\n")
sys.stderr.write(f"\nCheck failed for {idp['entityID']} with {utils.get_label(sp['entityID'])}.\n\nRun {e_p.ECCS_DIR}/runEccs.py --idp {idp['entityID']} --replace\n")
sys.exit(1)
if (len(check_results) == len(e_p.ECCS_SPS)):
check_result_sp1 = check_results[0][3]
check_result_sp2 = check_results[1][3]
check_result_sp3 = check_results[2][3]
check_result_weberr1 = check_results[0][4]
check_result_weberr2 = check_results[1][4]
check_result_weberr3 = check_results[2][4]
# If all checks are 'OK', than the IdP consuming correctly eduGAIN Metadata.
if (check_result_sp1 == check_result_sp2 == "OK"):
# If the result of both good SPs is 'OK'
# and if the result of the fake SP is not 'OK'
# than the IdP is consuming correctly eduGAIN Metadata.
if ((check_result_sp1 == check_result_sp2 == "OK") and check_result_sp3 != "OK"):
store_eccs_result(idp,sp,check_results,'OK',test)
elif (check_result_sp1 == check_result_sp2 == "DISABLED"):
......