From fb689b82754f584c810e0ec5562888daeaf07e4d Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 13:09:22 -0700 Subject: [PATCH 1/8] Restrict write access to initializer --- nix/sources.json | 2 +- src/distributed/assetstorage/assetstorage.mo | 36 +++++++++++--------- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/nix/sources.json b/nix/sources.json index 51df1aa1c3..f9d14cacbc 100644 --- a/nix/sources.json +++ b/nix/sources.json @@ -46,7 +46,7 @@ "motoko": { "ref": "master", "repo": "ssh://git@github.com/dfinity-lab/motoko", - "rev": "719ea2b3f54de4c4c867bd34a8284e52b49999a7", + "rev": "ea4585c4b3606689e224c89ae809cf50ab07df30", "type": "git" }, "napalm": { diff --git a/src/distributed/assetstorage/assetstorage.mo b/src/distributed/assetstorage/assetstorage.mo index 04d0af480b..6be0d388e1 100644 --- a/src/distributed/assetstorage/assetstorage.mo +++ b/src/distributed/assetstorage/assetstorage.mo @@ -1,31 +1,35 @@ import A "mo:base/AssocList"; import L "mo:base/List"; -import Prim "mo:prim"; +import O "mo:base/Option"; +import P "mo:prim"; actor { + public type Path = Text; + public type Contents = Blob; - var stored: A.AssocList = L.nil<(Path, Contents)>(); + private let initializer : Principal = P.caller(); + + private stable var db: A.AssocList = L.nil(); - func pathEq(a: Path, b: Path): Bool { + func eq(a: Path, b: Path): Bool { return a == b; }; - public func store(path : Path, contents: Contents) : async () { - let (newStored, _) = A.replace(stored, path, pathEq, ?contents); - stored := newStored; + public shared { caller } func store(path : Path, contents : Contents) { + if (caller != initializer) { + throw P.error("not authorized") + } else { + db := A.replace(db, path, eq, ?contents).0; + }; }; - public query func retrieve(path: Path): async Contents { - let result = A.find(stored, path, pathEq); - switch result { - case null { - // more than 8 chars treated as invalid UTF-8 - // TODO: https://github.com/dfinity-lab/sdk/issues/701 - throw Prim.error("notfound") - }; - case (?contents) { contents }; - } + public query func retrieve(path : Path) : async Contents { + O.get(A.find(db, path, eq), { + // more than 8 chars treated as invalid UTF-8 + // TODO: https://github.com/dfinity-lab/sdk/issues/701 + throw P.error("not found") + }); }; }; From 4a82ffdc5fe8cdf4952acf41dec794dcbbbdcd5e Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 13:43:43 -0700 Subject: [PATCH 2/8] Remove obsolete comment --- src/distributed/assetstorage/assetstorage.mo | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/distributed/assetstorage/assetstorage.mo b/src/distributed/assetstorage/assetstorage.mo index 6be0d388e1..a8d8bf6013 100644 --- a/src/distributed/assetstorage/assetstorage.mo +++ b/src/distributed/assetstorage/assetstorage.mo @@ -27,8 +27,6 @@ actor { public query func retrieve(path : Path) : async Contents { O.get(A.find(db, path, eq), { - // more than 8 chars treated as invalid UTF-8 - // TODO: https://github.com/dfinity-lab/sdk/issues/701 throw P.error("not found") }); }; From 0419e9049cfdbddebe7ec1000337349bfad7da0d Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 14:16:16 -0700 Subject: [PATCH 3/8] Fix issue with Motoko evaluation strategy --- src/distributed/assetstorage/assetstorage.mo | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/distributed/assetstorage/assetstorage.mo b/src/distributed/assetstorage/assetstorage.mo index a8d8bf6013..56fd834341 100644 --- a/src/distributed/assetstorage/assetstorage.mo +++ b/src/distributed/assetstorage/assetstorage.mo @@ -1,6 +1,5 @@ import A "mo:base/AssocList"; import L "mo:base/List"; -import O "mo:base/Option"; import P "mo:prim"; actor { @@ -26,8 +25,10 @@ actor { }; public query func retrieve(path : Path) : async Contents { - O.get(A.find(db, path, eq), { - throw P.error("not found") - }); + let result = A.find(db, path, eq); + switch result { + case null throw P.error("not found"); + case (?contents) contents; + }; }; }; From 0a9c9db435b19f9f3c0b4c2216e3ed41600771d8 Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 15:26:49 -0700 Subject: [PATCH 4/8] Attempt to reconsice key match --- e2e/bats/assetscanister.bash | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/e2e/bats/assetscanister.bash b/e2e/bats/assetscanister.bash index 7221c11de7..044ae3a1f2 100644 --- a/e2e/bats/assetscanister.bash +++ b/e2e/bats/assetscanister.bash @@ -42,5 +42,16 @@ teardown() { assert_eq '(vec { 88; 87; 86; })' assert_command_fail dfx canister call --query e2e_project_assets retrieve '("C")' -} + assert_command dfx canister call --update e2e_project_assets store '("AA", vec { 100; 107; 62; 9; })' + + # Keys are the same, but should be different + pwd + echo $HOME + cat $HOME/.dfinity/identity/creds.pem + ls -lah . + HOME=. dfx canister call --update e2e_project_assets store '("test", vec { 100; 107; 62; 9; })' + cat .dfinity/identity/creds.pem + exit 1 + +} From e919517e598918c33b0517764f93d3a4675c56dc Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 15:31:27 -0700 Subject: [PATCH 5/8] Include debug output in asset stroage module --- src/distributed/assetstorage/assetstorage.mo | 46 ++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/distributed/assetstorage/assetstorage.mo b/src/distributed/assetstorage/assetstorage.mo index 56fd834341..b61e828a17 100644 --- a/src/distributed/assetstorage/assetstorage.mo +++ b/src/distributed/assetstorage/assetstorage.mo @@ -1,7 +1,16 @@ import A "mo:base/AssocList"; +import D "mo:base/Debug"; import L "mo:base/List"; import P "mo:prim"; +////////////////// +import Array "mo:base/Array"; +import Iter "mo:base/Iter"; +import Option "mo:base/Option"; +import Prim "mo:prim"; +import Result "mo:base/Result"; +////////////////// + actor { public type Path = Text; @@ -17,6 +26,10 @@ actor { }; public shared { caller } func store(path : Path, contents : Contents) { + ////////////////// + D.print("Caller is " # principalToText(caller)); + D.print("Initializer is " # principalToText(initializer)); + ////////////////// if (caller != initializer) { throw P.error("not authorized") } else { @@ -31,4 +44,37 @@ actor { case (?contents) contents; }; }; + + ////////////////// + private func principalToText(p : Principal) : Text { + return encode(Iter.toArray(Prim.blobOfPrincipal(p).bytes())); + }; + + private type Result = Result.Result; + + private let base : Word8 = 0x10; + + private let symbols = [ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', + ]; + + /** + * Encode an array of unsigned 8-bit integers in hexadecimal format. + */ + private func encode(array : [Word8]) : Text { + Array.foldLeft(array, "", func (accum, w8) { + accum # encodeW8(w8); + }); + }; + + /** + * Encode an unsigned 8-bit integer in hexadecimal format. + */ + private func encodeW8(w8 : Word8) : Text { + let c1 = symbols[Prim.word8ToNat(w8 / base)]; + let c2 = symbols[Prim.word8ToNat(w8 % base)]; + Prim.charToText(c1) # Prim.charToText(c2); + }; + ////////////////// }; From 7591f82f73de4f3b0f66309010c935f0d5cf59c3 Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 16:34:21 -0700 Subject: [PATCH 6/8] Fix up e2e test and remove debug statements --- e2e/bats/assetscanister.bash | 10 +-- src/distributed/assetstorage/assetstorage.mo | 68 ++++---------------- 2 files changed, 15 insertions(+), 63 deletions(-) diff --git a/e2e/bats/assetscanister.bash b/e2e/bats/assetscanister.bash index 044ae3a1f2..4545d43470 100644 --- a/e2e/bats/assetscanister.bash +++ b/e2e/bats/assetscanister.bash @@ -45,13 +45,5 @@ teardown() { assert_command dfx canister call --update e2e_project_assets store '("AA", vec { 100; 107; 62; 9; })' - # Keys are the same, but should be different - pwd - echo $HOME - cat $HOME/.dfinity/identity/creds.pem - ls -lah . - HOME=. dfx canister call --update e2e_project_assets store '("test", vec { 100; 107; 62; 9; })' - cat .dfinity/identity/creds.pem - exit 1 - + HOME=. assert_command_fail dfx canister call --update e2e_project_assets store '("index.js", vec { 1; 2; 3; })' } diff --git a/src/distributed/assetstorage/assetstorage.mo b/src/distributed/assetstorage/assetstorage.mo index b61e828a17..20b9d6debc 100644 --- a/src/distributed/assetstorage/assetstorage.mo +++ b/src/distributed/assetstorage/assetstorage.mo @@ -1,15 +1,8 @@ -import A "mo:base/AssocList"; -import D "mo:base/Debug"; -import L "mo:base/List"; -import P "mo:prim"; - -////////////////// -import Array "mo:base/Array"; -import Iter "mo:base/Iter"; -import Option "mo:base/Option"; +import AssocList "mo:base/AssocList"; +import Debug "mo:base/Debug"; +import List "mo:base/List"; +import Prelude "mo:base/Prelude"; import Prim "mo:prim"; -import Result "mo:base/Result"; -////////////////// actor { @@ -17,64 +10,31 @@ actor { public type Contents = Blob; - private let initializer : Principal = P.caller(); + private let initializer : Principal = Prim.caller(); - private stable var db: A.AssocList = L.nil(); + private stable var db: AssocList.AssocList = List.nil(); func eq(a: Path, b: Path): Bool { return a == b; }; public shared { caller } func store(path : Path, contents : Contents) { - ////////////////// - D.print("Caller is " # principalToText(caller)); - D.print("Initializer is " # principalToText(initializer)); - ////////////////// if (caller != initializer) { - throw P.error("not authorized") + Debug.print("not authorized"); + Prelude.unreachable(); } else { - db := A.replace(db, path, eq, ?contents).0; + db := AssocList.replace(db, path, eq, ?contents).0; }; }; public query func retrieve(path : Path) : async Contents { - let result = A.find(db, path, eq); + let result = AssocList.find(db, path, eq); switch result { - case null throw P.error("not found"); + case null { + Debug.print("not found"); + Prelude.unreachable(); + }; case (?contents) contents; }; }; - - ////////////////// - private func principalToText(p : Principal) : Text { - return encode(Iter.toArray(Prim.blobOfPrincipal(p).bytes())); - }; - - private type Result = Result.Result; - - private let base : Word8 = 0x10; - - private let symbols = [ - '0', '1', '2', '3', '4', '5', '6', '7', - '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', - ]; - - /** - * Encode an array of unsigned 8-bit integers in hexadecimal format. - */ - private func encode(array : [Word8]) : Text { - Array.foldLeft(array, "", func (accum, w8) { - accum # encodeW8(w8); - }); - }; - - /** - * Encode an unsigned 8-bit integer in hexadecimal format. - */ - private func encodeW8(w8 : Word8) : Text { - let c1 = symbols[Prim.word8ToNat(w8 / base)]; - let c2 = symbols[Prim.word8ToNat(w8 % base)]; - Prim.charToText(c1) # Prim.charToText(c2); - }; - ////////////////// }; From 6dbc28acff05d4143bd9c9c412788f82e5c4bcb6 Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 16:51:16 -0700 Subject: [PATCH 7/8] Remove redundant test --- e2e/bats/assetscanister.bash | 2 -- 1 file changed, 2 deletions(-) diff --git a/e2e/bats/assetscanister.bash b/e2e/bats/assetscanister.bash index 4545d43470..99d0775538 100644 --- a/e2e/bats/assetscanister.bash +++ b/e2e/bats/assetscanister.bash @@ -43,7 +43,5 @@ teardown() { assert_command_fail dfx canister call --query e2e_project_assets retrieve '("C")' - assert_command dfx canister call --update e2e_project_assets store '("AA", vec { 100; 107; 62; 9; })' - HOME=. assert_command_fail dfx canister call --update e2e_project_assets store '("index.js", vec { 1; 2; 3; })' } From c2c4a616f85e2f27d563a5894ae3ea52cbde2175 Mon Sep 17 00:00:00 2001 From: Enzo Haussecker Date: Wed, 15 Jul 2020 17:38:16 -0700 Subject: [PATCH 8/8] Fix up error handling --- src/distributed/assetstorage/assetstorage.mo | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/distributed/assetstorage/assetstorage.mo b/src/distributed/assetstorage/assetstorage.mo index 20b9d6debc..eb6fa747e3 100644 --- a/src/distributed/assetstorage/assetstorage.mo +++ b/src/distributed/assetstorage/assetstorage.mo @@ -1,7 +1,6 @@ import AssocList "mo:base/AssocList"; -import Debug "mo:base/Debug"; +import Error "mo:base/Error"; import List "mo:base/List"; -import Prelude "mo:base/Prelude"; import Prim "mo:prim"; actor { @@ -18,10 +17,9 @@ actor { return a == b; }; - public shared { caller } func store(path : Path, contents : Contents) { + public shared { caller } func store(path : Path, contents : Contents) : async () { if (caller != initializer) { - Debug.print("not authorized"); - Prelude.unreachable(); + throw Error.reject("not authorized"); } else { db := AssocList.replace(db, path, eq, ?contents).0; }; @@ -31,8 +29,7 @@ actor { let result = AssocList.find(db, path, eq); switch result { case null { - Debug.print("not found"); - Prelude.unreachable(); + throw Error.reject("not found"); }; case (?contents) contents; };