Skip to content
This repository has been archived by the owner on Feb 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #929 from solita/feature/AE-1924
Browse files Browse the repository at this point in the history
Update WSS4J - AE-1924
  • Loading branch information
Juholei authored Jul 13, 2023
2 parents 5758f19 + 705feb9 commit 8456634
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 43 deletions.
8 changes: 6 additions & 2 deletions etp-backend/deps.edn
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{:paths ["src/main/clj"
"src/main/sql"
"src/main/resources"]
:mvn/repos {"shibboleth" {:url "https://build.shibboleth.net/maven/releases/"}}
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
ch.qos.logback/logback-classic {:mvn/version "1.4.8"}
org.slf4j/log4j-over-slf4j {:mvn/version "1.7.30"}
Expand Down Expand Up @@ -71,7 +72,9 @@
com.jcraft/jsch {:mvn/version "0.1.55"}
com.sun.mail/javax.mail {:mvn/version "1.6.2"}

org.apache.ws.security/wss4j {:mvn/version "1.6.19"}
org.apache.wss4j/wss4j-ws-security-dom {:mvn/version "3.0.0"}
org.apache.wss4j/wss4j-ws-security-common {:mvn/version "3.0.0"}
com.sun.xml.messaging.saaj/saaj-impl {:mvn/version "3.0.2"}
org.apache.axis/axis {:mvn/version "1.4"}
commons-io/commons-io {:mvn/version "2.11.0"}
;; commons-discovery is needed by some other library dynamically at runtime
Expand All @@ -82,7 +85,8 @@
"src/test/resources"]
:extra-deps {eftest/eftest {:mvn/version "0.5.9"}
prismatic/schema-generators {:mvn/version "0.1.3"}
ring/ring-mock {:mvn/version "0.4.0"}}
ring/ring-mock {:mvn/version "0.4.0"}
org.xmlunit/xmlunit-core {:mvn/version "2.9.1"}}
:jvm-opts ["-Djava.awt.headless=true"]}
:test {:main-opts ["-e" "(run-tests-and-exit!)" "-A:dev"]}
:test-ci {:main-opts ["-e" "(run-tests-with-junit-reporter-and-exit!)" "-A:dev"]}
Expand Down
59 changes: 31 additions & 28 deletions etp-backend/src/main/clj/solita/etp/service/suomifi_viestit.clj
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,22 @@
[clj-http.conn-mgr :as conn-mgr]
[clojure.string :as str]
[solita.etp.exception :as exception])
(:import (org.apache.ws.security WSConstants WSEncryptionPart)
(org.apache.ws.security.components.crypto CryptoFactory)
(org.apache.ws.security.message WSSecSignature WSSecHeader WSSecTimestamp)
(java.util Properties)
(java.io ByteArrayInputStream)
(:import (java.util Properties)
(java.io ByteArrayInputStream ByteArrayOutputStream)
(org.apache.xml.security Init)
(org.apache.axis.soap MessageFactoryImpl)
(org.apache.wss4j.common WSEncryptionPart)
(org.apache.wss4j.common.crypto CryptoFactory)
(org.apache.wss4j.dom WSConstants)
(org.apache.wss4j.dom.message WSSecHeader WSSecSignature WSSecTimestamp)
(org.apache.xml.security.c14n Canonicalizer)
(org.apache.axis.configuration NullProvider)
(org.apache.axis.client AxisClient)
(org.apache.axis MessageContext Message)))

;; register default algorithms
(Canonicalizer/registerDefaultAlgorithms)
(Init/init)

(defn- request-create-xml [data]
(clostache/render-resource (str "suomifi/viesti.xml") data))
Expand All @@ -30,7 +33,7 @@
(log/debug request)
(if config/suomifi-viestit-endpoint-url
(http/post config/suomifi-viestit-endpoint-url
(cond-> {:body request
(cond-> {:body request
:throw-exceptions false}
config/suomifi-viestit-proxy?
(assoc
Expand All @@ -49,11 +52,11 @@
(-> axisMessage .getSOAPEnvelope .getAsDocument))))

(defn- document->signed-request [document]
(let [c14n (Canonicalizer/getInstance Canonicalizer/ALGO_ID_C14N_WITH_COMMENTS)
canonicalMessage (.canonicalizeSubtree c14n document)
in (ByteArrayInputStream. canonicalMessage)]
(-> (.createMessage (MessageFactoryImpl.) nil in)
.getSOAPEnvelope .getAsString)))
(let [c14n (Canonicalizer/getInstance Canonicalizer/ALGO_ID_C14N_WITH_COMMENTS)]
(with-open [os (ByteArrayOutputStream.)]
(.canonicalizeSubtree c14n document os)
(-> (.createMessage (MessageFactoryImpl.) nil (ByteArrayInputStream. (.toByteArray os)))
.getSOAPEnvelope .getAsString))))

(defn- signSOAPEnvelope [request keystore-file keystore-password keystore-alias]
(let [properties (doto (Properties.)
Expand All @@ -63,21 +66,21 @@
(.setProperty "signer.username" keystore-alias)
(.setProperty "signer.password" keystore-password))
crypto (CryptoFactory/getInstance properties)
signer (doto (WSSecSignature.)
doc (raw-request->document request)
header (doto (WSSecHeader. doc)
(.setMustUnderstand true)
(.insertSecurityHeader))
signer (doto (WSSecSignature. header)
(.setUserInfo keystore-alias keystore-password)
(.setKeyIdentifierType WSConstants/BST_DIRECT_REFERENCE)
(.setUseSingleCertificate true))
doc (raw-request->document request)
header (doto (WSSecHeader.)
(.setMustUnderstand true)
(.insertSecurityHeader doc))
timestamp (doto (WSSecTimestamp.)
(.setTimeToLive 60))
build-doc (.build timestamp doc header)
timestampPart (WSEncryptionPart. "Timestamp", WSConstants/WSU_NS, "")
bodyPart (WSEncryptionPart. WSConstants/ELEM_BODY, WSConstants/URI_SOAP11_ENV, "")]
(.setParts signer [timestampPart bodyPart])
(document->signed-request (.build signer build-doc crypto header))))
(doto (WSSecTimestamp. header)
(.setTimeToLive 60)
(.build))
(.addAll (.getParts signer) (list timestampPart bodyPart))
(document->signed-request (.build signer crypto))))

(defn- read-response->xml [response]
(-> response xml/string->xml xml/without-soap-envelope first xml/with-kebab-case-tags))
Expand Down Expand Up @@ -105,12 +108,12 @@
(throw
(ex-info
(str "Sending suomifi message " (-> request :sanoma :tunniste) " failed.")
{:type type
{:type type
:endpoint-url config/suomifi-viestit-endpoint-url
:request (select-keys* request [[:sanoma :tunniste]
[:kysely :kohteet :nimike]])
:response response
:cause (ex-data cause)}
:request (select-keys* request [[:sanoma :tunniste]
[:kysely :kohteet :nimike]])
:response response
:cause (ex-data cause)}
cause)))

(defn- assert-status! [response]
Expand All @@ -121,8 +124,8 @@
(defn- read-response [request response]
(try
(some-> response assert-status! :body response-parser coerce-response!)
(catch Throwable t
(throw-ex-info! :suomifi-viestit-invalid-response request response t))))
(catch Throwable t
(throw-ex-info! :suomifi-viestit-invalid-response request response t))))

(defn- handle-request! [request keystore-file keystore-password keystore-alias]
(try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,72 @@
(:require [clojure.test :as t]
[solita.etp.test-system :as ts]
[clojure.java.io :as io]
[solita.etp.service.valvonta-kaytto.suomifi-viestit :as suomifi-viestit]
[solita.etp.service.valvonta-kaytto.suomifi-viestit :as valvonta-kaytto.suomifi-viestit]
[solita.etp.service.suomifi-viestit :as service.suomifi-viestit]
[clojure.string :as str]
[solita.etp.service.pdf :as pdf])
(:import (java.time LocalDate)))
(:import (java.time LocalDate)
(org.w3c.dom Node)
(org.xmlunit.diff Comparison$Detail ComparisonListener ComparisonResult ComparisonType DOMDifferenceEngine DifferenceEvaluator DifferenceEvaluators)
(org.xmlunit.builder Input)
(org.xmlunit.util Predicate)))

(t/use-fixtures :each ts/fixture)

(defn- handle-request [request-resource response-resource response-status]
(fn [request]
(t/is (= (-> request str/trim) (-> request-resource io/resource slurp str/trim)))
{:body (-> response-resource io/resource slurp)
:status response-status}))

(defn- in-tree? [node-name ^Node node]
(cond
(= node-name (.getNodeName node)) true
(nil? (.getParentNode node)) false
:else (in-tree? node-name (.getParentNode node))))

(defn- in-tree-filter [interesting-tree]
(reify Predicate
(test [_ node]
(if (= "soapenv:Envelope" (->> node (cast Node) .getNodeName)) ; Can't skip the top level element
true
(in-tree? interesting-tree node)))))

(defn- difference-listener []
(reify ComparisonListener (comparisonPerformed [_ comparison _]
(t/is false comparison)))) ; ComparisonListener is called when comparison fails. Fail the test

(defn- empty-node? [^Comparison$Detail target]
(str/blank? (-> target .getTarget .getTextContent)))

(defn- handle-request-with-xml-compare [request-resource response-resource response-status]
(fn [request]
(let [wanted (-> request-resource io/resource Input/fromURL .build)
actual (-> request str Input/fromString .build)]
;; Header contains attributes and text elements that are generated by encryption
;; Check that they're in place, but ignore values
(doto (DOMDifferenceEngine.)
(.setNodeFilter (in-tree-filter "soapenv:Header"))
(.setDifferenceEvaluator (reify DifferenceEvaluator
(evaluate [_ comparison outcome]
(if (and (contains? #{ComparisonType/ATTR_VALUE ComparisonType/TEXT_VALUE} (-> comparison .getType))
(= (-> comparison .getTestDetails empty-node?) (-> comparison .getControlDetails empty-node?)))
ComparisonResult/EQUAL
(.evaluate DifferenceEvaluators/Default comparison outcome)))))
(.addDifferenceListener (difference-listener))
(.compare wanted actual))
;; Check the body but ignore the wsu:Id
(doto (DOMDifferenceEngine.)
(.setNodeFilter (in-tree-filter "soapenv:Body"))
(.setDifferenceEvaluator (reify DifferenceEvaluator
(evaluate [_ comparison outcome]
(if (= "wsu:Id" (-> comparison .getTestDetails .getTarget .getNodeName))
ComparisonResult/EQUAL
(.evaluate DifferenceEvaluators/Default comparison outcome)))))
(.addDifferenceListener (difference-listener))
(.compare wanted actual))
{:body (-> response-resource io/resource slurp)
:status response-status}))
:status response-status})))

(def valvonta {:id 1
:rakennustunnus "103515074X"
Expand Down Expand Up @@ -50,24 +104,36 @@
:laskutus-salasana "0000"})

(t/deftest send-message-to-osapuoli-test
(with-bindings {#'solita.etp.service.suomifi-viestit/post! (handle-request "suomifi/viesti-request.xml"
(with-bindings {#'service.suomifi-viestit/post! (handle-request "suomifi/viesti-request.xml"
"suomifi/viesti-response.xml"
202)
#'suomifi-viestit/now (fn [] "2021-09-08T06:21:03.625667Z")
#'suomifi-viestit/bytes->base64 (fn [_] "dGVzdGk=")}
#'valvonta-kaytto.suomifi-viestit/now (fn [] "2021-09-08T06:21:03.625667Z")
#'valvonta-kaytto.suomifi-viestit/bytes->base64 (fn [_] "dGVzdGk=")}

(t/is (= (:sanoma-tunniste (suomifi-viestit/send-message-to-osapuoli! valvonta toimenpide osapuoli document config))
(t/is (= (:sanoma-tunniste (valvonta-kaytto.suomifi-viestit/send-message-to-osapuoli! valvonta toimenpide osapuoli document config))
"ARA-05.03.02-2021-31-ETP-KV-1-2-PERSON-1"))))

(t/deftest send-message-to-osapuoli-id-already-exists-test
(with-bindings {#'solita.etp.service.suomifi-viestit/post! (handle-request "suomifi/viesti-request.xml"
(with-bindings {#'service.suomifi-viestit/post! (handle-request "suomifi/viesti-request.xml"
"suomifi/viesti-id-already-exists-response.xml"
200)
#'suomifi-viestit/now (fn []
"2021-09-08T06:21:03.625667Z")
#'suomifi-viestit/bytes->base64 (fn [_]
"dGVzdGk=")}
#'valvonta-kaytto.suomifi-viestit/now (fn []
"2021-09-08T06:21:03.625667Z")
#'valvonta-kaytto.suomifi-viestit/bytes->base64 (fn [_]
"dGVzdGk=")}
(t/is (thrown-with-msg?
clojure.lang.ExceptionInfo
#"Sending suomifi message ARA-05.03.02-2021-31-ETP-KV-1-2-PERSON-1 failed."
(suomifi-viestit/send-message-to-osapuoli! valvonta toimenpide osapuoli document config)))))
(valvonta-kaytto.suomifi-viestit/send-message-to-osapuoli! valvonta toimenpide osapuoli document config)))))

(t/deftest send-message-to-osapuoli-with-signing-test
(with-bindings {#'service.suomifi-viestit/post! (handle-request-with-xml-compare "suomifi/viesti-request-signed.xml"
"suomifi/viesti-response.xml"
202)
#'valvonta-kaytto.suomifi-viestit/now (fn [] "2021-09-08T06:21:03.625667Z")
#'valvonta-kaytto.suomifi-viestit/bytes->base64 (fn [_] "dGVzdGk=")}
(let [config-with-keystore (merge config {:keystore-file (.getPath (io/resource "suomifi/store.jks"))
:keystore-password "password"
:keystore-alias "default"})]
(t/is (= (:sanoma-tunniste (valvonta-kaytto.suomifi-viestit/send-message-to-osapuoli! valvonta toimenpide osapuoli document config-with-keystore))
"ARA-05.03.02-2021-31-ETP-KV-1-2-PERSON-1")))))
Binary file added etp-backend/src/test/resources/suomifi/store.jks
Binary file not shown.
Loading

0 comments on commit 8456634

Please sign in to comment.