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

Commit

Permalink
Log the reason why downloading aineisto was forbidden
Browse files Browse the repository at this point in the history
Implements: AE-1944
  • Loading branch information
solita-antti-mottonen committed Aug 21, 2023
1 parent 13f4942 commit 580e3c6
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 21 deletions.
3 changes: 2 additions & 1 deletion etp-backend/deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@
;; commons-discovery is needed by some other library dynamically at runtime
;; related to suomi.fi-viestit implementation
commons-discovery/commons-discovery {:mvn/version "0.5"}
com.sun.xml.ws/webservices-rt {:mvn/version "2.0.1"}}
com.sun.xml.ws/webservices-rt {:mvn/version "2.0.1"}
kovacnica/clojure.network.ip {:mvn/version "0.1.3"}}
:aliases {:dev {:extra-paths ["src/test/clj"
"src/test/resources"]
:extra-deps {eftest/eftest {:mvn/version "0.5.9"}
Expand Down
35 changes: 25 additions & 10 deletions etp-backend/src/main/clj/solita/etp/service/aineisto.clj
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
(ns solita.etp.service.aineisto
(:require
[clojure.java.jdbc :as jdbc]
[solita.etp.db :as db]
[solita.etp.service.luokittelu :as luokittelu-service]))
[clojure.java.jdbc :as jdbc]
[clojure.network.ip :as ip]
[clojure.tools.logging :as log]
[solita.etp.db :as db]
[solita.etp.service.luokittelu :as luokittelu-service])
(:import (java.time Instant)))

(db/require-queries 'aineisto)

Expand All @@ -18,7 +21,7 @@
(aineisto-db/insert-kayttaja-aineisto! db {:aineisto-id aineisto-id
:kayttaja-id kayttaja-id
:valid-until valid-until
:ip-address ip-address}))
:ip-address ip-address}))

(defn delete-kayttaja-access! [db kayttaja-id]
(aineisto-db/delete-kayttaja-access! db {:kayttaja-id kayttaja-id}))
Expand All @@ -27,16 +30,28 @@
(aineisto-db/delete-kayttaja-aineisto! db {:kayttaja-id kayttaja-id
:aineisto-id aineisto-id}))


(defn check-access [db kayttaja-id aineisto-id ip-address]
(contains? (into #{} (->> (aineisto-db/select-allowed-aineisto-id-for-ip db {:kayttaja-id kayttaja-id
:ip-address ip-address})
(map :aineisto-id)))
aineisto-id))
(defn- ip-matches? [request-ip allowed-network-or-ip]
(if (re-find #"/" allowed-network-or-ip)
(let [ip-address (ip/make-ip-address request-ip)
network (ip/make-network allowed-network-or-ip)]
(contains? network ip-address))
(= request-ip allowed-network-or-ip)))

(defn find-kayttaja-aineistot [db kayttaja-id]
(aineisto-db/select-all-kayttaja-aineistot db {:kayttaja-id kayttaja-id}))

(defn check-access [db kayttaja-id aineisto-id ip-address]
(let [all-aineistot (find-kayttaja-aineistot db kayttaja-id)
wanted-aineistot (filter #(= aineisto-id (:aineisto-id %)) all-aineistot)
correct-ip (filter #(ip-matches? ip-address (:ip-address %)) wanted-aineistot)
is-valid-now (filter #(.isAfter (:valid-until %) (Instant/now)) correct-ip)]
(cond
(empty? all-aineistot) (do (log/info (str "User " kayttaja-id " has no access to any aineisto")) false)
(empty? wanted-aineistot) (do (log/info (str "User " kayttaja-id "has no access to aineisto " aineisto-id)) false)
(empty? correct-ip) (do (log/info (str "User " kayttaja-id " has no access to aineisto " aineisto-id " from IP " ip-address)) false)
(empty? is-valid-now) (do (log/info (str "User " kayttaja-id " access to aineisto " aineisto-id " has expired")) false)
:else true)))

(defn set-kayttaja-aineistot! [db kayttaja-id aineistot]
;; Only 10 ip addresses are allowed to access the aineistot
(when (> (count aineistot) 10)
Expand Down
95 changes: 95 additions & 0 deletions etp-backend/src/test/clj/solita/etp/aineisto_api_test.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
(ns solita.etp.aineisto-api-test
(:require
[clojure.data.codec.base64 :as b64]
[clojure.test :as t]
[ring.mock.request :as mock]
[solita.etp.service.aineisto :as aineisto-service]
[solita.etp.test-data.kayttaja :as test-kayttajat]
[solita.etp.test-system :as ts])
(:import (java.time Duration Instant)))

(def allowed-network "192.168.1.1/32")
(def user-ip "192.168.1.1")

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

(t/deftest fetch-aineisto
(let [kayttaja-id (test-kayttajat/insert! (->> (test-kayttajat/generate-adds 1)
(map #(merge % test-kayttajat/aineistoasiakas))
(map #(assoc %
:email "yhteyshenkilo@example.com"
:api-key "password"
))))
api-key (->> "yhteyshenkilo@example.com:password"
.getBytes
(b64/encode)
(String.))
auth-header (str "Basic " api-key)
aineisto-url "/api/external/aineistot/1/energiatodistukset.csv"]
(aineisto-service/set-kayttaja-aineistot! ts/*db* kayttaja-id #{{:aineisto-id 1
:valid-until (.plus (Instant/now) (Duration/ofSeconds 86400))
:ip-address allowed-network}})
(t/testing "User with access receives redirect response"
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" user-ip)
(mock/header "Accept" "application/json")))]
(t/is (some? (-> response :headers (get "Location"))))
(t/is (= (:status response) 302))))
(t/testing "Anonymous user can't fetch aineisto"
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "X-Forwarded-For" user-ip)
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 401))))
(t/testing "User with expired access can't fetch aineisto"
(aineisto-service/set-kayttaja-aineistot! ts/*db* kayttaja-id #{{:aineisto-id 1
:valid-until (.minus (Instant/now) (Duration/ofSeconds 86400))
:ip-address allowed-network}})
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" user-ip)
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 403))))
(t/testing "User with wrong IP can't fetch aineisto"
(aineisto-service/set-kayttaja-aineistot! ts/*db* kayttaja-id #{{:aineisto-id 1
:valid-until (.plus (Instant/now) (Duration/ofSeconds 86400))
:ip-address "192.168.1.2"}})
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" user-ip)
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 403))))
(t/testing "User can't access aineisto with wrong id"
(aineisto-service/set-kayttaja-aineistot! ts/*db* kayttaja-id #{{:aineisto-id 2
:valid-until (.plus (Instant/now) (Duration/ofSeconds 86400))
:ip-address allowed-network}})
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" user-ip)
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 403))))
(t/testing "Access can be just an IP"
(aineisto-service/set-kayttaja-aineistot! ts/*db* kayttaja-id #{{:aineisto-id 1
:valid-until (.plus (Instant/now) (Duration/ofSeconds 86400))
:ip-address "192.168.1.1"}})
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" user-ip)
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 302))))
(t/testing "IP subnetting works"
(aineisto-service/set-kayttaja-aineistot! ts/*db* kayttaja-id #{{:aineisto-id 1
:valid-until (.plus (Instant/now) (Duration/ofSeconds 86400))
:ip-address "192.168.1.0/24"}})
(t/testing "with allowed ip"
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" "192.168.1.100")
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 302))))
(t/testing "with not allowed ip"
(let [response (ts/handler (-> (mock/request :get aineisto-url)
(mock/header "Authorization" auth-header)
(mock/header "X-Forwarded-For" "192.168.2.100")
(mock/header "Accept" "application/json")))]
(t/is (= (:status response) 403)))))))
30 changes: 20 additions & 10 deletions etp-backend/src/test/clj/solita/etp/service/aineisto_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@


(def user-id-with-allowed-ip 66666)
(def allowed-ip "192.168.11.1/32")
(def allowed-network "192.168.11.1/32")
(def actual-ip "192.168.11.1")
(def allowed-aineisto-type 1)

(t/deftest check-access-test
Expand All @@ -35,25 +36,25 @@
allowed-aineisto-type
(-> (Instant/now)
(.plusSeconds 864000))
allowed-ip])
allowed-network])

(t/testing "User has access with the given ip-address"
(t/is (true? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, allowed-ip))))
(t/testing "User has access with the given ip address"
(t/is (true? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, actual-ip))))

(t/testing "User has no access to another aineistotype even if the ip is allowed"
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, 2, "192.168.11.2/32"))))
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, 2, actual-ip))))

(t/testing "User doesn't have access with another ip than the allowed one"
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, "192.168.11.2/32"))))
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, "192.168.1.2"))))

(t/testing "User can't access aineistot from an ip that is allowed for different user"
(jdbc/execute! ts/*db* ["insert into kayttaja (id, rooli_id, etunimi, sukunimi, email, puhelin) VALUES (?, 4, 'Ei testiaineistoa', 'testikäyttäjä', 'testi2@solita.fi', '')", 666667])

(t/is (false? (aineisto/check-access ts/*db*, 666667, allowed-aineisto-type, allowed-ip))))
(t/is (false? (aineisto/check-access ts/*db*, 666667, allowed-aineisto-type, actual-ip))))

(t/testing "User can't access aineistot after access has been removed"
(aineisto/delete-kayttaja-access! ts/*db* user-id-with-allowed-ip)
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, allowed-ip))))
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, actual-ip))))

(t/testing "User can't access aineistot when the access has expired"
;; Add access that has valid_until in the past
Expand All @@ -62,5 +63,14 @@
allowed-aineisto-type
(-> (Instant/now)
(.minusSeconds 5))
allowed-ip])
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, allowed-ip)))))
allowed-network])
(t/is (false? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, actual-ip))))

(t/testing "Access can be just an ip address"
(jdbc/execute! ts/*db* ["insert into kayttaja_aineisto (kayttaja_id, aineisto_id, valid_until, ip_address) VALUES (?, ?, ?, ?)"
user-id-with-allowed-ip
allowed-aineisto-type
(-> (Instant/now)
(.plusSeconds 86400))
actual-ip])
(t/is (true? (aineisto/check-access ts/*db*, user-id-with-allowed-ip, allowed-aineisto-type, actual-ip)))))
1 change: 1 addition & 0 deletions etp-backend/src/test/clj/solita/etp/test_data/kayttaja.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
(def patevyyden-toteaja {:rooli 1})
(def paakayttaja {:rooli 2})
(def laskuttaja {:rooli 3})
(def aineistoasiakas {:rooli 4})
(def public nil)

(def KayttajaAdd
Expand Down

0 comments on commit 580e3c6

Please sign in to comment.