Compare commits

...

12 Commits

  1. 5
      .dockerignore
  2. 5
      .gitignore
  3. 11
      Dockerfile.dvza
  4. 12
      Dockerfile.his
  5. 13
      Dockerfile.wbx
  6. 2
      cryptoutil/certgen.go
  7. 6
      docker-compose.yaml
  8. 2
      dvzaservice/app/src/Connection.js
  9. 2
      dvzaservice/app/src/Home.js
  10. 1
      dvzaservice/app/src/Registrations.js
  11. 25
      dvzaservice/main.go
  12. 4
      dvzaservice/model/db.go
  13. 12
      dvzaservice/openapisrv.go
  14. 7
      dvzaservice/srv.go
  15. 1
      dvzaservice/templates/index.html
  16. 6
      go.mod
  17. 4
      go.sum
  18. 2
      his/model/db.go
  19. 4
      his/model/models.go
  20. 11
      his/openapiclient.go
  21. 0
      his/patients/cbries.edi
  22. 0
      his/patients/cbries.fhir.json
  23. 0
      his/patients/jkorts.edi
  24. 0
      his/patients/jkorts.fhir.json
  25. 18
      his/srv.go
  26. 6
      sharedmodel/auth.go
  27. 2
      sharedmodel/model.go
  28. 6
      sharedmodel/registration.go
  29. 2
      sharedmodel/service.go
  30. 2
      whiteboxservice/app/src/Connection.js
  31. 2
      whiteboxservice/app/src/Home.js
  32. 1
      whiteboxservice/app/src/Registrations.js
  33. 0
      whiteboxservice/bin/amd64/ediviewer
  34. 0
      whiteboxservice/bin/amd64/medeur.json
  35. 0
      whiteboxservice/bin/amd64/template.html
  36. 0
      whiteboxservice/bin/arm64/ediviewer
  37. 0
      whiteboxservice/bin/arm64/medeur.json
  38. 300
      whiteboxservice/bin/arm64/template.html
  39. 31
      whiteboxservice/main.go
  40. 4
      whiteboxservice/model/db.go
  41. 30
      whiteboxservice/openapisrv.go
  42. 38
      whiteboxservice/srv.go
  43. 1
      whiteboxservice/templates/index.html

@ -1,4 +1,5 @@
**/.git
**/node_modules
**/app
**/data/data.db
**/assets
**/data/data.db
**/certs

5
.gitignore vendored

@ -1,3 +1,4 @@
node_modules
data/data.db
certs
data.db
certs
assets

@ -3,11 +3,12 @@
##
## Build
##
FROM golang:1.18-alpine AS build
FROM golang:1.19-alpine AS build
RUN apk update
RUN apk upgrade
RUN apk add build-base git
RUN apk add --update npm
WORKDIR /app
@ -20,16 +21,20 @@ WORKDIR /app/dvzaservice
RUN go build -o /app/bin/dvza
WORKDIR /app/dvzaservice/app
RUN npm i
RUN npx webpack
##
## Deploy
##
FROM golang:1.18-alpine
FROM alpine
WORKDIR /
COPY ./dvzaservice/assets /assets
COPY ./dvzaservice/templates /templates
COPY --from=build /app/bin/dvza ./dvza
COPY --from=build /app/dvzaservice/assets ./assets
RUN adduser -D nonroot
USER nonroot:nonroot

@ -3,11 +3,12 @@
##
## Build
##
FROM golang:1.18-alpine AS build
FROM golang:1.19-alpine AS build
RUN apk update
RUN apk upgrade
RUN apk add build-base git
RUN apk add --update npm
WORKDIR /app
@ -20,16 +21,21 @@ WORKDIR /app/his
RUN go build -o /app/bin/his
WORKDIR /app/his/app
RUN npm i
RUN npx webpack
##
## Deploy
##
FROM golang:1.18-alpine
FROM alpine
WORKDIR /
COPY ./his/assets /assets
COPY ./his/templates /templates
COPY ./his/patients /patients
COPY --from=build /app/bin/his ./his
COPY --from=build /app/his/assets ./assets
RUN adduser -D nonroot
USER nonroot:nonroot

@ -3,11 +3,12 @@
##
## Build
##
FROM golang:1.18-alpine AS build
FROM golang:1.19-alpine AS build
RUN apk update
RUN apk upgrade
RUN apk add build-base git
RUN apk add --update npm
WORKDIR /app
@ -20,17 +21,21 @@ WORKDIR /app/whiteboxservice
RUN go build -o /app/bin/wbx
WORKDIR /app/whiteboxservice/app
RUN npm i
RUN npx webpack
##
## Deploy
##
FROM golang:1.18-alpine
FROM alpine
WORKDIR /
COPY ./whiteboxservice/assets /assets
COPY ./whiteboxservice/templates /templates
COPY ./whiteboxservice/bin /bin
COPY ./whiteboxservice/bin/amd64 /wbxbin
COPY --from=build /app/bin/wbx ./wbx
COPY --from=build /app/whiteboxservice/assets ./assets
RUN adduser -D nonroot
USER nonroot:nonroot

@ -1,4 +1,4 @@
package certgen
package cryptoutil
import (
"bytes"

@ -16,7 +16,7 @@ services:
container_name: okapi_wbx
restart: always
environment:
EXT_ADDR: "okapi_wbx:8888"
EXT_ADDR: "okapi_wbx"
BIN_FOLDER: "/wbxbin"
volumes:
- "./wbx/data:/data"
@ -24,11 +24,11 @@ services:
ports:
- 8085:8085
okapi_dvza:
image: src.whiteboxsystems.nl/decozo/okapidemo/whitebox
image: src.whiteboxsystems.nl/decozo/okapidemo/dvza
container_name: okapi_dvza
restart: always
environment:
EXT_ADDR: "okapi_dvza:9999"
EXT_ADDR: "okapi_dvza"
volumes:
- "./dvza/data:/data"
- "./dvza/certs:/certs"

@ -40,8 +40,6 @@ const Connection = () => {
useEffect(() => {
fetch(`/api/connections/${params.connId}/${params.serviceId}`).then(x => x.json()).then(x => setService(x) )
}, [])
console.log('connection', connection)
console.log('service', service)
return (
<div>
<h1 className="t-page-header">Verbinding</h1>

@ -5,7 +5,7 @@ const App = () => {
return (
<div>
<h1 className="t-page-header">Welkom bij DVZA</h1>
<p>Dit systeem is beschikbaar op: <code>dvza.openkv.mcsr.nl:9999</code></p>
<p>Dit systeem is beschikbaar op: <code>{window.externalUrl}</code></p>
</div>
);
};

@ -7,7 +7,6 @@ const App = () => {
useEffect(() => {
fetch('/api/registrations').then(x => x.json()).then(x => setRegistrations(x) )
}, [])
console.log('registrations', registrations)
return (
<div>
<h1 className="t-page-header">Registratie verzoeken</h1>

@ -12,18 +12,20 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
)
var rpcAddr = "0.0.0.0:9999"
var rpcPort = "9999"
var rpcAddr = "0.0.0.0:" + rpcPort
var extRpcAddr = "localhost:" + rpcPort
var uiAddr = "0.0.0.0:9095"
func loadCert() *tls.Certificate {
_, err := os.Stat("certs/client.crt")
if err != nil {
_, _, certPem, keyPem, err := certgen.GenCert("dvza", "dvza")
_, _, certPem, keyPem, err := cryptoutil.GenCert("dvza", "dvza")
if err != nil {
panic(err)
@ -53,20 +55,9 @@ func loadCert() *tls.Certificate {
func loadKeyPair() credentials.TransportCredentials {
certificate := loadCert()
// data, err := ioutil.ReadFile("certs/ca.crt")
// if err != nil {
// panic("failed to load CA file: " + err.Error())
// }
// capool := x509.NewCertPool()
// if !capool.AppendCertsFromPEM(data) {
// panic("can't add ca cert")
// }
tlsConfig := &tls.Config{
ClientAuth: tls.RequestClientCert,
Certificates: []tls.Certificate{*certificate},
// ClientCAs: capool,
}
return credentials.NewTLS(tlsConfig)
}
@ -84,6 +75,10 @@ func main() {
grpcServer := grpc.NewServer(opts...)
if ext := os.Getenv("EXT_ADDR"); ext != "" {
extRpcAddr = ext + ":" + rpcPort
}
go func() {
lis, err := net.Listen("tcp", rpcAddr)
if err != nil {

@ -4,8 +4,8 @@ import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
func GetDB(location string) (*gorm.DB, error) {

@ -13,10 +13,10 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/DECOZO/okapidemo/dvzaservice/model"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
"src.whiteboxsystems.nl/decozo/okapidemo/dvzaservice/model"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
var errNotAuthorized = fmt.Errorf("Not Authorized")
@ -59,7 +59,7 @@ func requireConnection(db *gorm.DB, ctx context.Context) (*sharedmodel.Connectio
if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok {
item := mtls.State.PeerCertificates[0]
log.Println("request certificate subject:", item.Subject)
pk, err := certgen.PublicKeyToJWK(item.PublicKey)
pk, err := cryptoutil.PublicKeyToJWK(item.PublicKey)
if err != nil {
return nil, errNotAuthorized
}
@ -240,7 +240,7 @@ func (srv *OkAPIServer) CompleteRegistration(
if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok {
item := mtls.State.PeerCertificates[0]
pk, err := certgen.PublicKeyToJWK(item.PublicKey)
pk, err := cryptoutil.PublicKeyToJWK(item.PublicKey)
if err != nil {
return nil, errNotAuthorized
}

@ -10,8 +10,8 @@ import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapidemo/dvzaservice/model"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapidemo/dvzaservice/model"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
type UIService struct {
@ -69,7 +69,8 @@ func (srv *UIService) init() {
}
func (srv *UIService) GetIndex(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{})
c.HTML(http.StatusOK, "index.html", gin.H{"externalURL": extRpcAddr})
}
func (srv *UIService) GetConnection(c *gin.Context) {

@ -8,6 +8,7 @@
</head>
<body>
<div id="root"></div>
<script>window.externalUrl = '{{.externalURL}}'</script>
<script src="/assets/js/index.js"></script>
</body>
</html>

@ -1,14 +1,16 @@
module src.whiteboxsystems.nl/DECOZO/okapidemo
module src.whiteboxsystems.nl/decozo/okapidemo
go 1.18
require (
github.com/gin-gonic/gin v1.8.1
github.com/gofrs/uuid v4.2.0+incompatible
github.com/lestrrat-go/jwx v1.2.25
google.golang.org/grpc v1.48.0
google.golang.org/protobuf v1.28.0
gorm.io/driver/sqlite v1.3.6
gorm.io/gorm v1.23.8
src.whiteboxsystems.nl/decozo/okapi v0.0.5
)
require (
@ -27,7 +29,6 @@ require (
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.1 // indirect
github.com/lestrrat-go/jwx v1.2.25 // indirect
github.com/lestrrat-go/option v1.0.0 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-sqlite3 v1.14.12 // indirect
@ -42,5 +43,4 @@ require (
golang.org/x/text v0.3.6 // indirect
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
src.whiteboxsystems.nl/DECOZO/okapi v0.0.4 // indirect
)

@ -229,5 +229,5 @@ gorm.io/gorm v1.23.8 h1:h8sGJ+biDgBA1AD1Ha9gFCx7h8npU7AsLdlkX0n2TpE=
gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
src.whiteboxsystems.nl/DECOZO/okapi v0.0.4 h1:Hn/zWj14zknYYQr2rxkpwgZxpE+lH/qqeBp2RL9A/xY=
src.whiteboxsystems.nl/DECOZO/okapi v0.0.4/go.mod h1:zzd5uxSJdsWDyIu2LOpyBLme9Nvo6GaXsbd1CL7BmDU=
src.whiteboxsystems.nl/decozo/okapi v0.0.5 h1:iAvxdLSib7M8+LwOufPa1rF5usufzG0Vv2qWp2gjzi0=
src.whiteboxsystems.nl/decozo/okapi v0.0.5/go.mod h1:NTi+fO+ZkB01CVZlCDPiyWjbxEfaiPS2ECBPmu/XWek=

@ -4,7 +4,7 @@ import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
func GetDB(location string) (*gorm.DB, error) {

@ -4,8 +4,8 @@ import (
"time"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
type ConnectionState string

@ -11,10 +11,10 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/protobuf/types/known/structpb"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/DECOZO/okapidemo/his/model"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
"src.whiteboxsystems.nl/decozo/okapidemo/his/model"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
func toStruct(m map[string]interface{}) *structpb.Struct {
@ -40,7 +40,6 @@ func getUnauthenticatedClient(addr string) (okapi.OkAPIClient, error) {
return nil, err
}
// defer serviceProvider.Close()
return okapi.NewOkAPIClient(conn), nil
}
@ -88,7 +87,7 @@ func (srv *HISServer) register(addr string) (*model.ServiceProvider, error) {
return nil, err
}
jwkBytes, err := certgen.PublicKeyToJWKJson(certgen.ExtractPublicKey(srv.clientCert.PrivateKey))
jwkBytes, err := cryptoutil.PublicKeyToJWKJson(cryptoutil.ExtractPublicKey(srv.clientCert.PrivateKey))
if err != nil {
return nil, err

@ -18,17 +18,17 @@ import (
"github.com/gin-gonic/gin"
"google.golang.org/grpc/credentials"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/DECOZO/okapidemo/his/model"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
"src.whiteboxsystems.nl/decozo/okapidemo/his/model"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
func loadCert() *tls.Certificate {
_, err := os.Stat("certs/client.crt")
if err != nil {
_, _, certPem, keyPem, err := certgen.GenCert("whitebox", "whitebox")
_, _, certPem, keyPem, err := cryptoutil.GenCert("whitebox", "whitebox")
if err != nil {
panic(err)
@ -61,7 +61,6 @@ func loadKeyPair() credentials.TransportCredentials {
tlsConfig := &tls.Config{
ClientAuth: tls.RequestClientCert,
Certificates: []tls.Certificate{*certificate},
// ClientCAs: capool,
}
return credentials.NewTLS(tlsConfig)
}
@ -265,8 +264,9 @@ func (srv *HISServer) Authenticate(c *gin.Context) {
raw := ""
method := ""
if len(c.Request.TLS.PeerCertificates) > 0 {
jwk, err := certgen.PublicKeyToJWK(c.Request.TLS.PeerCertificates[0].PublicKey)
jwk, err := cryptoutil.PublicKeyToJWK(c.Request.TLS.PeerCertificates[0].PublicKey)
if err != nil {
log.Printf("Error extracting public key JKW: %v", err)
@ -568,7 +568,7 @@ func (srv *HISServer) GetPatient(c *gin.Context) {
return
}
f, err := os.Open(path.Join("./data/patients", patient.FileBase+".edi"))
f, err := os.Open(path.Join("./patients", patient.FileBase+".edi"))
if err != nil {
c.Error(err)
return
@ -599,7 +599,7 @@ func (srv *HISServer) GetFHIRPatient(c *gin.Context) {
return
}
f, err := os.Open(path.Join("./data/patients", patient.FileBase+".fhir.json"))
f, err := os.Open(path.Join("./patients", patient.FileBase+".fhir.json"))
if err != nil {
c.Error(err)
return

@ -6,8 +6,8 @@ import (
"google.golang.org/protobuf/types/known/structpb"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
)
const AuthMethodDecozoMTLS = "http://decozo.org/proto/auth/mtls"
@ -56,7 +56,7 @@ func NewAuthConfig(cfg *okapi.ProtocolAuthConfiguration) *AuthConfig {
authConfig.Raw, _ = cfg.GetConfiguration().AsMap()["token"].(string)
case AuthMethodDecozoMTLS:
k, _ := cfg.GetConfiguration().AsMap()["publicKey"].(string)
jwk, _ := certgen.StringToJWK(k)
jwk, _ := cryptoutil.StringToJWK(k)
if jwk != nil {
rawBytes, _ := jwk.Thumbprint(crypto.SHA256)
authConfig.Raw = fmt.Sprintf("%X", rawBytes)

@ -6,7 +6,7 @@ import (
"errors"
"fmt"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/decozo/okapi"
)
type Protocol struct {

@ -5,8 +5,8 @@ import (
"fmt"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
)
type RegistrationStatus string
@ -35,7 +35,7 @@ func (r *Registration) SetAuthConfig(cfg *okapi.XISAuthConfiguration) error {
switch cfg.Method {
case okapi.XISAuthMethod_mTLS:
k, err := certgen.StringToJWK(cfg.GetMtlsConfiguration().GetPublicKey())
k, err := cryptoutil.StringToJWK(cfg.GetMtlsConfiguration().GetPublicKey())
if err != nil {
return err

@ -5,7 +5,7 @@ import (
"google.golang.org/protobuf/types/known/structpb"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/decozo/okapi"
)
type ServiceDefinition struct {

@ -40,8 +40,6 @@ const Connection = () => {
useEffect(() => {
fetch(`/api/connections/${params.connId}/${params.serviceId}`).then(x => x.json()).then(x => setService(x) )
}, [])
console.log('connection', connection)
console.log('service', service)
return (
<div>
<h1 className="t-page-header">Patiënten</h1>

@ -5,7 +5,7 @@ const App = () => {
return (
<div>
<h1 className="t-page-header">Welkom bij Whitebox</h1>
<p>Dit systeem is beschikbaar op: <code>wbx.openkv.mcsr.nl:8888</code></p>
<p>Dit systeem is beschikbaar op: <code>{window.externalUrl}</code></p>
</div>
);
};

@ -7,7 +7,6 @@ const App = () => {
useEffect(() => {
fetch('/api/registrations').then(x => x.json()).then(x => setRegistrations(x) )
}, [])
console.log('registrations', registrations)
return (
<div>
<h1 className="t-page-header">Registratie verzoeken</h1>

@ -1,103 +1,98 @@
<!DOCTYPE html><html><head>
<meta charset="utf-8">
<title>{{.Patient.Name}} — MEDEUR</title>
<style>
body { margin: 1em auto; font-size: 12pt; font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; }
@media screen and (max-width: 700px) {
body { width: 700px; }
}
@media screen and (min-width: 1200px) {
body { width: 1200px; }
}
.container { margin: 1em auto; font-size: 12pt; font-family: "Helvetica Neue",Helvetica,Arial,sans-serif; }
@media screen and (max-width: 700px) {
.container { width: 700px; }
}
@media screen and (min-width: 1200px) {
.container { width: 1200px; }
}
* { margin: 0; padding: 0; box-sizing: border-box; }
h3 { margin-bottom: 8px; }
ul { list-style-type: none; }
li + li { margin-top: 10px; }
td { vertical-align: top; }
.seg { margin-top: 8px; padding: 8px 10px; border: 0 solid #e7e7e7; border-width: 0 1px 1px 1px; }
.seg-hdr { margin-top: 8px; border-top-width: 1px; background: #F8F8F8; }
.seg-hdr-white { margin-top: 8px; background: #FFFFFF; border-color: transparent; }
a { color: royalblue }
table.patient th { text-align: left; padding-right: 1em }
table.SOEPkind { margin: -6px 0 }
table.SOEPkind tr { height: 1em }
table.SOEPkind th { text-align: left; width: 32px; height: 1em; font-weight: normal }
table.results th { text-align: left }
.error { position: fixed; top: 10px; margin-left: 32px; border: 2px solid #000000; background-color: #FF2222; }
.row + .row { margin-top: 4px; }
.table { font-size: 100%; width: 100%; border-spacing: 0; border-collapse: collapse; }
.table tr { font-size: 100% }
.table td { font-size: 100% }
.table th { font-size: 100%; text-align: left; }
.table tbody td { font-size: 100%; text-align: left; padding: 2px 8px 2px 0; }
.table thead th { font-size: 100%; padding: 0 0 2px 0; }
.table tr.highlight { background: #E3E9E3 }
.column-attn { width: 1em; }
.column-code { width: 4em; }
.column-date { width: 6.5em; }
.column-small-name { width: 10em; }
.column-medium-name { width: 14em; }
.column-large-name { width: 28em; }
.column-name { width: 20em; }
.column-instr{ width: 15em; }
.column-quantity { width: 12em; }
.no-margin { margin-bottom: 0px; }
.seg-date { float: left; width: 6.5em; }
.seg-content-offset { margin-left: 6.5em }
.column-result-id { width: 16em }
.column-result-name { width: 10em }
h3.fold-toggle { margin-bottom: 8px }
h4.fold-toggle { margin-bottom: 0; margin-top: 4px }
h3.fold-toggle.fold-toggle-hidden { margin-bottom: 0 }
h4.fold-toggle.fold-toggle-hidden { margin-bottom: 4px }
.fold-toggle .fold-marker:before,
h3.fold-toggle:before,
h4.fold-toggle:before { content: "▾ " }
.fold-toggle.fold-toggle-hidden .fold-marker:before,
h3.fold-toggle.fold-toggle-hidden:before,
h4.fold-toggle.fold-toggle-hidden:before { content: "▸ " }
.fold-toggle { cursor: pointer; }
.fold-hidden {display: none}
.hidden
{
display: none !important
}
.container h3 { margin-bottom: 8px; }
.container ul { list-style-type: none; }
.container li + li { margin-top: 10px; }
.container td { vertical-align: top; }
.container .seg { margin-top: 8px; padding: 8px 10px; border: 0 solid #e7e7e7; border-width: 0 1px 1px 1px; }
.container .seg-hdr { margin-top: 8px; border-top-width: 1px; background: #F8F8F8; }
.container .seg-hdr-white { margin-top: 8px; background: #FFFFFF; border-color: transparent; }
.container a { color: royalblue }
.container table.patient th { text-align: left; padding-right: 1em }
.container table.SOEPkind { margin: -6px 0 }
.container table.SOEPkind tr { height: 1em }
.container table.SOEPkind th { text-align: left; width: 32px; height: 1em; font-weight: normal }
.container table.results th { text-align: left }
.container .error { position: fixed; top: 10px; margin-left: 32px; border: 2px solid #000000; background-color: #FF2222; }
.row + .row { margin-top: 4px; }
.table { font-size: 100%; width: 100%; border-spacing: 0; border-collapse: collapse; }
.table tr { font-size: 100% }
.table td { font-size: 100% }
.table th { font-size: 100%; text-align: left; }
.table tbody td { font-size: 100%; text-align: left; padding: 2px 8px 2px 0; }
.table thead th { font-size: 100%; padding: 0 0 2px 0; }
.table tr.highlight { background: #E3E9E3 }
.column-attn { width: 1em; }
.column-code { width: 4em; }
.column-date { width: 6.5em; }
.column-small-name { width: 10em; }
.column-medium-name { width: 14em; }
.column-large-name { width: 28em; }
.column-name { width: 20em; }
.column-instr{ width: 15em; }
.column-quantity { width: 12em; }
.no-margin { margin-bottom: 0px; }
.seg-date { float: left; width: 6.5em; }
.seg-content-offset { margin-left: 6.5em }
.column-result-id { width: 16em }
.column-result-name { width: 10em }
.container h3.fold-toggle { margin-bottom: 8px }
.container h4.fold-toggle { margin-bottom: 0; margin-top: 4px }
.container h3.fold-toggle.fold-toggle-hidden { margin-bottom: 0 }
.container h4.fold-toggle.fold-toggle-hidden { margin-bottom: 4px }
.fold-toggle .fold-marker:before,
.container h3.fold-toggle:before,
.container h4.fold-toggle:before { content: "▾ " }
.fold-toggle.fold-toggle-hidden .fold-marker:before,
.container h3.fold-toggle.fold-toggle-hidden:before,
.container h4.fold-toggle.fold-toggle-hidden:before { content: "▸ " }
.fold-toggle { cursor: pointer; }
.fold-hidden {display: none}
.hidden
{
display: none !important
}
textarea
{
border: 1px solid #333;
padding: .5em;
font-size: 100%
}
.container textarea
{
border: 1px solid #333;
padding: .5em;
font-size: 100%
}
input[type=submit]
{
font-size: 90%;
padding: .25em
}
.container input[type=submit]
{
font-size: 90%;
padding: .25em
}
button
{
font-size: 75%;
padding: .25em
}
</style></head>
.container button
{
font-size: 75%;
padding: .25em
}
</style>
<body>
<div class="container">
{{define "person-row"}}
@ -114,9 +109,6 @@ button
<thead>
<tr>
<th class="column-date">Datum</th>
{{if HasEndDates .Episodes }}
<th class="column-date">Einddatum</th>
{{end}}
<th class="column-code">Type</th>
<th class="column-date">ICPC</th>
<th class="column-name">Beschrijving</th>
@ -128,32 +120,26 @@ button
</tr>
</thead>
<tbody>
{{range .Episodes}}
<tr {{if .Anchor}} id="medigroup-{{.Anchor}}"{{end}}>
<td>{{.Date}}</td>
{{if HasEndDates $.Episodes }}
<td style="{{if EndDateInPast .EndDate}}color: red;{{end}}">{{.EndDate}}</td>
{{end}}
{{range .Episodes}}
<tr{{if .Anchor}} id="medigroup-{{.Anchor}}"{{end}}>
<td>{{.Date}}</td>
<td>{{.Type}}</td>
<td>{{.ICPC}}</td>
<td>{{.Type}}</td>
<td>{{.ICPC}}</td>
{{if eq $root.Extra.verbose_icpc_description "true"}}
<td>{{.Title}}</td>
<td>{{.Description}}</td>
{{else}}
{{if eq .Description ""}}
<td colspan="2">{{.Title}}</td>
{{else}}
<td colspan="2">{{.Description}}</td>
{{end}}
{{end}}
</tr>
{{end}}
{{if eq $root.Extra.verbose_icpc_description "true"}}
<td>{{.Title}}</td>
<td>{{.Description}}</td>
{{else}}
{{if eq .Description ""}}
<td colspan="2">{{.Title}}</td>
{{else}}
<td colspan="2">{{.Description}}</td>
{{end}}
{{end}}
</tr>{{end}}
</tbody>
</table>
{{end}}
{{end}}
{{define "medi-group-ica"}}
{{$root := .}}
@ -244,7 +230,7 @@ button
{{if .Episodes}}
<div class="seg seg-hdr">
<h3>Episodelijst</h3>
{{template "medi-group-epi" .}}
{{template "medi-group-epi" .}}
</div>
{{end}}
@ -363,14 +349,14 @@ button
{{if .Extra.csrf_token }}
<div class="seg seg-hdr-white">
<br>
<h3 id="feedback-header"><button onClick="feedbackHeaderClicked()">Notitie schrijven</button></h3>
<form id="feedback-form" class="hidden" method="POST" onSubmit="sendFeedback(); return false"> <!-- TODO: contruct proper URL in golang -->
<input type="hidden" name="csrf_token" value="{{.Extra.csrf_token}}" id="csrf">
<p><br><textarea id="feedback-textarea" rows="7" cols="50"></textarea>
<p><br><input type="submit" value="Opsturen">
</form>
</div>
@ -453,68 +439,4 @@ function getXMLHttpRequest()
}
}
function sendFeedback()
{
var URL = document.location.href.replace('preview', '') + 'pushback'
var textarea = document.getElementById('feedback-textarea')
var message = textarea.value
var csrf_element = document.getElementById('csrf')
var csrf= csrf_element.value
var xmlhttp = getXMLHttpRequest()
xmlhttp.onreadystatechange = function() { sendFeedbackHandler(xmlhttp) }
params = '';
params += 'csrf_token=' + encodeURIComponent(csrf) + '&'
params += 'message=' + encodeURIComponent(message)
xmlhttp.open('POST', URL, true)
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded')
xmlhttp.setRequestHeader('Content-length', params.length)
xmlhttp.setRequestHeader('Connection', 'close')
xmlhttp.send(params)
}
function sendFeedbackHandler(xmlhttp)
{
if (xmlhttp.readyState != 4) { return }
if (xmlhttp.status == 200)
{
try
{
var response = JSON.parse(xmlhttp.responseText)
if (response.success) {
alert('Feedback is succesvol verstuurd.')
toggle('feedback-form')
} else {
alert('Er is iets fout gegaan: ' + response.error)
}
}
catch (e)
{
alert('Fout bij versturen feedback.')
}
}
else
{
alert('Fout bij versturen feedback.')
}
}
function send_height_to_parent() {
var body = document.body,
html = document.documentElement;
var height = Math.max( body.scrollHeight, body.offsetHeight,
html.clientHeight, html.scrollHeight, html.offsetHeight );
var msg = {
"height": height,
};
parent.postMessage(msg, "*");
}
send_height_to_parent();
</script></body></html>

@ -3,6 +3,7 @@ package main
import (
"context"
"crypto/tls"
"fmt"
"io/ioutil"
"log"
"net"
@ -12,19 +13,22 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
)
var srvaddr = "0.0.0.0:8888"
var rpcPort = "8888"
var srvaddr = "0.0.0.0:" + rpcPort
var extRpcAddr = "localhost:" + rpcPort
var patientIf = "0.0.0.0:8085"
var binFolder = "./bin"
func loadCert() *tls.Certificate {
_, err := os.Stat("certs/client.crt")
var binFolder = "./bin/arm64"
func loadCert(name string) *tls.Certificate {
_, err := os.Stat(fmt.Sprintf("certs/%s.crt", name))
if err != nil {
_, _, certPem, keyPem, err := certgen.GenCert("whitebox", "whitebox")
_, _, certPem, keyPem, err := cryptoutil.GenCert("whitebox", "whitebox")
if err != nil {
panic(err)
@ -34,15 +38,15 @@ func loadCert() *tls.Certificate {
panic(err)
}
if err := ioutil.WriteFile("certs/client.crt", []byte(certPem), 0600); err != nil {
if err := ioutil.WriteFile(fmt.Sprintf("certs/%s.crt", name), []byte(certPem), 0600); err != nil {
panic(err)
}
if err := ioutil.WriteFile("certs/client.key", []byte(keyPem), 0600); err != nil {
if err := ioutil.WriteFile(fmt.Sprintf("certs/%s.key", name), []byte(keyPem), 0600); err != nil {
panic(err)
}
}
certificate, err := tls.LoadX509KeyPair("certs/client.crt", "certs/client.key")
certificate, err := tls.LoadX509KeyPair(fmt.Sprintf("certs/%s.crt", name), fmt.Sprintf("certs/%s.key", name))
if err != nil {
panic("Load client certification failed: " + err.Error())
@ -52,12 +56,11 @@ func loadCert() *tls.Certificate {
}
func loadKeyPair() credentials.TransportCredentials {
certificate := loadCert()
certificate := loadCert("client")
tlsConfig := &tls.Config{
ClientAuth: tls.RequestClientCert,
Certificates: []tls.Certificate{*certificate},
// ClientCAs: capool,
}
return credentials.NewTLS(tlsConfig)
}
@ -71,6 +74,10 @@ func main() {
binFolder = os.Getenv("BIN_FOLDER")
}
if ext := os.Getenv("EXT_ADDR"); ext != "" {
extRpcAddr = ext + ":" + rpcPort
}
openapisrv := NewServer()
openapisrv.LoadData("./data/data.db")
opts := []grpc.ServerOption{

@ -4,8 +4,8 @@ import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
)
func GetDB(location string) (*gorm.DB, error) {

@ -14,10 +14,10 @@ import (
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/structpb"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapi"
"src.whiteboxsystems.nl/DECOZO/okapidemo/certgen"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/DECOZO/okapidemo/whiteboxservice/model"
"src.whiteboxsystems.nl/decozo/okapi"
"src.whiteboxsystems.nl/decozo/okapidemo/cryptoutil"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapidemo/whiteboxservice/model"
)
var errNotAuthorized = fmt.Errorf("Not Authorized")
@ -45,6 +45,8 @@ type OkAPIServer struct {
okapi.UnimplementedOkAPIServer
data *gorm.DB
clientCert tls.Certificate
visiteCert tls.Certificate
wnhCert tls.Certificate
}
func (srv *OkAPIServer) LoadData(location string) error {
@ -61,7 +63,7 @@ func requireConnection(db *gorm.DB, ctx context.Context) (*sharedmodel.Connectio
if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok {
item := mtls.State.PeerCertificates[0]
log.Println("request certificate subject:", item.Subject)
pk, err := certgen.PublicKeyToJWK(item.PublicKey)
pk, err := cryptoutil.PublicKeyToJWK(item.PublicKey)
if err != nil {
return nil, errNotAuthorized
}
@ -224,7 +226,7 @@ func (srv *OkAPIServer) CompleteRegistration(
if mtls, ok := p.AuthInfo.(credentials.TLSInfo); ok {
item := mtls.State.PeerCertificates[0]
pk, err := certgen.PublicKeyToJWK(item.PublicKey)
pk, err := cryptoutil.PublicKeyToJWK(item.PublicKey)
if err != nil {
return nil, errNotAuthorized
}
@ -307,7 +309,15 @@ func (srv *OkAPIServer) EnableService(
AuthConfig: sharedmodel.NewAuthConfig(in.Fetch.Auth),
}
publicKey, err := certgen.PublicKeyToJWKJson(certgen.ExtractPublicKey(srv.clientCert.PrivateKey))
var cert tls.Certificate
if in.ServiceId == "wbx:visitelijst" {
cert = srv.visiteCert
} else if in.ServiceId == "wbx:waarneming" {
cert = srv.wnhCert
}
publicKey, err := cryptoutil.PublicKeyToJWKJson(cryptoutil.ExtractPublicKey(cert.PrivateKey))
if err != nil {
return nil, fmt.Errorf("Error retrieving pub key: %v", err)
@ -621,9 +631,13 @@ func (srv *OkAPIServer) ListPatientRegistrations(
}
func NewServer() *OkAPIServer {
cert := loadCert()
cert := loadCert("client")
visiteCert := loadCert("visite")
wnhCert := loadCert("wnh")
return &OkAPIServer{
clientCert: *cert,
visiteCert: *visiteCert,
wnhCert: *wnhCert,
}
}

@ -13,14 +13,16 @@ import (
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"src.whiteboxsystems.nl/DECOZO/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/DECOZO/okapidemo/whiteboxservice/model"
"src.whiteboxsystems.nl/decozo/okapidemo/sharedmodel"
"src.whiteboxsystems.nl/decozo/okapidemo/whiteboxservice/model"
)
type UIService struct {
srv *http.Server
inited bool
clientCert tls.Certificate
visiteCert tls.Certificate
wnhCert tls.Certificate
data *gorm.DB
}
@ -70,7 +72,7 @@ func (srv *UIService) init() {
}
func (srv *UIService) GetIndex(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{})
c.HTML(http.StatusOK, "index.html", gin.H{"externalURL": extRpcAddr})
}
func (srv *UIService) GetConnection(c *gin.Context) {
@ -117,14 +119,21 @@ func (srv *UIService) GetPatient(c *gin.Context) {
}
url := fmt.Sprintf("%v/%v/%v", protoconfig["url"], "patients", protometa["patientID"])
req, _ := http.NewRequest("GET", url, nil)
var cert tls.Certificate
if serviceConfig.Service.ServiceID == "wbx:visitelijst" {
cert = srv.visiteCert
} else if serviceConfig.Service.ServiceID == "wbx:waarneming" {
cert = srv.wnhCert
}
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
InsecureSkipVerify: true,
Certificates: []tls.Certificate{srv.clientCert},
Certificates: []tls.Certificate{cert},
},
},
}
@ -224,12 +233,19 @@ func (srv *UIService) GetRegistrations(c *gin.Context) {
}
func NewUIServer(addr string) *UIService {
cert := loadCert()
srv := &UIService{srv: &http.Server{
Addr: addr,
Handler: gin.Default(),
}, clientCert: *cert}
cert := loadCert("client")
visiteCert := loadCert("visite")
wnhCert := loadCert("wnh")
srv := &UIService{
srv: &http.Server{
Addr: addr,
Handler: gin.Default(),
},
clientCert: *cert,
visiteCert: *visiteCert,
wnhCert: *wnhCert,
}
return srv
}

@ -8,6 +8,7 @@
</head>
<body>
<div id="root"></div>
<script>window.externalUrl = '{{.externalURL}}'</script>
<script src="/assets/js/index.js"></script>
</body>
</html>
Loading…
Cancel
Save