Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update: User inter-canister calls [SDK-702] #6

Merged
24 commits merged into from
Feb 12, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dfx.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"output": "canisters"
},
"start": {
"address": "0.0.0.0",
"address": "127.0.0.1",
"port": 8000
}
},
"version": 1
"dfx": "0.4.12"
}
3,941 changes: 2,047 additions & 1,894 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
"bootstrap": "^4.3.1",
"copy-webpack-plugin": "^5.1.1",
"jquery": "^3.4.1",
"jquery-backstretch": "^2.1.18",
"popper.js": "^1.16.0",
"tweetnacl": "^1.0.1",
"typed.js": "^2.0.11",
"wowjs": "^1.1.3"
},
"devDependencies": {
"css-loader": "^3.4.2",
"style-loader": "^1.1.2",
"terser-webpack-plugin": "2.2.2",
"webpack": "4.41.3",
"webpack-cli": "3.3.10"
Expand Down
45 changes: 45 additions & 0 deletions src/graph/entries.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Array "mo:stdlib/array.mo";
import Blob "mo:stdlib/blob.mo";
import HashMap "mo:stdlib/hashMap.mo";
import Hash "mo:stdlib/hash.mo";
import Iter "mo:stdlib/iter.mo";
import List "mo:stdlib/list.mo";
import Nat "mo:stdlib/nat.mo";
import Option "mo:stdlib/option.mo";

import Types "./types.mo";

module {
type Entry = Types.Entry;
type EntryId = Types.EntryId;

public class Entries () {

func natEq (x : Nat32, y : Nat32) : Bool { x == y };
func hashEntryId (x : EntryId) : Hash.Hash { Nat.toWord32(Nat.fromNat32(x)) };
let hashMap = HashMap.HashMap<EntryId, Entry>(1, natEq, hashEntryId);

public func addConnection (fromUser : EntryId, toUser : EntryId) : () {
let entry = getEntry(fromUser);
let updated : Entry = {
id = entry.id;
connections = Array.append<EntryId>(entry.connections, [toUser]);
invitations = entry.invitations;
};
ignore hashMap.set(fromUser, updated);
};

func getEntry (entryId : EntryId) : Entry {
let existing = hashMap.get(entryId);
switch (existing) {
case (?existing) { existing };
case (null) { { id = entryId; connections = []; invitations = []; } };
};
};

public func getConnections (userId : EntryId) : [EntryId] {
let entry = Option.unwrap<Entry>(hashMap.get(userId));
entry.connections
};
};
};
20 changes: 20 additions & 0 deletions src/graph/main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,32 @@ import Result "mo:stdlib/result.mo";
import Trie "mo:stdlib/trie.mo";
import Util "../common/util.mo";

import Entries "./entries.mo";
import Types "./types.mo";

type Entry = Types.Entry;
type EntryId = Types.EntryId;

actor Graph {

type List<T> = List.List<T>;
type Result<Ok, Err> = Result.Result<Ok, Err>;
type Trie<Key, Value> = Trie.Trie<Key, Value>;

var entries : Entries.Entries = Entries.Entries();

public func healthcheck () : async Bool { true };

// Connections

public func connect (caller : Nat32, userId : Nat32) : async () {
entries.addConnection(caller, userId);
};

public func getConnections (userId : EntryId) : async [EntryId] {
entries.getConnections(userId)
};

/**
* The type of a user identifier.
*/
Expand Down
11 changes: 11 additions & 0 deletions src/graph/types.mo
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Hash "mo:stdlib/hash.mo";

module {
public type EntryId = Nat32;

public type Entry = {
id : EntryId;
connections : [EntryId];
invitations : [EntryId];
};
};
57 changes: 51 additions & 6 deletions src/profile/directory.mo
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import Array "mo:stdlib/array.mo";
import Blob "mo:stdlib/blob.mo";
import HashMap "mo:stdlib/hashMap.mo";
import Hash "mo:stdlib/hash.mo";
import Iter "mo:stdlib/iter.mo";
import List "mo:stdlib/list.mo";
import Nat "mo:stdlib/nat.mo";
import Option "mo:stdlib/option.mo";

import Types "./types.mo";

module {
type NewProfile = Types.NewProfile;
type Profile = Types.Profile;
type PrincipalId = Types.PrincipalId;

public class Directory () {
func passthrough (hash : PrincipalId) : PrincipalId { hash };
let hashMap = HashMap.HashMap<PrincipalId, Profile>(1, Hash.Hash.hashEq, passthrough);
var seeded = false;

public func get (userId : PrincipalId) : ?Profile {
hashMap.get(userId)
};

public func set (userId : PrincipalId, _profile : Profile) : () {
public func createOne (userId : PrincipalId, _profile : NewProfile) : () {
let profile : Profile = {
id = userId;
firstName = _profile.firstName;
Expand All @@ -30,7 +31,23 @@ module {
ignore hashMap.set(userId, profile);
};

public func search (term : Text) : [Profile] {
public func updateOne (userId : PrincipalId, profile : Profile) : () {
ignore hashMap.set(userId, profile);
};

public func findOne (userId : PrincipalId) : ?Profile {
hashMap.get(userId)
};

public func findMany (userIds : [PrincipalId]) : [Profile] {
func getProfile (userId : PrincipalId) : Profile {
Option.unwrap<Profile>(hashMap.get(userId))
};
Array.map<PrincipalId, Profile>(getProfile, userIds)
};

public func findBy (term : Text) : [Profile] {
// @TODO: Use HashMap.mapFilter
var profiles : [Profile] = [];
for ((id, profile) in hashMap.iter()) {
let fullName = profile.firstName # " " # profile.lastName;
Expand Down Expand Up @@ -60,5 +77,33 @@ module {
};
false
};

public func seed () : () {
if (seeded) { return; };

let table : [[Text]] = [
["Dominic", "Williams", "Founder & Chief Scientist", "DFINITY", "Working to reinvent the Internet as a shared supercomputer"],
["Diego", "Prats", "Director de Producto", "DFINITY", "Building a public computing platform to rival AWS, Azure, and Google Cloud."],
["Stanley", "Jones", "Engineering Manager", "DFINITY", "Making it fun and easy to build apps for the Internet Computer"],
["Olive", "Menjo", "Senior Dog", "DFINITY", "Dog stuff"]
];

let profiles : [Profile] = Array.mapWithIndex<[Text], Profile>(makeProfile, table);
for (profile in profiles.vals()) { ignore hashMap.set(profile.id, profile); };

seeded := true;
};

func makeProfile (row : [Text], index : Nat) : Profile {
let profile : Profile = {
id = Nat.toWord32(1000 + index);
firstName = row[0];
lastName = row[1];
title = row[2];
company = row[3];
experience = row[4];
};
profile
};
};
};
75 changes: 69 additions & 6 deletions src/profile/main.mo
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,91 @@
* Stability : Experimental
*/

import Array "mo:stdlib/array.mo";
import Blob "mo:stdlib/blob.mo";
import Nat "mo:stdlib/nat.mo";
import Option "mo:stdlib/option.mo";

import Graph "canister:graph";

import Directory "./directory.mo";
import Types "./types.mo";

type NewProfile = Types.NewProfile;
type Profile = Types.Profile;
type PrincipalId = Types.PrincipalId;

actor Profile {
var directory : Directory.Directory = Directory.Directory();
directory.seed();

public func healthcheck () : async Bool {
let isGraphAlive : Bool = await Graph.healthcheck();
true and isGraphAlive
};

// Profiles

public shared { caller } func create (profile : NewProfile) : async PrincipalId {
let newUserId = getUserId(caller);
directory.createOne(newUserId, profile);
newUserId
};

public shared { caller } func set (profile : Profile) : async PrincipalId {
let userId : PrincipalId = Blob.hash(caller);
directory.set(userId, profile);
userId
public shared { caller } func update (profile : Profile) : async () {
if (hasAccess(getUserId(caller), profile)) {
directory.updateOne(profile.id, profile);
};
};

public shared { caller } func setMany (profiles : [Profile]) : async () {
if (isAdmin(getUserId(caller))) {
for (profile in profiles.vals()) {
directory.updateOne(profile.id, profile);
};
};
};

public shared query { caller } func getOwn () : async ?Profile {
directory.findOne(getUserId(caller))
};

public query func get (userId : PrincipalId) : async ?Profile {
directory.get(userId)
directory.findOne(userId)
};

public query func search (term : Text) : async [Profile] {
directory.search(term)
directory.findBy(term)
};

// Connections

public shared { caller } func connect (userId : PrincipalId) : async () {
await Graph.connect(toEntryId(Blob.hash(caller)), toEntryId(userId));
};

public query func getConnections (userId : PrincipalId) : async [Profile] {
let entryIds = await Graph.getConnections(toEntryId(userId));
let profileIds = Array.map<Nat32, PrincipalId>(fromEntryId, entryIds);
directory.findMany(profileIds)
};

func toEntryId (userId : PrincipalId) : Nat32 { Nat.toNat32(Nat.fromWord32(userId)) };
func fromEntryId (entryId : Nat32) : PrincipalId { Nat.toWord32(Nat.fromNat32(entryId)) };

// Authorization

let adminIds : [PrincipalId] = [];

func getUserId (caller : Blob) : PrincipalId { Blob.hash(caller) };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a gaping security hole: The Blob hash is not a cryptographic one, and (especially with user-picked identities) easy to create collisions for.

I think you simply want the PrincipalId to be the blob (or the new abstract PrincipalId created in dfinity/motoko#1128)


// @TODO: use Array.includes
func isAdmin (userId : PrincipalId) : Bool {
func identity (x : PrincipalId) : Bool { x == userId };
Option.isSome<PrincipalId>(Array.find<PrincipalId>(identity, adminIds))
};

func hasAccess (userId : PrincipalId, profile : Profile) : Bool {
userId == profile.id or isAdmin(userId)
};
};
4 changes: 3 additions & 1 deletion src/profile/public/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,9 @@ padding: 0;
-moz-justify-content: center;
-ms-justify-content: center;
-o-justify-content: center;
-webkit-justify-content: center;
-webkit-justify-content: center;
background: url('https://enzohaussecker.s3-us-west-2.amazonaws.com/images/handshake.jpg') no-repeat center center fixed;
background-size: cover;
}

.dim {
Expand Down
Loading