Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Navitabs
rootReferenceimplementering (NAP) - Leverancebeskrivelse
includeroottrue

Table of Contents

Introduktion

I NAP optræder både self hosted services samt INSP services.

Denne guide er primært målrettet til anvendere, som skal implementere en ny afprøvningsløsning til INSP og har til formål, at give eksempler på kode og opsætning af web applikation og facade til NAP. 


Warning
NAP projektet er ikke aktivt og NAP er derfor ikke pt. under support og vedligehold.
NAP er ikke opdateret til at understøtte MitID/NemLogin3 og det er derfor ikke længere
muligt at anvende den nuværende version på testmiljøerne.
En evt. genoptagelse af NAP projektet vil afhænge af konkret efterspørgsel.
Kontakt evt. Sundhedsdatastyrelsen ved interesse i NAP.


Table of Contents

Introduktion

Denne guide er primært målrettet til anvendere, som skal implementere en ny afprøvningsløsning og har til formål, at give eksempler på kode og opsætning af web applikation og facade til NAP. 

Guiden beskriver en web applikation som bruger en backend, som Guiden beskriver en web applikation som bruger en backend, som interagerer med Dokumentdelingsservice (DDS), og som anvender oiosaml.java 2.21 (https://www.digitaliser.dk/resource/5359238) som sikkerhed- og autentikationslaget jf. Sikkerhedsarkitektur for iNSP løsninger.

Der er beskrevet instruktioner til opsætning af et web projekt og en backend, som overholder givne NSP standarder (Husregler for webløsninger og https://www.nspop.dk/display/public/web/Husregler+for+udvikling+til+NSP)

Guiden indeholder eksempler på kodeimplementationer, men ellers er projekterne tilgængelig på svn.nspop.dk og kan bruges som inspiration til et afprøvningsprojekt. 

nap-reference-web

nap-reference-facade 

Sammenhæng med øvrige dokumenter

Dette dokument er en del af den samlede dokumentation for NAP Reference implemenationimplementation.

Dokumentet er udformet, så det i videst muligt omfang opfylder sit formål uafhængigt af de øvrige dokumenter.

...

 NAP Ref.Impl. Opbygning

Nap-reference består references består af en java backend service (nap-reference-facade) samt en front for backend (nap-reference-web), som anvender bygget med angular fremworket frameworket og som anvender version 1.0.0 af nap-typescript-sdk og nap-angular-sdk.

Applikationen kan køres indlejrede i et værtssytem (eks. nap-Applikationens hovedfunktionalitet er at vise aftaledokumenter for den patient, der er i kontekst i det værtssystem, den er indlejret i (f.eks. nap-java-host). Derfor er funktionalitet i applikationen begrænset, hvis den åbnes uden at være framet.

Hvis applikationen køres indlejret i et værtssystem som understøtter sikker browser opstart og tilføjer , hvori der tilføjes en NAP Bridge på global scope . Således af indlejrede systeme kan kommunikation mellem host og nap-reference ske i gennem NAP SDK - Guide til anvendere.

Denne kommunikation giver muligheden for overførelse af patientkonteksten og sessionsfejl  til det indlejret værtssysteminformationer som patientkontekst og sessionsfejl mellem det indlejrede system og værtssystemet.

Når nap-reference får overført en patientkontekst kaldes facaden med det cpr nummer sam samt en SOR værdi tilføjet i en "X-OrganizationSor" header (denne værdi er hardcoded i dette tilfælde, da den ikke kan læses ud af SOSI IDkortet)". Facaden validere kaldet og viderestiller til Dokumentdelingsservice (DDS)..

Nap-reference-facade sørger for at tjekke at  står for at validere kaldet og hente relevante aftaledokumenter (via anvendelse af org.openehealth.ipf.commons) for den valgte patient. For at formatere aftaledokumenterne fra XML til objekter, bruges biblioteket "dk.s4.hl7.builders".

Før objekterne aftaledokumenterne returneres, bliver kun relevant information sendt tilbage til information, der anvendes nap-reference-web udtrukket

Nap-reference-web

Nap-reference-web er bygget med Angular version 9 og fungerer som illustration på:

  1. Projekt opsætning overholdende https://www.nspop.dk/display/public/web/Husregler+for+udvikling+til+NSP.
  2. Brug af NAP SDK NAP SDK - Guide til anvendere

  3. GUI for NAP Projekter.

Applikationens hovedfunktionalitet er at vise aftaledokumenter for den patient, der er i kontekst i det værtssystem, den er indlejret i (f.eks. nap-java-host). Derfor af funktionalitet i applikationen begrænset, hvis den åbnes uden at være framet.

Opsætning og struktur af et angular projekt

Nap-reference-web er en front end til nap-reference-facade og deployes i dennes kontekst-rod i byggepipelinen.

Se NAP Ref.Impl. - Installationsvejledning for mere information omkring Jenkins pibeline.

Opsætning og struktur af et angular projekt

Komponenter

Angular frameworket lægger sig op af MVC (model-view-controller) design mønstremønsteret, og dette er implementeret i nap-reference-web.

Der er simple komponenter til at visning af aftaler, hjælpinformationhjælpeinformationer, fejl osv. 

Services

Services håndterer forretningslogikken.

...

Nap-reference-web benytter sig af en json konfigurationsfil (assets/configurtation.json), som loades via configuration-configurations servicen.

Denne konfigurationsfil bliver således overskrevet i de forskellige docker-compose setups.

For yderligere information omkring konfiuration henvises til NAP Ref.Impl. - Guide til udviklere.

Da NSP web applikationer og services kan blive deployet på vilkårlige url'er, er det vigtigt at applikationen fungerer med relative paths. Dette er opnået ved hashrouting.

Der findes 2 konfigurationer en til dev (configurations-dev.json) samt en til releases (configurations.json) som adskiller sig i serverens url.

Configurations-dev.json anvendes til når nap-refence-web er under udvikling og køres via ng serve (så der kommer live reloading af kodeændringer), hvor configurations.json anvendes når nap-reference-web er pakket og deployet sammen med nap-reference-facade i kontekstroden.

For yderligere information omkring konfiguration henvises til NAP Ref.Impl. - Driftsvejledning.

Da INSP web applikationer og services kan blive deployet på vilkårlige url'er, og skal kunne loade ressourcer relativt til, hvor de er deployet, er det vigtigt at applikationen fungerer med relative paths.

Dette er opnået ved hashrouting.

Code Block
Code Block
@NgModule({
  imports: [RouterModule.forRoot(routes, { useHash: true })],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Debugging

Du kan læse mere på https://angular.io/guide/router

Debugging

Hvis Nap-reference-web åbnes i browser kan den Hvis Nap-reference-web åbnes i browser kan den debugges med almindelige developer tools.

Hvis den åbnes i nap-java-host, er den eneste debugging mulighed . console.konsol logs().

Testing

Nap-reference-web benytter sig af testframeworket Karma, da det kommer default med angular. Karma bruger Istanbul til at generere test-coverage rapporter. Testene køres i en headless chromium browser.

...

nap-reference-web gør brug af det udstillede interface i nap-angular-sdk, og nedenfor er der eksempler på hvordan det er blevet brugt. 

Security (SAML logout samt Session-

...

close NapEvent)

Der er lavet en AuthService, som sørger for at håndtere sessioner.

Nedenstående funktionalitet findes i src/app/services/auth.service.ts.

I constructoren Auth service lytter man på indkommende beskeder fra NAP SDK'et, og filtrerer beskeder, således det der kun er reageres på beskeder af typen SessionOpen og SessionClose som håndteres. I constructoren bliver der ligeledes sendt en besked til NAP SDK'et om man gerne vil anmode om at starte en ny trusted session (SessionOpen), hvor brugeres SAMLAssertion overføres.

Hvis en bruger ønsker at blive logget ud Hvis der logges ud, sendes en besked med SessionClose , og brugerens SAMLAssertion slettes.

Code Block
languagejs
export class AuthService {
 private sessionMessageSubject = new BehaviorSubject<NAPMessage | undefined>(
    undefined
  );

  /**
   * The current session
   *
   * @memberof AuthService
   */
  public session$ = this.sessionMessageSubject.asObservable();

  constructor(private napSDK: NapAngularService) {
    this.napSDK.incoming$
      .pipe(
        filterEvents([
          NAPEventCatalogue.v1.SessionOpen,
          NAPEventCatalogue.v1.SessionClose,
        ])
      )
      .subscribe((napMessage) => {
        if (napMessage.event.type === NAPEventCatalogue.v1.SessionOpen) {
          this.sessionMessageSubject.next(napMessage);
        } else {
          this.sessionMessageSubject.next(undefined);
        }
      });

    const napMsg: NAPMessage = {
      date: new Date().toISOString(),
      id: UUID(),
      event: { type: NAPEventCatalogue.v1.SessionOpen },
    };

    // Ask for the SAMLassertion in host
    this.napSDK.sendMessage(napMsg);
  }

  /**
   * Trigger logout event
   */
  logout(): void {
    this.sessionMessageSubject.next(undefined);
    const napMsg: NAPMessage = {
      date: new Date().toISOString(),
      id: UUID(),
      event: { type: NAPEventCatalogue.v1.SessionClose },
    };
    this.napSDK.sendMessage(napMsg);
  }
}

Patient-Open og Patient-close

I komponenten appointment (src/app/appoinment/), vises de forskellige aftaler for den givne patient.

For at få den nuværende patient, sendes en besked med typen PatientOpen igennem SDK'et. Dette gøres i constructoren.

Herefter lyttes der på indkommende beskeder med typen PatientOpen. Der gøres brug af en hjælpe metode i nap-typescript-sdk, FHIRValueGetter.getPatientInfo(), som konvertere den indkommende besked til et objekt som indeholder patient oplysningerne.  

Hvis den nuværende patient skal lukkes, set fra det indlejrede systems perspektiv, sendes der en besked med typen PatientClose.

Code Block
  constructor(
    private napSDK: NapAngularService,
    private appointmentService: AppointmentService,
    private authService: AuthService
  ) {
    const napMsg: NAPMessage = {
      date: new Date().toISOString(),
      id: UUID(),
      event: {
        type: NAPEventCatalogue.v1.PatientOpen,
      },
    };
    this.napSDK.sendMessage(napMsg);
  }

  public currentPatient$: Observable<NAPPatientInfo | undefined> = this.napSDK.incoming$.pipe(
    filterEvent(NAPEventCatalogue.v1.PatientOpen),
    map(message => FHIRValueGetter.getPatientInfo(message))
  );

Session-Error

I appointment service (src/app/service/appointment.service.ts) vises et eksempel på fejlhåndtering som skal gå igennem NAP SDK'et.

createNapErrorMessage() opbygger en besked med typen SessionError, som indikerer der er sket en uventet fejl i projektet som skal sendes til værtssystemet og vice versa.

Fejlen bliver sendt hvis kaldet til getAppointments() fejler.

Code Block
  private appointmentEndPoint$ = this.configurationService.fetch(
    config => config.appointmentsEndpoint
  );
  private serverUrl = this.configurationService.fetch(config => config.serverUrl);
  
  private createNapErrorMessage(error: GenericAppError): NAPMessage {
    return {
      date: new Date().toISOString(),
      id: UUID(),
      event: {
        type: NAPEventCatalogue.v1.SessionError,
        context: [
          {
            resource: {
              resourceType: FHIRValueSetter.FHIRResourceType.Basic,
              code: {
                coding: [
                  {
                    code: NAPEventCatalogue.v1.SessionError,
                    system: FHIRValueSetter.FHIRSystem.NAP,
                  },
                ],
              },
              identifier: [
                {
                  system: FHIRValueSetter.FHIRIdentifierSystem.NAPErrorMessage,
                  value: error.innerError?.message ? error.innerError?.message : '',
                },
                {
                  system: FHIRValueSetter.FHIRIdentifierSystem.NAPErrorDescription,
                  value: error.errorMessage,
                },
              ],
            },
          },
        ],
      }
    }
  }

  public getAppointments(patientIdentifier: string | undefined): Observable<any[] | undefined> {
    return combineLatest(
      [
        this.serverUrl,
        this.appointmentEndPoint$,
        this.serviceActivator
      ]
    ).pipe(
      switchMap(([serverUrl, endpointPath, _]) => this.http.get<any[]>(serverUrl + endpointPath + '/' + patientIdentifier)),
      catchError(error => {
        this.errorService.postError(error);
        this.napSDK.sendMessage(this.createNapErrorMessage(error)); // indicate to the host that something went wrong
        return of(undefined);
      }),
    );
  }

Nap-reference-facade

Nap-reference-facade er bygget med java 8 og fungerer som illustration på:

  1. Projekt opsætning overholdende https://www.nspop.dk/display/public/web/Husregler+for+udvikling+til+NSP.
  2. Sikkerhed i NAP kontekst Sikkerhedsarkitektur for iNSP løsninger
  3. Brug af Dokumentdelingsservice (DDS)

Opsætning og struktur 

Dependencies

Dependencies er hentet fra https://nexus.nspop.dk/nexus/content/groups/public og de dependencies, som stilles til rådighed af wildfly8 platformen er angivet med scope provided.

For at kunne kalde Dokumentdelingsservice (DDS) er følgende dependencies anvendt:

...

dk.sosi.seal

...

Java-bibliotek til understøttelse af "Den Gode Webservice" og validering af SAMLassertion, se http://digitaliser.dk/group/374971

...

dk.s4.hl7.builders

...

Dansk profileret XML converter af hl7 clinical documents

...

org.openehealth.ipf.commons

...

fra den kontekst brugeren har igangsat eventet.

Det betyder at eventet både kan sendes fra indlejret system til værtsystem og omvendt.

Når brugeren skal logges ud skal browseren flyttes til saml/Logout på den server, der holder sessionen. 

Se auth.service.ts i https://svn.nspop.dk/src/components/nap/nap-reference-web/trunk/src/app/services/


Patient-Open og Patient-close

I komponenten appointment (src/app/appointment/), vises de forskellige aftaler for den givne patient.

For at få den nuværende patient, sendes en besked med typen PatientOpen igennem SDK'et. Dette gøres i constructoren.

Herefter lyttes der på indkommende beskeder med typen PatientOpen. Der gøres brug af en hjælpe metode i nap-typescript-sdk, FHIRValueGetter.getPatientInfo(), som konvertere den indkommende besked til et objekt som indeholder patient oplysningerne.  

Hvis den nuværende patient skal lukkes, set fra det indlejrede systems perspektiv, sendes der en besked med typen PatientClose.

se appointment.component.ts i https://svn.nspop.dk/src/components/nap/nap-reference-web/trunk/src/app/appointment/

Session-Error

I appointment service (src/app/service/appointment.service.ts) vises et eksempel på fejlhåndtering som skal gå igennem NAP SDK'et.

createNapErrorMessage() opbygger en besked med typen SessionError, som indikerer der er sket en uventet fejl i projektet som skal sendes til værtssystemet og vice versa.

Fejlen bliver sendt hvis kaldet til getAppointments() fejler.

se appointment.service.ts i https://svn.nspop.dk/src/components/nap/nap-reference-web/trunk/src/app/services/

Nap-reference-facade

Nap-reference-facade er backend til nap-reference-web.

Nap-reference-facade er bygget med java 8 og fungerer som illustration på:

  1. Projekt opsætning overholdende https://www.nspop.dk/display/public/web/Husregler+for+udvikling+til+NSP.
  2. Sikkerhed i NAP kontekst Sikkerhedsarkitektur for iNSP løsninger
  3. Brug af Dokumentdelingsservice (DDS)

I bygge processen lægges nap-reference-web ind i src/main/webapp, hvorfra disse er tilgængelig i kontekstroden. Se NAP Ref.Impl. - Installationsvejledning for mere information omkring Jenkins pibeline.

Opsætning og struktur 

Dependencies

Dependencies er hentet fra https://nexus.nspop.dk/nexus/content/groups/public og de dependencies, som stilles til rådighed af wildfly8 platformen er angivet med scope provided.

For at kunne kalde Dokumentdelingsservice (DDS) er følgende dependencies anvendt:

dk.sosi.seal

Java-bibliotek til understøttelse af "Den Gode Webservice" og validering af SAMLassertion, se http://digitaliser.dk/group/374971

dk.s4.hl7.builders

Dansk profileret XML converter af hl7 clinical documents

org.openehealth.ipf.commons
Dansk profileret bibliotek til Cross Enterprise Document sharing. Bruges til at integrere Dokumentdelingsservice (DDS). Du kan læse mere på https://github.com/KvalitetsIT/aftaledeling/tree/master/dgws-eksempel/src/main/java/dk/sds/appointment.
dk.digst.oiosaml2.java
Danske profilering af OASIS SAML 2.0 standarden.

Konfiguration

Alt konfiguration foregår ved at loade filer fra et selv-defineret wildfly modul og ind i classpath under deployment.
Dette gør wildfly ved at modulet defineres i jboss-deployment-structure.xml. 

Der findes flere information om hvordan nap-refernece-facade konfigureres på NAP Ref.Impl. - Driftsvejledning.

Testing

Testene bliver eksekveret af maven-surefire-plugin med test frameworket junit. Test coverage bliver målt af Jacoco.

Dokumentation

Kodedokumentationen overholder javadoc standarden således dette kan generes med et værktøj fra fx den IDE man bruger.

Sikkerhed

Sikkerhedsarkitekturen følger det skitserede i Sikkerhedsarkitektur for iNSP løsninger. Dermed er der åben for mulighederne for integration til udbredte procedure Sikker browser opstart, samt understøtter tilslutning til Nemlogin.

dk.digst.oiosaml2.java

Anvendes som autentifikationsfilter. For at et OIOSAML kan loades skal oiosaml-kofigurationer være placeret i oiosaml.home under deployment. 
Indstilling af oisaml.home og konifiguration af OIOSAML sker som vist i compose/development/docker-compose.yml med environmental variable og ved mounte konfigurationsfiler ind.

For registreing af SPFIlter se SpringLoader.Java i https://svn.nspop.dk/src/components/nap/nap-reference-facade/trunk/src/main/java/dk/sds/nsp/nap/reference/facade/

OIOSAML sørger for at validere den assertion der kommer fra en given iDP (https://www.nspop.dk/display/public/web/Sikkerhedsservices+%28STS%29+-+Leverancebeskrivelse  i dette tilfælde) og tilføjer denne til brugerens session.

Der findes eksempel på hvordan OIOSAML konfigureres på NAP Ref.Impl. - Guide til udviklere.

SOSI idkort fra Samlassertion

Desuden er der implementeret et service specifikt autentifikationsfilter, der udtrækker det IDkort, som er indlejret i en SAMLassertion så det kan bruges i DGWS kald. 

Se AuthFilter.java i https://svn.nspop.dk/src/components/nap/nap-reference-facade/trunk/src/main/java/dk/sds/nsp/nap/reference/facade/filters/AuthFilter.java


dk.sosi.seal

Seal anvendes til at indlejre et SOSI IDkort i et XML dokument som er parseable for DGWS.

for et eksemple på en SOAP interceptors, som sætter en DGWS header for client requests til DGWS se https://svn.nspop.dk/src/components/nap/nap-reference-facade/trunk/src/main/java/dk/sds/nsp/nap/reference/facade/soapinterceptor/.


Snitfladebeskrivelse og brug

Nap-reference-facades snitflader bliver beskrevet i følgende.

oiosaml

Dokumentationen OIOSAML kan læses her https://github.com/digst/OIOSAML.Java/tree/master/docs

Men konkret anvendes følgende 2 endpoints til autentifikation og logout.

/saml/SAMLAssertionConsumer

Brugeres til at validere autentifikations requests og registrere en brugers assertion i en threadlocal session.

/saml/Logout

Bruger til single logout. Den sletter brugerens session.

/youAreLoggedOut

Anvendes af oiosaml.java som for singleLogoutService i forbindelse med Sikker browser opstart.

Hvis en bruger, som har startet applikationen med sikker browser opstart ønskes logget ud, vil oiosaml.java navigerer brugeren til dette endpoint, når brugeren er blevet logget ud.

Dette endpoint er derfor ikke beskyttet af autentikationsfilter og vil altid returnere 200 samt en html, der fortæller brugeren er logget ud

Konfiguration

Alt konfiguration foregår ved at loade filer fra et selv-defineret wildfly modul og ind i classpath under deployment.
Dette gør wildfly ved at modulet defineres i jboss-deployment-structure.xml. 

Der findes flere information om hvordan nap-refernece-facade konfigureres på NAP Ref.Impl. - Guide til udviklere.

Testing

Testene bliver eksekveret af maven-surefire-plugin med test frameworket junit. Test coverage bliver målt af Jacoco.

Dokumentation

Kodedokumentationen overholder javadoc standarden således dette kan generes med et værktøj fra fx den IDE man bruger.

Sikkerhed

dk.sosi.seal bliver brugt til at verificere SAMLassertion.

Code Block
languagejava
/**
     * @param headers Map of request headers
     * @return Saml assertion if its valid or null in case of invalid SAML assertion
     * @throws AuthenticationException If ant authentication exception occure
     */
    public OIOSAMLAssertion extractAndValidate(MultivaluedMap<String, String> headers) throws AuthenticationException {
        final List<String> authentication = headers.get(AUTHENTICATION_PROPERTY);
        try {
            String xml = new String(Base64.decode(authentication.get(0)), StandardCharsets.UTF_8);

            Document doc = parseXml(xml);

            Element encryptedAssertionElm = doc.getDocumentElement();

            PrivateKey privateKeyForAudience = certificateVault.getSystemCredentialPair().getPrivateKey();

            // decrypt the xml for the assertion and parse it
            final Element element = EncryptionUtil.decryptAndDetach(encryptedAssertionElm, privateKeyForAudience);
            OIOSAMLAssertion assertion = new OIOSAMLAssertion(element);

            log.debug("extracted: \n" + assertion.getUID());
            validateAssertion(assertion);

            return assertion;
        } catch (IOException | ParserConfigurationException | SAXException e) {
            throw new AuthenticationException("Could not validate authentication header", e);
        }
    }

Snitfladebeskrivelse og brug

Nap-reference-facade har 2 snitflader som bliver beskrevet i følgende.

/aftaler/{cpr}

Den eneste aftager af denne service er nap-reference-web. 

Headers
KeyValue
AuthenticationBASE64 encoded SAMLassertion
X-OrganizationSor

Sor nummer (bruges af dokumentdelingsservicen)

/isAlive

Bruges af NSP loadbalanceren for at tjekke at servicen er deployet. Returnerer en html side med deployment info.

...