Skip to content

Commit

Permalink
unwrap db policy after stage
Browse files Browse the repository at this point in the history
When staging with a policy-activating opt (:did or :role), you need to be able to
subsequently transact with different policies for different users. This commit makes
that possible by resetting the db to a root db after checking policies.

If you want to interact with the db with a specific policy wrapping, you need to specify
the identity/role with which you are doing so every time.
  • Loading branch information
dpetran committed Jun 1, 2023
1 parent 4e901b0 commit c644193
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 55 deletions.
14 changes: 8 additions & 6 deletions src/fluree/db/json_ld/transact.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -402,20 +402,22 @@
(validate-rules tx-state)
<?
(policy/allowed? tx-state)
<?)))
<?
;; unwrap the policy
(dbproto/-rootdb))))

(defn stage
"Stages changes, but does not commit.
Returns async channel that will contain updated db or exception."
[db json-ld opts]
(go-try
(let [{tx :subject issuer :issuer} (or (<? (cred/verify json-ld))
{:subject json-ld})
opts* (cond-> opts issuer (assoc :did issuer))
db* (if-let [policy-opts (perm/policy-opts opts)]
(let [{tx :subject did :issuer} (or (<? (cred/verify json-ld))
{:subject json-ld})
opts* (cond-> opts did (assoc :did did))
db* (if-let [policy-opts (perm/policy-opts opts*)]
(<? (perm/wrap-policy db policy-opts))
db)
tx-state (->tx-state db* (assoc opts :issuer issuer))
tx-state (->tx-state db* (assoc opts* :issuer did))
flakes (if (q-parse/update? tx)
(<? (modify db tx tx-state))
(<? (insert db tx tx-state)))]
Expand Down
75 changes: 53 additions & 22 deletions test/fluree/db/json_ld/credential_test.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -110,14 +110,20 @@
#?(:clj
(deftest ^:integration cred-wrapped-transactions-and-queries
(let [conn @(fluree/connect {:method :memory})
ledger @(fluree/create conn "credentialtest" {:defaultContext [test-utils/default-str-context {"@base" "ledger:credentialtest/" "@vocab" ""}]})
db0 (fluree/db ledger)

ledger @(fluree/create conn "credentialtest" {:defaultContext
[test-utils/default-str-context
{"@base" "ledger:credentialtest/" "@vocab" ""}]})

root-user {"id" (:id auth) "name" "Daniel" "f:role" {"id" "role:cool"} "favnums" [1 2 3]}
pleb-user {"id" (:id pleb-auth) "name" "Plebian" "f:role" {"id" "role:notcool"}}
tx [root-user pleb-user]
db1 @(test-utils/transact ledger (async/<!! (cred/generate tx (:private auth))))
policy {"id" "rootPolicy"
"type" "f:Policy"
"f:targetNode" {"id" "f:allNodes"}
"f:allow" [{"f:targetRole" {"id" "role:cool"}
"f:action" [{"id" "f:view"} {"id" "f:modify"}]}]}
tx [root-user pleb-user policy]
;; can't use credentials until after an identity with a role has been created
db1 @(test-utils/transact ledger tx)

mdfn {"delete" [["?s" "name" "Daniel"]
["?s" "favnums" 1]]
Expand All @@ -126,14 +132,8 @@
["?s" "favnums" 5]
["?s" "favnums" 6]]
"where" [["?s" "@id" (:id auth)]]}
policy {"id" "rootPolicy"
"type" "f:Policy"
"f:targetNode" {"id" "f:allNodes"}
"f:allow" [{"f:targetRole" {"id" "role:cool"}
"f:action" [{"id" "f:view"} {"id" "f:modify"}]}]}

db2 @(test-utils/transact ledger (async/<!! (cred/generate mdfn (:private auth))))
;; need a policy to allow viewing
db3 @(test-utils/transact ledger (async/<!! (cred/generate policy (:private auth))))

query {"select" {"?s" ["*"]} "where" [["?s" "@id" (:id auth)]]}]
(is (= [root-user]
Expand All @@ -144,31 +144,28 @@
"modify transaction credential")

(is (= []
@(fluree/query db2 (async/<!! (cred/generate query (:private auth)))))
"query credential w/no policy allowing access")
(is (= []
@(fluree/query db3 (async/<!! (cred/generate query (:private pleb-auth)))))
@(fluree/query (fluree/db ledger) (async/<!! (cred/generate query (:private pleb-auth)))))
"query credential w/ policy forbidding access")
(is (= [{"id" (:id auth)
"name" "D"
"favnums" [2 3 4 5 6]
"f:role" {"id" "role:cool"}}]
@(fluree/query db3 (async/<!! (cred/generate query (:private auth)))))
@(fluree/query db2 (async/<!! (cred/generate query (:private auth)))))
"query credential w/ policy allowing access")

(is (= {"nums" [2 3 4 5 6],
"root" [{"id" (:id auth)
"name" "D",
"favnums" [2 3 4 5 6]
"f:role" {"id" "role:cool"}}]}
@(fluree/multi-query db3
"name" "D",
"favnums" [2 3 4 5 6]
"f:role" {"id" "role:cool"}}]}
@(fluree/multi-query db2
(async/<!! (cred/generate {"nums" {"select" "?nums" "where" [["?s" "favnums" "?nums"]]}
"root" query}
(:private auth)))))
"multiquery credential - allowing access")
(is (= {"nums" [],
"root" []}
@(fluree/multi-query db3
@(fluree/multi-query db2
(async/<!! (cred/generate {"nums" {"select" "?nums" "where" [["?s" "favnums" "?nums"]]}
"root" query}
(:private pleb-auth)))))
Expand All @@ -188,6 +185,40 @@
(:cause)))
"history query credential - forbidding access"))))

(comment
(def conn @(fluree/connect {:method :memory}))
(def ledger @(fluree/create conn "credentialtest" {:defaultContext
[test-utils/default-str-context
{"@base" "ledger:credentialtest/" "@vocab" ""}]}))

(def root-user {"id" (:id auth) "name" "Daniel" "f:role" {"id" "role:cool"} "favnums" [1 2 3]})
(def pleb-user {"id" (:id pleb-auth) "name" "Plebian" "f:role" {"id" "role:notcool"}})
(def policy {"id" "rootPolicy"
"type" "f:Policy"
"f:targetNode" {"id" "f:allNodes"}
"f:allow" [{"f:targetRole" {"id" "role:cool"}
"f:action" [{"id" "f:view"} {"id" "f:modify"}]}]})

(def tx [root-user pleb-user policy])
;; can't use credentials until after an identity with a role has been created
(def db0 (fluree/db ledger))
(def db1 @(fluree/stage db0 tx))

(def mdfn {"delete" [["?s" "name" "Daniel"]
["?s" "favnums" 1]]
"insert" [["?s" "name" "D"]
["?s" "favnums" 4]
["?s" "favnums" 5]
["?s" "favnums" 6]]
"where" [["?s" "@id" (:id auth)]]})

(def db2 @(test-utils/transact ledger (async/<!! (cred/generate mdfn (:private auth)))))

(def query {"select" {"?s" ["*"]} "where" [["?s" "@id" (:id auth)]]})


)

(comment
#?(:cljs

Expand Down
56 changes: 29 additions & 27 deletions test/fluree/db/policy/transact_test.clj
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,13 @@
{:f/path :schema/name
:f/allow [{:f/targetRole :ex/userRole
:f/action [:f/view :f/modify]}]}]}])]

(testing "Policy allowed modification"
(testing "using role + id"
(let [update-name @(fluree/stage db+policy {:id :ex/alice
:schema/email "[email protected]"}
(let [update-name @(fluree/stage db+policy
{:id :ex/alice
:schema/email "[email protected]"}
{:did alice-did
:role :ex/userRole})]

(is (= [{:id :ex/alice,
:rdf/type [:ex/User],
:schema/name "Alice",
Expand All @@ -94,33 +93,36 @@
:ex/location {:id nil}}]
@(fluree/query update-name
{:select {'?s [:*]}
:where [['?s :schema/name "Alice"]]}))
:where [['?s :schema/name "Alice"]]
:opts {:did alice-did}}))
"Alice should be allowed to update her own name.")))
(testing "using role only"
(let [update-price @(fluree/stage db+policy {:id :ex/widget
:schema/price 105.99}
{:role :ex/rootRole})]

(is (= [{:id :ex/widget,
:rdf/type [:ex/Product],
:schema/name "Widget",
:schema/price 105.99,
:schema/priceCurrency "USD"}]
@(fluree/query update-price
{:select {'?s [:*]}
:where [['?s :rdf/type :ex/Product]]}))
"Updated :schema/price should have been allowed, and entire product is visible in query."))
(let [update-name @(fluree/stage db+policy {:id :ex/widget
:schema/name "Widget2"}
{:role :ex/userRole})]
(let [update-price @(fluree/stage db+policy
{:id :ex/widget
:schema/price 105.99}
{:role :ex/rootRole})]

(is (= [{:rdf/type [:ex/Product]
:schema/name "Widget2"}]
@(fluree/query update-name
{:select {'?s [:*]}
:where [['?s :rdf/type :ex/Product]]}))
"Updated :schema/name should have been allowed, and only name is visible in query."))))
(is (= [{:id :ex/widget,
:rdf/type [:ex/Product],
:schema/name "Widget",
:schema/price 105.99,
:schema/priceCurrency "USD"}]
@(fluree/query update-price
{:select {'?s [:*]}
:where [['?s :rdf/type :ex/Product]]}))
"Updated :schema/price should have been allowed, and entire product is visible in query."))
(let [update-name @(fluree/stage db+policy
{:id :ex/widget
:schema/name "Widget2"}
{:role :ex/userRole})]

(is (= [{:rdf/type [:ex/Product]
:schema/name "Widget2"}]
@(fluree/query update-name
{:select {'?s [:*]}
:where [['?s :rdf/type :ex/Product]]
:opts {:role :ex/userRole}}))
"Updated :schema/name should have been allowed, and only name is visible in query."))))
(testing "Policy doesn't allow a modification"
(let [update-price @(fluree/stage db+policy {:id :ex/widget
:schema/price 42.99}
Expand Down

0 comments on commit c644193

Please sign in to comment.