def test_MarketProfile_update_success(self): u = objects.Profile() u.about = "updated world" p = Profile(self.db) p.update(u) updated_user = p.get() self.assertEqual("updated world", updated_user.about)
def send_message(self, receiving_node, public_key, message_type, message, subject=None): """ Sends a message to another node. If the node isn't online it will be placed in the dht for the node to pick up later. """ pro = Profile().get() if len(message) > 1500: return p = objects.Plaintext_Message() p.sender_guid = self.kserver.node.id p.signed_pubkey = self.kserver.node.signed_pubkey p.encryption_pubkey = PrivateKey(self.signing_key.encode()).public_key.encode() p.type = message_type p.message = message if subject is not None: p.subject = subject if pro.handle: p.handle = pro.handle if pro.avatar_hash: p.avatar_hash = pro.avatar_hash p.timestamp = int(time.time()) signature = self.signing_key.sign(p.SerializeToString())[:64] p.signature = signature skephem = PrivateKey.generate() pkephem = skephem.public_key.encode(nacl.encoding.RawEncoder) box = Box(skephem, PublicKey(public_key, nacl.encoding.HexEncoder)) nonce = nacl.utils.random(Box.NONCE_SIZE) ciphertext = box.encrypt(p.SerializeToString(), nonce) def get_response(response): if not response[0]: self.kserver.set(receiving_node.id, pkephem, ciphertext) self.protocol.callMessage(receiving_node, pkephem, ciphertext).addCallback(get_response)
def rpc_follow(self, sender, proto, signature): self.log.info("Follow request from %s" % sender.id.encode("hex")) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.signed_pubkey[64:]) verify_key.verify(proto, signature) f = Followers.Follower() f.ParseFromString(proto) if f.guid != sender.id: raise Exception('GUID does not match sending node') if f.following != self.proto.guid: raise Exception('Following wrong node') f.signature = signature FollowData().set_follower(f) proto = Profile().get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.nsfw = proto.nsfw return [ "True", m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64] ] except Exception: self.log.warning("Failed to validate follower") return ["False"]
def test_MarketProfile_replace_social_no_proof(self): p = Profile(self.db) p.add_social_account("FACEBOOK", "test_updated_username") u = p.get() self.assertEqual(1, len(u.social)) self.assertEqual(0, u.social[0].type) self.assertEqual('test_updated_username', u.social[0].username)
def test_MarketProfile_add_social_invalid(self): p = Profile(self.db) p.add_social_account("TEST", "test_twitter_username") u = p.get() self.assertEqual(1, len(u.social)) self.assertEqual(0, u.social[0].type) self.assertEqual('test_fb_username', u.social[0].username)
def rpc_follow(self, sender, proto, signature): self.log.info("received follow request from %s" % sender) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.pubkey) verify_key.verify(proto, signature) f = Followers.Follower() f.ParseFromString(proto) if f.guid != sender.id: raise Exception('GUID does not match sending node') if f.following != self.node.id: raise Exception('Following wrong node') f.signature = signature self.db.follow.set_follower(f) proto = Profile(self.db).get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.short_description = proto.short_description m.nsfw = proto.nsfw for listener in self.listeners: try: verifyObject(NotificationListener, listener) listener.notify(sender.id, f.metadata.handle, "follow", "", "", f.metadata.avatar_hash) except DoesNotImplement: pass return ["True", m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64]] except Exception: self.log.warning("failed to validate follower") return ["False"]
def test_MarketProfile_add_pgp_key_success(self): p = Profile(self.db) self.assertTrue( p.add_pgp_key(self.PUBLIC_KEY, self.SIGNATURE, self.VALID_GUID)) u = p.get() self.assertEqual(self.SIGNATURE, u.pgp_key.signature) self.assertEqual(self.PUBLIC_KEY, u.pgp_key.public_key)
def test_MarketProfile_get_success(self): p = Profile(self.db).get() self.assertEqual('test_name', p.name) self.assertEqual(2, p.location) self.assertEqual('hello world', p.about) self.assertEqual(1, len(p.social)) self.assertEqual(0, p.social[0].type) self.assertEqual('test_fb_username', p.social[0].username)
def get_profile(self, request): def parse_profile(profile): if profile is not None: profile_json = { "profile": { "name": profile.name, "location": str(CountryCode.Name(profile.location)), "enryption_key": profile.encryption_key.encode("hex"), "nsfw": profile.nsfw, "vendor": profile.vendor, "moderator": profile.moderator, "handle": profile.handle, "about": profile.about, "website": profile.website, "email": profile.email, "primary_color": profile.primary_color, "secondary_color": profile.secondary_color, "background_color": profile.background_color, "text_color": profile.text_color, "pgp_key": profile.pgp_key.publicKey, "avatar_hash": profile.avatar_hash.encode("hex"), "header_hash": profile.header_hash.encode("hex"), "social_accounts": {} } } if "guid" in request.args: profile_json["profile"]["guid"] = request.args["guid"][0] else: profile_json["profile"]["guid"] = KeyChain().guid.encode( "hex") for account in profile.social: profile_json["profile"]["social_accounts"][str( objects.Profile.SocialAccount.SocialType.Name( account.type)).lower()] = { "username": account.username, "proof_url": account.proof_url } request.setHeader('content-type', "application/json") request.write(json.dumps(profile_json, indent=4)) request.finish() else: request.write(NoResource().render(request)) request.finish() if "guid" in request.args: def get_node(node): if node is not None: self.mserver.get_profile(node).addCallback(parse_profile) else: request.write(NoResource().render(request)) request.finish() self.kserver.resolve(unhexlify( request.args["guid"][0])).addCallback(get_node) else: parse_profile(Profile().get()) return server.NOT_DONE_YET
def test_MarketProfile_add_social_no_proof(self): p = Profile(self.db) p.add_social_account("TWITTER", "test_twitter_username") u = p.get() self.assertEqual(2, len(u.social)) self.assertEqual(0, u.social[0].type) self.assertEqual('test_fb_username', u.social[0].username) self.assertEqual(1, u.social[1].type) self.assertEqual('test_twitter_username', u.social[1].username)
def unmake_moderator(self): """ Deletes our moderator entry from the network. """ key = digest(self.kserver.node.getProto().SerializeToString()) signature = self.signing_key.sign(key)[:64] self.kserver.delete("moderators", key, signature) Profile(self.db).remove_field("moderator")
def rpc_get_profile(self, sender): self.log.info("Fetching profile") self.router.addContact(sender) try: proto = Profile().get(True) return [proto, self.signing_key.sign(proto)[:64]] except Exception: self.log.error("Unable to load the profile") return ["None"]
def rpc_get_profile(self, sender): self.log.info("serving profile to %s" % sender) self.router.addContact(sender) try: proto = Profile(self.db).get(True) return [proto, self.signing_key.sign(proto)[:64]] except Exception: self.log.error("unable to load the profile") return None
def rpc_get_profile(self, sender): self.log.info("serving profile to %s" % sender) self.audit.record(sender.id.encode("hex"), "GET_PROFILE") self.router.addContact(sender) try: proto = Profile(self.db).get(True) return [proto, self.signing_key.sign(proto)[:64]] except Exception: self.log.error("unable to load the profile") return None
def addsocialaccount(): parser = argparse.ArgumentParser( description="Add a social media account to the profile.", usage='''usage: networkcli.py addsocialaccout -t TYPE, -u USERNAME, -p PROOF''') parser.add_argument('-t', '--type', help="the type of account") parser.add_argument('-u', '--username', help="the username") parser.add_argument('-p', '--proof', help="the proof url") args = parser.parse_args(sys.argv[2:]) p = Profile() p.add_social_account(args.type, args.username, args.proof)
def parse_response(moderators): if moderators is not None: m.clear_all() def parse_profile(profile, node): if profile is not None: # TODO: should check signatures here before entering in database m.save_moderator(node.id.encode("hex"), node.pubkey, profile.bitcoin_key.public_key, profile.bitcoin_key.signature, profile.name, profile.avatar_hash, profile.moderation_fee, profile.handle, profile.short_description) moderator = { "id": message_id, "moderator": { "guid": node.id.encode("hex"), "name": profile.name, "handle": profile.handle, "short_description": profile.short_description, "avatar_hash": profile.avatar_hash.encode("hex"), "about": profile.about, "fee": profile.moderation_fee } } self.transport.write( str( bleach.clean(json.dumps(moderator, indent=4), tags=ALLOWED_TAGS))) else: m.delete_moderator(node.id) for mod in moderators: try: val = objects.Value() val.ParseFromString(mod) n = objects.Node() n.ParseFromString(val.serializedData) node_to_ask = Node( n.guid, n.nodeAddress.ip, n.nodeAddress.port, n.publicKey, None if not n.HasField("relayAddress") else (n.relayAddress.ip, n.relayAddress.port), n.natType, n.vendor) if n.guid == KeyChain(self.factory.db).guid: parse_profile( Profile(self.factory.db).get(), node_to_ask) else: self.factory.mserver.get_profile(node_to_ask)\ .addCallback(parse_profile, node_to_ask) except Exception: pass
def delete_social_account(self, request): try: p = Profile(self.db) if "account_type" in request.args: p.remove_social_account(request.args["account_type"][0]) request.write(json.dumps({"success": True})) request.finish() return server.NOT_DONE_YET except Exception, e: request.write(json.dumps({"success": False, "reason": e.message}, indent=4)) request.finish() return server.NOT_DONE_YET
def rpc_get_listings(self, sender): self.log.info("serving store listings to %s" % sender) self.router.addContact(sender) try: p = Profile(self.db).get() l = Listings() l.ParseFromString(self.db.ListingsStore().get_proto()) l.handle = p.handle l.avatar_hash = p.avatar_hash return [l.SerializeToString(), self.signing_key.sign(l.SerializeToString())[:64]] except Exception: self.log.warning("could not find any listings in the database") return None
def make_moderator(self): """ Set self as a moderator in the DHT. """ u = objects.Profile() k = u.PublicKey() k.public_key = bitcoin.bip32_deserialize(KeyChain(self.db).bitcoin_master_pubkey)[5] k.signature = self.signing_key.sign(k.public_key)[:64] u.bitcoin_key.MergeFrom(k) u.moderator = True Profile(self.db).update(u) proto = self.kserver.node.getProto().SerializeToString() self.kserver.set(digest("moderators"), digest(proto), proto)
def rpc_get_user_metadata(self, sender): self.log.info("serving user metadata to %s" % sender) self.router.addContact(sender) try: proto = Profile(self.db).get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.short_description = proto.short_description m.avatar_hash = proto.avatar_hash m.nsfw = proto.nsfw return [m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64]] except Exception: self.log.error("unable to load profile metadata") return None
def add_social_account(self, request): try: p = Profile(self.db) if "account_type" in request.args and "username" in request.args and "proof" in request.args: p.add_social_account(request.args["account_type"][0], request.args["username"][0], request.args["proof"][0] if "proof" in request.args else None) else: raise Exception("Missing required fields") request.write(json.dumps({"success": True})) request.finish() return server.NOT_DONE_YET except Exception, e: request.write(json.dumps({"success": False, "reason": e.message}, indent=4)) request.finish() return server.NOT_DONE_YET
def make_moderator(self): """ Set self as a moderator in the DHT. """ u = objects.Profile() k = u.PublicKey() k.public_key = unhexlify(bitcointools.bip32_extract_key(KeyChain(self.db).bitcoin_master_pubkey)) k.signature = self.signing_key.sign(k.public_key)[:64] u.bitcoin_key.MergeFrom(k) u.moderator = True Profile(self.db).update(u) proto = self.kserver.node.getProto().SerializeToString() self.kserver.set(digest("moderators"), digest(proto), proto) self.log.info("setting self as moderator on the network")
def parse_response(moderators): if moderators is not None: m.clear_all() def parse_profile(profile, node): if profile is not None: m.save_moderator( node.id.encode("hex"), node.signed_pubkey, profile.encryption_key.public_key, profile.encryption_key.signature, profile.bitcoin_key.public_key, profile.bitcoin_key.signature, profile.name, profile.avatar_hash, profile.moderation_fee, profile.handle, profile.short_description) moderator = { "id": message_id, "moderator": { "guid": node.id.encode("hex"), "name": profile.name, "handle": profile.handle, "short_description": profile.short_description, "avatar_hash": profile.avatar_hash.encode("hex"), "about": profile.about, "fee": profile.moderation_fee } } self.sendMessage(json.dumps(moderator, indent=4), False) else: m.delete_moderator(node.id) for mod in moderators: try: val = objects.Value() val.ParseFromString(mod) n = objects.Node() n.ParseFromString(val.serializedData) node_to_ask = Node(n.guid, n.ip, n.port, n.signedPublicKey) if n.guid == KeyChain(self.factory.db).guid: parse_profile( Profile(self.factory.db).get(), node_to_ask) else: self.factory.mserver.get_profile(node_to_ask)\ .addCallback(parse_profile, node_to_ask) except Exception: pass
def addpgpkey(): parser = argparse.ArgumentParser( description="Add a pgp key to the profile.", usage='''usage: networkcli.py addpgpkey -k KEY, -s SIGNATURE''') parser.add_argument('-k', '--key', help="path to the key file") parser.add_argument('-s', '--signature', help="path to the signature file") args = parser.parse_args(sys.argv[2:]) with open(args.key, "r") as filename: key = filename.read() with open(args.signature, "r") as filename: sig = filename.read() p = Profile() print p.add_pgp_key(key, sig, KeyChain().guid.encode("hex"))
def rpc_get_user_metadata(self, sender): self.log.info("Fetching metadata") self.router.addContact(sender) try: proto = Profile().get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.nsfw = proto.nsfw return [ m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64] ] except Exception: self.log.error("Unable to get the profile metadata") return ["None"]
def rpc_get_contract_metadata(self, sender, contract_hash): self.log.info("serving metadata for contract %s to %s" % (contract_hash.encode("hex"), sender)) self.router.addContact(sender) try: proto = self.db.listings.get_proto() p = Profile(self.db).get() l = Listings() l.ParseFromString(proto) for listing in l.listing: if listing.contract_hash == contract_hash: listing.avatar_hash = p.avatar_hash listing.handle = p.handle ser = listing.SerializeToString() return [ser, self.signing_key.sign(ser)[:64]] except Exception: self.log.warning("could not find metadata for contract %s" % contract_hash.encode("hex")) return None
def follow(self, node_to_follow): """ Sends a follow message to another node in the network. The node must be online to receive the message. The message contains a signed, serialized `Follower` protobuf object which the recipient will store and can send to other nodes, proving you are following them. The response is a signed `Metadata` protobuf that will store in the db. """ def save_to_db(result): if result[0] and result[1][0] == "True": try: u = objects.Following.User() u.guid = node_to_follow.id u.signed_pubkey = node_to_follow.signed_pubkey m = objects.Metadata() m.ParseFromString(result[1][1]) u.metadata.MergeFrom(m) u.signature = result[1][2] pubkey = node_to_follow.signed_pubkey[64:] verify_key = nacl.signing.VerifyKey(pubkey) verify_key.verify(result[1][1], result[1][2]) self.db.FollowData().follow(u) return True except Exception: return False else: return False proto = Profile(self.db).get(False) m = objects.Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.short_description = proto.short_description m.nsfw = proto.nsfw f = objects.Followers.Follower() f.guid = self.kserver.node.id f.following = node_to_follow.id f.signed_pubkey = self.kserver.node.signed_pubkey f.metadata.MergeFrom(m) signature = self.signing_key.sign(f.SerializeToString())[:64] d = self.protocol.callFollow(node_to_follow, f.SerializeToString(), signature) self.log.info("sending follow request to %s" % node_to_follow) return d.addCallback(save_to_db)
def rpc_follow(self, sender, proto, signature): self.log.info("received follow request from %s" % sender) self.router.addContact(sender) try: verify_key = nacl.signing.VerifyKey(sender.pubkey) verify_key.verify(proto, signature) f = Followers.Follower() f.ParseFromString(proto) if f.guid != sender.id: raise Exception('GUID does not match sending node') if f.following != self.node.id: raise Exception('Following wrong node') f.signature = signature self.db.follow.set_follower(f.SerializeToString()) proto = Profile(self.db).get(False) m = Metadata() m.name = proto.name m.handle = proto.handle m.avatar_hash = proto.avatar_hash m.short_description = proto.short_description m.nsfw = proto.nsfw for listener in self.listeners: try: verifyObject(NotificationListener, listener) listener.notify(sender.id, f.metadata.handle, "follow", "", "", f.metadata.avatar_hash) except DoesNotImplement: pass # Send SMTP notification notification = SMTPNotification(self.db) notification.send( "[OpenBazaar] %s is now following you!" % f.metadata.name, "You have a new follower:<br><br>Name: %s<br>GUID: <a href=\"ob://%s\">%s</a><br>" "Handle: %s" % (f.metadata.name, f.guid.encode('hex'), f.guid.encode('hex'), f.metadata.handle)) return [ "True", m.SerializeToString(), self.signing_key.sign(m.SerializeToString())[:64] ] except Exception: self.log.warning("failed to validate follower") return ["False"]
def rpc_get_contract_metadata(self, sender, contract_hash): self.log.info("Fetching metadata for contract %s" % hexlify(contract_hash)) self.router.addContact(sender) try: proto = ListingsStore().get_proto() l = Listings() l.ParseFromString(proto) for listing in l.listing: if listing.contract_hash == contract_hash: country_code = Profile().get().country_code listing.country_code = country_code ser = listing.SerializeToString() return [ser, self.signing_key.sign(ser)[:64]] except Exception: self.log.warning("Could not find metadata for contract %s" % hexlify(contract_hash)) return ["None"]
def update_profile(self, request): p = Profile() if not p.get().encryption_key \ and "name" not in request.args \ and "location" not in request.args: return "False" u = objects.Profile() if "name" in request.args: u.name = request.args["name"][0] if "location" in request.args: # This needs to be formatted. Either here or from the UI. u.location = CountryCode.Value(request.args["location"][0].upper()) if "handle" in request.args: u.handle = request.args["handle"][0] if "about" in request.args: u.about = request.args["about"][0] if "nsfw" in request.args: u.nsfw = True if "vendor" in request.args: u.vendor = True if "moderator" in request.args: u.moderator = True if "website" in request.args: u.website = request.args["website"][0] if "email" in request.args: u.email = request.args["email"][0] if "avatar" in request.args: with open(DATA_FOLDER + "store/avatar", 'wb') as outfile: outfile.write(request.args["avatar"][0]) avatar_hash = digest(request.args["avatar"][0]) HashMap().insert(avatar_hash, DATA_FOLDER + "store/avatar") u.avatar_hash = avatar_hash if "header" in request.args: with open(DATA_FOLDER + "store/header", 'wb') as outfile: outfile.write(request.args["header"][0]) header_hash = digest(request.args["header"][0]) HashMap().insert(header_hash, DATA_FOLDER + "store/header") u.header_hash = header_hash if "pgp_key" in request.args and "signature" in request.args: p.add_pgp_key(request.args["pgp_key"][0], request.args["signature"][0], KeyChain().guid.encode("hex")) u.encryption_key = KeyChain().encryption_pubkey p.update(u)