Giter Club home page Giter Club logo

spid-testenv2's Introduction

SPID

Join the #spid-testenv channel Get invited SPID on forum.italia.it .github/workflows/ci.yml

⚠️ This software is in End of Life, please don't use spid-testenv2 anymore! For testing a SPID SP consider to use spid-saml-check for a user friendly IdP or spid-sp-test for Continuous Integration tests.

spid-testenv2

Identity Provider di test per SPID

Questo Identity Provider consente agli sviluppatori di verificare le proprie integrazioni con SPID in modo semplice, ottenendo messaggi diagnostici chiari ed assicurandosi dell'interoperabilità.

Può essere facilmente eseguito in locale o su un proprio server seguendo le istruzioni di seguito riportate.

⚠️ AVVISO DI SICUREZZA: spid-testenv2 non deve essere utilizzato in ambienti di produzione. Nessun Service Provider deve accettare in produzione autenticazioni prodotte da spid-testenv2, che è solo uno strumento da utilizzarsi in fase di sviluppo e test.

Requisiti

Installazione

Docker (consigliata)

  1. Clonare il repository in locale

    git clone https://github.com/italia/spid-testenv2.git
  2. Entrare nella directory

    cd spid-testenv2
  3. Fare build dell'immagine

    docker build -t italia/spid-testenv2 .
  4. Lanciare il container:

    docker run -p 8088:8088 -v $(pwd)/conf:/app/conf italia/spid-testenv2

L'immagine italia/spid-testenv2 a anche disponibile su Docker Hub.

Manuale

  1. Installare le dipendenze.

    Su macOS si può usare brew install libxmlsec1 libffi.

    Su Debian/Ubuntu si può usare apt-get install libxmlsec1 libffi6.

  2. Creare ed attivare un virtualenv

    virtualenv -p `which python` env
    . env/bin/activate
  3. Installare i pacchetti necessari tramite pip

    pip install -r requirements.txt
  4. Generare una chiave privata ed un certificato

    openssl req -x509 \
                -nodes \
                -sha256 \
                -subj '/C=IT' \
                -newkey rsa:2048 \
                -keyout conf/idp.key \
                -out conf/idp.crt
  5. Creare e modificare il file config.yaml secondo le esigenze.

    cp conf/config.yaml.example conf/config.yaml
  6. Avvio

    python spid-testenv.py

Ansible

Alternativamente alle procedure riportate sopra, è possible installare spid-testenv2 tramite ansible.

Tutte le informazioni sono nella directory ansible/.

Caricamento metadata Service Provider

L'unico valore che è necessario modificare rispetto ai default è metadata, che indica i metadata dei Service Provider che si intendono collegare a spid-testenv2.

I metadati possono essere:

  1. Compilati manualmente a partire dal file sp_metadata.xml.example;
  2. Generati ed esposti automaticamente dalla propria implementazione del Service Provider (ad esempio https://mioserviceprovider.example.com/metadata).
  3. Inseriti manualmente dall'interfaccia in /admin/databasesprecord.

spid-testenv2 supporta il caricamento in tre modalità, che possono essere combinate tra loro:

  • local: i metadati vengono letti da file locali (all'avvio di testenv2);
  • remote: i metadati vengono letti da URL HTTP remote (all'avvio di testenv2);
  • db: i metadati vengono letti da un database (alla ricezione di ciascuna richiesta).

I Service Provider registrati correttamente saranno visualizzati nella pagina principale in https://localhost:8088/.

Caricamento da database

Nel caso in cui si usi la modalità db sia il database che la tabella verranno creati automaticamente al primo avvio se l'utente configurato ha privilegi per farlo. Abilitando l'opzione database_admin_interface spid-testenv2 esporrà una semplice interfaccia di gestione all'indirizzo /admin; è possibile ovviamente usare un qualsiasi tool di gestione esterno.

  1. Per testare spid-testenv2 con un'immagine Docker di PostgreSQL

    docker image pull  postgres:13.2-alpine
    docker run --name some-postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres -d postgres:13.2-alpine
    
  2. Configurare poi in conf/conf.yml la connessione

      db: 'postgresql+psycopg2://postgres:postgres@localhost:5432/postgres'
    
    # ...
    database_admin_interface: true
    

spid-testenv2 utilizza SQLAlchemy. Si può usare qualsivoglia DBMS engine disponibile per SQLAlchemy e non per forza esclusivamente postgres.

Metadata

Il metadata dell'Identity Provider di test è generato automaticamente ed esposto all'URL /metadata. Questo metadata deve essere inserito nella configurazione del proprio Service Provider.

Utenti

Gli utenti di test sono configurati nel file users.json e possono essere aggiunti nella pagina /add-user.

In alternativa è possibile usare un database Postgres configurando l'opzione users_db.

Autori

Questo software è stato sviluppato dal Team per la Trasformazione Digitale, ed è mantenuto con l'ausilio della community di Developers Italia.

Contribuire

Linee Guida

Link utili

spid-testenv2's People

Contributors

alranel avatar archetipo avatar bfabio avatar damikael avatar davidlibrera avatar dependabot[bot] avatar fiblan avatar fmarco avatar ioggstream avatar lucaprete avatar peppelinux avatar sanjioh avatar sebbalex avatar umbros avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spid-testenv2's Issues

valore di AuthnContextClassRef

nelle regole tecniche pag 10 c'è un esempio di valore per RequestedAuthnContext.AuthnContextClassRef:

<samlp:RequestedAuthnContext Comparison="minimum">
  <saml:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:SpidL2</saml:AuthnContextClassRef>
</samlp:RequestedAuthnContext>

se la mando così dà IndexError.

procede solo se AuthnContextClassRef vale ad esempio: https://www.spid.gov.it/SpidL1

Documentare prerequisiti

Nel tentare di eseguire spid-testenv.py su una Ubuntu Trusty LTS ho dovuto installare i seguenti pacchetti:

  • libffi-dev
  • xmlsec1

ed inoltre ho dovuto aggiungere le seguenti righe al requirements.txt:

markupsafe==1.0
passlib==1.7.1
urllib3==1.22
chardet==3.0.4
certifi==2018.1.18
enum==0.4.6
cffi==1.11.5

Ho anche dovuto eseguire pip install --process-dependency-links ctutlz per risolvere il problema descritto in https://stackoverflow.com/questions/47369117/python-cryptography-error-cannot-import-name-certificate-transparency.

Attributi richiesti dal SP

L'elenco degli attributi forniti dall'Identity Provider dipende unicamente dagli attributi settati all'interno dell'IdP stesso invece di essere dipendenti dai campi
<md:RequestedAttribute ...>
del metadata lato SP

Nel caso qui sotto in fase di creazione utente ho lasciato solo il codice fiscale, mentre il metadata del service provider non riporta alcun attributo

screenshot from 2018-06-28 16-51-54

Migliorare validazione firma messaggi

Ho provato ad inviare una AuthnRequest firmata con una chiave diversa da quella presente nel metadata SP caricato nel testenv2. L'errore che ottengo è Verifica della firma del messaggio fallita.

Non è sbagliato, ma dovremmo sforzarci di essere più precisi perché il nostro compito non è solo quello di dire "valido/non valido" ma è quello di aiutare gli sviluppatori a capire dove mettere le mani.

La verifica di una firma può fallire per diversi motivi:

  • tag <ds:Signature> non presente (POST)
  • tag <ds:Signature> con struttura figli non valida (POST)
  • parametri SigAlg e/o Signature non presenti (Redirect)
  • digest checksum non valido
  • uso dell'algoritmo SHA1 (deprecato)
  • firma con chiave pubblica difforme da quella presente nel metadata SP caricato
  • ...altro?

Validazione AuthnRequest

Ho provato ad inviare una AuthnRequest contenente un AssertionConsumerServiceIndex con valore non valido (ho messo 2 ma nel mio metadata SP quell'AssertionConsumerService non c'è), eppure non mi viene mostrato nessun errore e visualizzo il form con username/password.

Analogamente, anche le seguenti prove non mi hanno mostrato alcun errore (come invece mi aspetterei visto che sono valori non validi):

  • AttributeConsumingServiceIndex="11"
  • <saml2:AuthnContextClassRef>https://www.spid.gov.it/SpidL4</saml2:AuthnContextClassRef> (livello 4 di SPID non esistente!)

@fmarco, queste validazioni forse non sono su master?
Non ho fatto altre prove oltre a quelle qui riportate però bisogna verificare ogni singolo elemento dell'XML.

Validazione SSL/TLS

Le Regole Tecniche SPID, così come modificate anche dall'avviso 1, impongono che i Service Provider usino HTTPS protetto con TLS 1.2 e che possano eventualmente supportare SSLv3.0 (tuttavia sconsigliabile) e TLS 1.1. Altri protocolli non sono ammessi.

Per come funziona SAML in realtà non abbiamo chiamate dirette tra IdP e SP (tranne che nel caso del logout IdP-initiated), quindi non c'è un flusso in cui possiamo inserire questa validazione e dobbiamo farla apposta.

Un'idea è quella di eseguire il controllo (su tutti gli endpoint degli SP configurati) ad ogni avvio del testenv. Questo coprirebbe lo scenario 1 descritto in #19.

Per lo scenario 2 (configurazione SP da interfaccia web) la validazione potrebbe avvenire all'atto della creazione di un nuovo SP, con un ulteriore tasto per forzare la rivalidazione.

Per lo scenario 3 (controllo dell'IdP di test via API da parte del portale di onboarding) idem ma via API anziché da interfaccia web.

Cosa ne pensi @umbros?

Verificare se il caricamento remoto dei metadati richieda necessariamente il certificato locale

Per leggere i metadati SP da remoto si indicano un parametro url ed un parametro cert. Quest'ultimo contiene il path ad un file contenente un certificato che per sicurezza deve coincidere con quello letto nei metadati remoti. La documentazione di pysaml2 non è chiarissima al riguardo, ma sembra che questo parametro sia opzionale. Se fosse effettivamente opzionale lo toglierei dalla nostra configurazione di esempio per semplificare la vita agli sviluppatori. Non introduce effettivi problemi di sicurezza perché questo testenv2 non è un reale IdP ma è solo un tool di sviluppo.

Bisogna quindi testare che la lettura remota dei metadati SP funzioni anche in assenza del parametro cert.

Non ritorna all'AssertionConsumerService richiesto

Al termine del login, il testenv2 mi rimanda ad un URL non valido (http://<hostname testenv2>/None) anziché all'AssertionConsumerService del mio Service Provider.

Il metadata del SP contiene:

        <md:AssertionConsumerService  
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"  
            Location="https://localhost/spid-acs"  
            index="0"  
            isDefault="true" /> 

La AuthnRequest è la seguente:

<saml2p:AuthnRequest IssueInstant="2018-06-28T15:49:38Z" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="1" Destination="http://localhost:8088/sso" ID="a0a50e3ee82ecb44365a0b4bec0374e0" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Version="2.0" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" NameQualifier="https://localhost/">https://localhost/</saml2:Issuer><saml2p:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"></saml2p:NameIDPolicy><saml2p:RequestedAuthnContext Comparison="minimum"><saml2:AuthnContextClassRef>https://www.spid.gov.it/SpidL1</saml2:AuthnContextClassRef></saml2p:RequestedAuthnContext></saml2p:AuthnRequest>

Generazione metadata IdP

È necessario esporre un endpoint /metadata con il metadata dell'IdP di test generato automaticamente, per poterlo dare in pasto alle implementazioni dei Service Provider.

Lettura remota metadati SP

Quando si configurano i metadati del SP usando l'elemento remote il testenv2 li legge da una URL remota (è comodo per leggerli direttamente da un endpoint esposto dal SP invece di salvarli in locale).

Sarebbe utile specificare nei commenti del config.yml se questa lettura avvenga al lancio del testenv2, oppure alla prima AuthnRequest, oppure ad ogni singola richiesta (ovvero senza alcun caching). Sarebbe molto comodo se fosse quest'ultimo caso.

Consentire di creare utenti non legati a nessuno specifico Service Provider

Al momento quando si crea un utente tramite il form all'indirizzo

/add-user

tra i campi principali abbiamo, oltre ad utente e password, anche un ID del service provider.
Quando si inseriscono le credenziali l'accesso viene effettuato se il dominio di provenienza (l'SP) corrisponde a quello associato all'utente.

A parer mio questo confonde un po' perché nelle specifiche SPID non c'è nessun vincolo tra credenziali e SP. Inoltre è un campo che tiene conto di porte, protocolli e slash finali, rendendo spesso difficile configurare.

E' possibile rimuoverlo e, per autenticare un utente, accettare utente e password senza controllare il service provider?

Login di base

Il primo passo dovrebbe essere l'implementazione completa di un flusso di login:

  • L'IdP di test riceve la AuthnRequest e la valida;
  • mostra la schermata di login chiedendo login e password;
  • mostra la schermata di autorizzazione al trasferimento degli attributi;
  • confeziona la Response/Assertion e ritorna al Service Provider chiamante.

Questa attività dovrebbe supportare tutte le modalità previste dalle Regole Tecniche SPID, ovvero HTTP-Redirect e HTTP-POST.

Non essendo prevista, in quesa fase iniziale, la diagnostica dettagliata sulla correttezza dei messaggi in ingresso, si può usare anche una libreria pronta come PySAML o onelogin.
Quando, nella fase successiva, si andrà ad implementare anche la diagnostica dettagliata sui messaggi (ad esempio, presenza degli attributi, certificato scaduto ecc.) probabilmente non sarà più possibile usare una libreria esistente perché non offrirebbe la granularità necessaria nella validazione, ma sarà necessario reimplementare direttamente il parsing e la validazione.

Validazione configurazione

La mancata validazione del file di configurazione del testenv deve generare errori comprensibili.

Ad esempio, se avvio il testenv senza aver configurato nessun metadata SP ottengo questo errore poco comprensibile:

Traceback (most recent call last):
  File "spid-testenv.py", line 708, in <module>
    server = IdpServer(app=Flask(__name__), config=config)
  File "spid-testenv.py", line 224, in __init__
    self._prepare_server()
  File "spid-testenv.py", line 341, in _prepare_server
    self.idp_config.load(cnf=self._idp_config())
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/saml2/config.py", line 356, in load
    self.load_complex(cnf, metadata_construction=metadata_construction)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/saml2/config.py", line 296, in load_complex
    self.load_metadata(cnf["metadata"]))
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/saml2/config.py", line 400, in load_metadata
    mds.imp(metadata_conf)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/saml2/mdstore.py", line 918, in imp
    for val in vals:
TypeError: 'NoneType' object is not iterable

Validazione ai fini dell'onboarding

È in corso il lavoro di sviluppo di un portale di onboarding rivolto ai SP per gestire tutti i flussi di adesione alla federazione SPID, modifica dei servizi attivi, pubblicazione dei metadati.
In questo portale è prevista una fase di validazione tecnica dei servizi caricati prima che questi vadano in produzione nella federazione SPID.
A tale scopo bisogna integrare nel portale in corso di sviluppo questo IdP di test in modo da poter raccogliere l'esito dei test.

Di fatto ci saranno tre possibili scenari d'uso per questo IdP di test:

  1. Si potrà eseguire in locale o su proprio server (a fini di studio, sviluppo, demo).
  2. Si potrà usare in versione hosted, liberamente da parte di chiunque (sempre a fini di studio, sviluppo, demo).
  3. Si potrà usare nell'ambito del processo di onboarding al fine di eseguire la validazione di un servizio.

Lo scenario 1, che al momento è già coperto, richiede la massima facilità di configurazione ed esecuzione. Non richiede un'interfaccia web per la creazione degli SP perché è sufficiente chiamare i metadati dal file di configurazione.

Gli scenari 2 e 3 prevedono che l'IdP di test sia erogato centralmente da AgID su propri server, ma con alcune differenze.
Nello scenario 2 serve un'interfaccia web in cui gli utenti (che possono essere anche semplici cittadini) possano configurare uno o più SP. L'accesso a questa interfaccia web potrebbe essere protetto semplicemente con un accesso GitHub in modo che ciascun utente veda i suoi SP. Deve essere a tutti gli effetti una sandbox, senza legami con il portale di onboarding.

Nello scenario 3 invece devono poter operare solo i soggetti già registrati nel portale di onboarding, e devono poter caricare solo i servizi che stanno validando/caricando nello stesso portale di onboarding. Inoltre il portale di onboarding deve avere accesso alle informazioni sui test eseguiti. In questo scenario non serve un'interfaccia web per la configurazione dei SP perché li creerebbe d'ufficio il portale di onboarding usando ad esempio delle API o un database.

Per lo scenario 2 serve un database per la persistenza delle utenze e dei metadati SP creati a runtime. Per lo scenario 3 si potrebbero esporre delle API per caricare i metadati relativi agli SP da validare (non serve che siano associati ad utenze), per modificarli, per cancellarli.
Inoltre bisogna poter abilitare il logging nel database finalizzato ad esporre, sempre tramite API, i risultati dei test in modo che il portale di onboarding possa usarli a fini di validazione.

Le informazioni da loggare (con XML completo ed eventuali warning rilevati dal testenv) sono:

  • AuthnRequest ricevute con successo;
  • LogoutRequest ricevute con successo (SP-initiated logout);
  • LogoutResponse ricevute con successo (IdP-initiated logout).

È importante evitare il conflitto di entityID tra quelli caricati dall'interfaccia pubblica e quelli in validazione: questi ultimi devono sempre avere la precedenza. Probabilmente conviene separare gli scenari 2 e 3 in due installazioni diverse.

Errore in accesso pagina login Spid

Passi eseguiti su browser Chrome e Firefox :

  • Sp richeide metadata a IdP test env
  • Sp esegue chiamata all'endpoint sso configurato con AuthnRequest valida per esecuzione login
  • Idp test env risponde :

Http-Redirect
Request key: None

risulta quindi impossibile eseguire il flusso di login

Validazione metadati SP

I metadati SP dovrebbero essere validati nel momento in cui vengono usati, e quindi gli errori dovrebbero essere mostrati a video insieme agli errori della AuthnRequest. Cosa ne pensi @fmarco?

AuthnRequest: ID non viene validato correttamente

Ho inviato una AuthnRequest il cui attributo ID è 28af4fe5a2923bd47d5d01ea07a255b5.

Questo valore non è valido perché la definizione del tipo XSD NCName prevede che la stringa non possa iniziare con un numero.

Credo che la cosa migliore sia quella validare tutti i messaggi XML in entrata con gli schemi XSD di SAML, in aggiunta agli altri controlli che facciamo.

Validazione login

Quando l'Idp di test riceve la AuthnRequest, dovrebbe validarla secondo le specifiche del protocollo SAML e secondo le Regole Tecniche SPID. Gli errori (ERROR) e gli avvisi (WARNING), oltre ad essere loggati sulla console in STDOUT vanno anche visualizzati a video all'utente.

Esempi di possibili errori sono:

  • AuthnContextClassRef con valore non valido
  • entityID non corrispondente con quelli configurati nell'IdP di test
  • firma mancante
  • firma non valida
  • certificato SP non corrispondente a quello presente nella configurazione
  • AssertionConsumerServiceIndex non presente nella configurazione

Ogni singola prescrizione delle regole SAML e delle regole SPID deve essere tradotta in una regola di validazione.

Il codice deve essere scritto in modo che sia facile e pulito aggiungere altre validazioni dopo l'impementazione iniziale.

Risposte custom

Per consentire agli sviluppatori di testare la sicurezza delle proprie implementazioni è necessario verificare che effettuino il parsing e la validazione delle Response da parte dell'IdP in modo corretto e completo, senza fidarsi dell'input.
Bisogna quindi fare in modo che l'IdP di test possa restituire delle Response e delle asserzioni con errori che il Service Provider deve intercettare.

Una buona lista di circostanza da testare è quella riportata in questa checklist sotto la voce "Response/Assertion parsing": ciascuna di quelle problematiche deve poter essere testabile. Anche se la lista è un po' lunga credo sia utile consentire di testarle tutte una per una.

Mi immagino che nella schermata di login dell'IdP ci sia anche un riquadro aggiuntivo con cui si possa personalizzare la Response scegliendo una di queste anomalie (ad esempio: manda una firma sbagliata, ometti un attributo ecc.).

Errore dopo l'invio delle credenziali

Sto testando il branch feature/issue-3-validazione-login.

Dopo aver inserito le credenziali dell'utente nella schermata di login, ottengo questo errore:

127.0.0.1 - - [07/Jul/2018 12:04:09] "POST /login HTTP/1.1" 500 -
Traceback (most recent call last):
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "/Users/alranel/Documents/git/TD/spid-testenv2/env/lib/python2.7/site-packages/flask/app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/Users/alranel/Documents/git/TD/spid-testenv2/spid-testenv.py", line 998, in login
    'lines':  escape(prettify_xml(response)).splitlines(),
  File "/Users/alranel/Documents/git/TD/spid-testenv2/spid-testenv.py", line 206, in prettify_xml
    msg = etree.tostring(etree.XML(msg), pretty_print=True)
  File "src/lxml/etree.pyx", line 3192, in lxml.etree.XML

  File "src/lxml/parser.pxi", line 1872, in lxml.etree._parseMemoryDocument

ValueError: Unicode strings with encoding declaration are not supported. Please use bytes input or XML fragments without declaration.

Service Provider non configurato

Quando si invia una AuthnRequest contenente un Issuer che non corrisponde a nessun Service Provider configurato nel testenv si ottiene questa schermata:

Errore

Mi aspetterei invece di vedere la stessa schermata che mostra gli errori di validazione della AuthnRequest, con spiegato il problema.

HTTPS

Anche se le regole tecniche SPID impongono che tutti gli Identity Provider operino solo in HTTPS, al fine di far girare l'ambiente in locale credo sia molto utile consentire anche di farlo girare in HTTP semplice per non avere problemi di certificati (non ottenibili per localhost). Nella configurazione ci vuole quindi un parametro anche per http/https.

Supporto multi-utente

In questa issue tracciamo le caratteristiche del supporto multi-utente previsto per la seconda fase di sviluppo. Nella prima fase ci focalizziamo sulla realizzazione di un ambiente di test che gli sviluppatori possano eseguire in locale oppure installare su un proprio server (milestone 1.0). Poi passeremo all'aggiunta delle funzionalità necessarie all'erogazione centralizzata dell'ambiente di test.

Ciascun utente dovrà poter entrare e creare/modificare/cancellare i propri Service Provider.

Funzionalità necessarie:

  • login utenti (SSO con GitHub?)
  • interfaccia di backoffice per amministrazione
  • database per utenti e relative configurazioni Service Provider

Da valutare:

  • logging messaggi SAML e loro visualizzazione ordinata nel pannello di backoffice?
  • interfaccia di amministrazione?

Documentare creazione chiave

Nel README bisogna riportare il seguente comando che consente di creare chiave e certificato per l'IdP:

openssl req -x509 -nodes -sha256 -days 365 -newkey rsa:2048 -keyout idp.key -out idp.crt

Consentire avvio in modalità headless (con login automatico)

Una richiesta pervenuta da chi sviluppa applicazioni mobili che hanno il login SPID consiste nel poter usare il testenv2 nei test CI. In quei casi infatti i framework di testing non sono in grado di interagire con il contenuto delle webview e quindi non possono eseguire automaticamente il login SPID proseguendo poi con i successivi test nell'applicazione.

Potremmo offrire questa funzionalità in testenv2 aggiungendo un parametro auto_login nella configurazione, il cui valore è uno username. In questo modo alla ricezione di una AuthnRequest il testenv2 risponderà automaticamente con l'asserzione di login saltando qualsiasi richiesta interattiva. In caso di errori nella richiesta basterà scriverli nel log e a console come già facciamo.

Errore: mancata lettura dell'attributo AssertionConsumerServiceIndex

Questa è l'AuthnRequest:

<saml2p:AuthnRequest IssueInstant="2018-07-10T09:11:22Z" ID="d8dc862865f1b742d475b7f30f7fc94b" Version="2.0" Destination="http://localhost:8088/sso" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceIndex="0" AttributeConsumingServiceIndex="1" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" NameQualifier="https://localhost/">https://localhost/</saml2:Issuer><saml2p:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"></saml2p:NameIDPolicy><saml2p:RequestedAuthnContext Comparison="minimum"><saml2:AuthnContextClassRef>https://www.spid.gov.it/SpidL1</saml2:AuthnContextClassRef></saml2p:RequestedAuthnContext></saml2p:AuthnRequest>

Questo è uno stralcio del metadata SP:

        <md:AssertionConsumerService
            Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
            Location="https://localhost/spid-acs"
            index="0"
            isDefault="true" />

Ottengo il seguente errore:

screen shot 2018-07-10 at 11 18 49

La password degli utenti in users.json è cifrata

Il file users.json contiene le password cifrate. Ad esempio:

{
    "mario": {
        "pwd": "$6$rounds=656000$xCdRvCbE6Y5//1y3$KJQubY/lL9ZUua22WSKo2Xh6qVSZTRR5cR/AOYwFgCo9E49bbEwTuSFCPWUMsErd4pUUs3yA94GTBezbxfJtU1", 
        "sp": "https://localhost/", 
        "attrs": {
            "name": "Mario", 
            "companyName": "Rossi srl", 
            "fiscalNumber": "RSOMRO70M20H501X", 
            "familyName": "Rossi", 
            "spidCode": "334-4ke-34-sa", 
            "dateOfBirth": "1970-01-01", 
            "gender": "M", 
            "placeOfBirth": "Roma", 
            "countryOfBirth": "Italy"
        }
    }
}

Credo sia meglio averle in chiaro. Questo non è un vero IdP ma è solo un ambiente di test, e se non le mostriamo in chiaro l'utente si deve ricordare cosa aveva impostato (e peraltro non potremmo generare utenti di default). Invece è utile consultare il file users.json per vedere le password utilizzabili.

incongruenza nome certificato generato da comando openssl in README

L'esperienza del newbie che clona il repo sarebbe ancora più scorrevole se il nome del certificato generato dal comando openssl (idp.crt) nel README fosse congruente con quello di default in config.yaml.example (idp.cert).

Propongo di cambiare il secondo a idp.crt. PR ?

Docker

Bisogna creare un'immagine Docker, collegata via CI con questo repository, per facilitare l'installazione.

IssueInstant deve validare timestamp ISO 8601 con millisecondi

Questo attributo è un timestamp valido ISO 8601 ma il tool non lo valida (errore la data non è in formato UTC):

IssueInstant="2018-07-11T07:28:57.935Z"

Tra l'altro l'errore e' di per se incorretto perche' dice che non e' in UTC, quando invece lo e', il problema e' che il timestamp viene interpretato con il codice:

datetime.strptime(date, '%Y-%m-%dT%H:%M:%SZ')

che non accetta un timestamp con millisecondi.

Architettura di base e file di configurazione

L'ambiente di test dovrebbe essere il più possibile self-contained, ed eseguibile da shell come segue:

./spid-testenv.py

La configurazione dovrebbe avvenire in un file YAML contenente le seguenti informazioni:

  • Configurazione dell'IdP:
    • entityID
    • path file chiave privata
    • path file certificato
    • ...altre informazioni necessarie al funzionamento dell'IdP
  • Configurazione dei Service Provider da abilitare (più di uno), per ciascuno:
    • entityID
    • path file certificato
    • AssertionConsumerService
    • AttributeConsumingService
    • ...altre informazioni necessarie al riconoscimento del Service Provider
  • Utenti di test, per ciascuno:
    • login/password
    • attributi primari e secondari definiti dalle regole SPID (codice fiscale, telefono ecc.)

Errori schermata di login

Nel branch feature/issue-3-validazione-login, la schermata di login non funziona correttamente perché:

  • il campo password è in chiaro
  • il tasto "Invia" non si abilita dopo aver inserito le credenziali quindi non si può proseguire

Metadata IdP

Nei metadati IdP esposti dal testenv all'URL /metadata vedo che l'attributo WantAuthnRequestsSigned è impostato a false. Dovrebbe essere impostato a true in modo da simulare più correttamente cosa avviene in produzione.

<ContactPerson> non è previsto dalle Regole Tecniche SPID, mentre <Organization> è facoltativo quindi non avendo dati utili per configurarlo direi che possiamo toglierlo.

Non usare SHA-1

Secondo le regole tecniche i messaggi vanno firmati con algoritmo SHA-256 o superiore. Nel codice di spid-testenv.py vedo:

SIGN_ALG = ds.SIG_RSA_SHA1
DIGEST_ALG = ds.DIGEST_SHA1

Messaggi più chiari per validazione configurazione

Bisogna fare in modo che la validazione del file di configurazione produca messaggi semplici e comprensibili. Ad esempio mi esce questo e non mi fa capire dove intervenire:

$ python spid-testenv.py
Traceback (most recent call last):
  File "spid-testenv.py", line 671, in <module>
    server = IdpServer(app=Flask(__name__), config=config)
  File "spid-testenv.py", line 208, in __init__
    self._prepare_server()
  File "spid-testenv.py", line 300, in _prepare_server
    self._setup_app_routes()
  File "spid-testenv.py", line 282, in _setup_app_routes
    self.app.add_url_rule(_url, '{}_{}'.format(ep_type, _binding), getattr(self, ep_type), methods=['GET',])
  File "/home/staff/spid-testenv2/env/local/lib/python2.7/site-packages/flask/app.py", line 64, in wrapper_func
    return f(self, *args, **kwargs)
  File "/home/staff/spid-testenv2/env/local/lib/python2.7/site-packages/flask/app.py", line 1043, in add_url_rule
    rule = self.url_rule_class(rule, methods=methods, **options)
  File "/home/staff/spid-testenv2/env/local/lib/python2.7/site-packages/werkzeug/routing.py", line 603, in __init__
    raise ValueError('urls must start with a leading slash')
ValueError: urls must start with a leading slash

Il tasto "Annulla" non esegue nessuna azione

In fase di login, se l'utente preme il tasto "Annulla" anziché completare il login, non succede nulla.

Invece si dovrebbe ritornare all'AssertionConsumerService del Service Provider chiamante passando il codice di errore specificato dalla tabella errori (vedere sito AgID, c'è la tabella errori).

Log dell'XML decodificato

Alla ricezione di una AuthnRequest, il log del testenv2 in STDOUT mostra la richiesta solo nel formato URL-encoded:

--------------------------------------------------------------------------------
INFO in spid-testenv [spid-testenv.py:433]:
Http-Redirect
--------------------------------------------------------------------------------
127.0.0.1 - - [28/Jun/2018 16:53:52] "GET /sso?SAMLRequest=nVPLbsIwELz3KyzfISRASS1AoqCqSH2kBfXQm5M4sFJip94Nj7%2BvCY9SFXHoxYo2M7szs3YfZZEHpRhVtNTv6qtSSGziDtCSwOgBXxKVwvNyk8h8aZBE2ApDD9FwFllDJjH5PegU9GLAK6uFkQgotCwUCkrEbPT8JIJmS8R7EIrH%2BTxqRK%2BzOWfTyYDHcdYL0lAmSdy7i7O0e9tpx924l7pKr%2BMHnH0oi7UU18ZxECs11UhSkyu1%2FLDRum0E4dzviG5bdINPzkaIyu7kj43GqlB2puwKEkdL1WbAXZcRkYW4IrVHOGG%2FIT5nmyLXKPb5XLdWHnL4xblOkUeFfNiv4aL2ZdmLg75VMocMlN2nj%2Bfxe5w9GFtIut5%2FV4G0kdVQoTQBbfnwb7e%2Bdz79oKUUOxXTSWRySLb%2FmEdWagQ31bnzLrQ8zTncOJXW98%2FtgtSG2NgUpbSAu5271UBRFaeYzoHj3MX4rrKTr%2FV63cQS0ubCrJpA3sx9P%2FlHjxepJ30Xpfz8Pn8gw5tv&SigAlg=http%3A%2F%2Fwww.w3.org%2F2000%2F09%2Fxmldsig%23rsa-sha1&Signature=YnX5xkhqp3XrB%2BWxI5EK%2B%2FEmN2mGEw%2FJ3vbRZTsmHUC7xfHOE0DSh84Ch7Yp9WSnLKXNewNyvC9GNZHQfHEMpAmXJ5ZFBG6%2BGgomZRKuMrb7ecHPuCUSfYVKiOP4NN%2FUOOi8ME7zuAddC2OPN%2FmMQD9SDeGX8Q8rcmLvQU3dI7yFRNA%2FHp3fIoSA%2FBsIzcW8zOafP46oqbIEXAzmqMZU6A%2FxkxOqLMpBt%2BwlM5Yp00L%2F8vhTAGf%2BASY%2BmR%2FvXwA2i1g%2BLwPrjgUW3xtAvMfOFP0iHO7K5HKea9PqD%2Fq4gdfC3gEm6pcV5Ymky4oblWkhaYDXR1COvFAqjokrM8TrdQ%3D%3D HTTP/1.1" 302 -
--------------------------------------------------------------------------------
DEBUG in spid-testenv [spid-testenv.py:545]:
Request key: 6e6f317fd0874faaf1f9be80861837e425e0529b
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
DEBUG in spid-testenv [spid-testenv.py:345]:
spid level 1 - verifica (False)
--------------------------------------------------------------------------------

Sarebbe utile avere anche l'XML decodificato.

(Su idp.log non viene scritto nulla)

Generazione metadata SP

Ad oggi si può usare l'interfaccia del vecchio sistema di test: https://idp.spid.gov.it:8080/

Potremmo:

  1. fornire un metadata di esempio nel repository, con i commenti che guidano l'utente nella sua compilazione
  2. fornire anche qui un'interfaccia web per la compilazione guidata

L'interfaccia in questione sarebbe in parte condivisa con quella necessaria per lo scenario n. 2 descritto in #19.

Documentare file di configurazione

Nel file di configurazione dobbiamo aggiungere dei commenti prima di ogni opzione per spiegare come va configurata.
Di seguito alcune cose non immediatamente chiare e che potrebbero confondere lo sviluppatore:

  • entityid: non è chiaro se sia quello dell'IdP o del SP da testare
  • https: non è chiaro se si riferisca all'IdP di test o in qualche modo al SP (ovviamente si riferisce al primo ma, ripeto, ad una prima lettura da parte di una persona non familiare con l'architettura di SPID, potrebbe sollevare l'interrogativo)
  • metadata: non è chiaro che si tratti dei SP e non è chiara la distinzione tra local e remote

Inoltre sostituirei i vari placeholder come <absolute-path> con dei valori di default (ad es. idp.key e idp.crt) in modo da diminuire l'effort di configurazione richiesto tra il git clone e l'esecuzione. Mi aspetto che provando ad eseguire il testenv mi venga mostrato un errore friendly come "Impossibile leggere la chiave privata dell'IdP di test: File 'idp.key' not found".

In realtà credo che possiamo tranquillamente distribuire una chiave privata e un certificato di default già nel repository.

AuthnRequest: non viene verificata la presenza della firma

Se mando AuthnRequest non firmate, il testenv2 le accetta senza riportare nessun errore. Esempio:

<saml2p:AuthnRequest IssueInstant="2018-07-12T14:37:02Z" Version="2.0" ID="28af4fe5a2923bd47d5d01ea07a255b5" Destination="http://localhost:8088/sso" AttributeConsumingServiceIndex="0" AssertionConsumerServiceIndex="0" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"><saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity" NameQualifier="https://localhost/">https://localhost/</saml2:Issuer><saml2p:NameIDPolicy Format="urn:oasis:names:tc:SAML:2.0:nameid-format:transient"></saml2p:NameIDPolicy><saml2p:RequestedAuthnContext Comparison="minimum"><saml2:AuthnContextClassRef>https://www.spid.gov.it/SpidL1</saml2:AuthnContextClassRef></saml2p:RequestedAuthnContext></saml2p:AuthnRequest>

Migliorare errore in caso di assenza del file users.json

Attualmente se il file users.json non esiste esce:

$ python spid-testenv.py
Traceback (most recent call last):
  File "spid-testenv.py", line 671, in <module>
    server = IdpServer(app=Flask(__name__), config=config)
  File "spid-testenv.py", line 202, in __init__
    self.user_manager = JsonUserManager()
  File "spid-testenv.py", line 133, in __init__
    self._load()
  File "spid-testenv.py", line 129, in _load
    except FileNotFoundError:
NameError: global name 'FileNotFoundError' is not defined

Questo errore esce anche nel caso in cui esista ma è malformato (ad esempio se è vuoto).

La cosa migliore sarebbe quella di (tentare di) creare il file automaticamente, se non esiste, e di restituire un errore nel caso in cui non si possa creare oppure esista ma sia non valido.

servizio operato dietro proxy: entityID e locations in metadata ?

supponiamo di avere questa config:

  • port: 8088
  • host: 127.0.0.1 in modo che ascolti solo su localhost
  • hostname: idp.simevo.com
  • proxy (es. nginx) che lo gira sulla porta 443 es. https://idp.simevo.com (vantaggi: posso gestire i certificati SSL con certbot / letsencrypt, minimizzazione delle porte aperte, eventuale caching ...)

problema: il metadata generato dalla route /metadata in entityID, locations si riferisce a hostname:port del servizio python:

<ns0:EntityDescriptor
  xmlns:ns0="urn:oasis:names:tc:SAML:2.0:metadata"
  xmlns:ns1="http://www.w3.org/2000/09/xmldsig#"
  entityID="http://idp.simevo.com:8088">

dovrebbe essere:

  entityID="https://idp.simevo.com">

come la risolviamo ?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.