From 62e776be7e16dc24c86290dd67b204de7e5dfbfa Mon Sep 17 00:00:00 2001 From: poka Date: Wed, 2 Dec 2020 07:23:12 +0100 Subject: [PATCH] Add cesium+ profile management; merge class to cesium.py --- cesium-profile.py | 54 +++++++++ dialog.py | 4 +- lib/{cesiumMessaging.py => cesium.py} | 165 +++++++++++++++++++++++++- 3 files changed, 220 insertions(+), 3 deletions(-) create mode 100755 cesium-profile.py rename lib/{cesiumMessaging.py => cesium.py} (67%) mode change 100755 => 100644 diff --git a/cesium-profile.py b/cesium-profile.py new file mode 100755 index 0000000..f7e276c --- /dev/null +++ b/cesium-profile.py @@ -0,0 +1,54 @@ +#!/usr/bin/env python3 + +import argparse, sys, os +from os.path import join, dirname +from shutil import copyfile +from dotenv import load_dotenv +from lib.cesium import Profiles + +VERSION = "0.1.1" + +# Get variables environment +if not os.path.isfile('.env'): + copyfile(".env.template", ".env") +dotenv_path = join(dirname(__file__), '.env') +load_dotenv(dotenv_path) + +dunikey = os.getenv('DUNIKEY') +pod = os.getenv('POD') +if not dunikey or not pod: + sys.stderr.write("Please fill the path of your private key (PubSec), and a Cesium ES address in .env file\n") + sys.exit(1) + +# Parse arguments +parser = argparse.ArgumentParser() + +subparsers = parser.add_subparsers() +getProfile_cmd = subparsers.add_parser('get', help="Voir un profile Cesium+") +setProfile_cmd = subparsers.add_parser('set', help="Configurer son profile Cesium+") +eraseProfile_cmd = subparsers.add_parser('erase', help="Effacer son profile Cesium+") + +if len(sys.argv) <= 1 or not sys.argv[1] in ('set','get','erase'): + sys.stderr.write("Veuillez indiquer une commande valide:\n\n") + parser.print_help() + sys.exit(1) + +setProfile_cmd.add_argument('-n', '--name', help="Nom du profile") +setProfile_cmd.add_argument('-d', '--description', help="Description du profile") +setProfile_cmd.add_argument('-v', '--ville', help="Ville du profile") +setProfile_cmd.add_argument('-a', '--adresse', help="Adresse du profile") +setProfile_cmd.add_argument('-pos', '--position', nargs=2, help="Points géographiques (lat + lon)") +setProfile_cmd.add_argument('-s', '--site', help="Site web du profile") + +getProfile_cmd.add_argument('-p', '--profile', help="Nom du profile") + +args = parser.parse_args() + +# Build gchange class +cesium = Profiles(dunikey, pod) +if sys.argv[1] == "set": + cesium.set(args.name, args.description, args.ville, args.adresse, args.position, args.site) +elif sys.argv[1] == "get": + cesium.get(args.profile) +elif sys.argv[1] == "erase": + cesium.erase() diff --git a/dialog.py b/dialog.py index 464663e..18daf51 100755 --- a/dialog.py +++ b/dialog.py @@ -4,7 +4,9 @@ import argparse, sys, os from os.path import join, dirname from shutil import copyfile from dotenv import load_dotenv -from lib.cesiumMessaging import ReadFromCesium, SendToCesium, DeleteFromCesium, VERSION +from lib.cesium import ReadFromCesium, SendToCesium, DeleteFromCesium + +VERSION = "0.1.1" # Get variables environment HOME = os.getenv("HOME") diff --git a/lib/cesiumMessaging.py b/lib/cesium.py old mode 100755 new mode 100644 similarity index 67% rename from lib/cesiumMessaging.py rename to lib/cesium.py index 80c7360..2f0a6d9 --- a/lib/cesiumMessaging.py +++ b/lib/cesium.py @@ -2,11 +2,11 @@ import os, sys, ast, requests, json, base58, base64, time, string, random, re from lib.natools import fmt, sign, get_privkey, box_decrypt, box_encrypt +from time import sleep from hashlib import sha256 from datetime import datetime from termcolor import colored -VERSION = "0.1.1" PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" def pp_json(json_thing, sort=True, indents=4): @@ -111,7 +111,7 @@ class ReadFromCesium: sys.stderr.write(colored(str(e), 'red') + '\n') pp_json(hits) continue - print("Objet: " + self.title) + print('\033[1m' + self.title + '\033[0m') print(self.content) print(colored(infoTotal.center(rows, '#'), "yellow")) @@ -297,3 +297,164 @@ class DeleteFromCesium: finalDoc = self.configDoc(idMsg) self.sendDocument(finalDoc, idMsg) + + + + +#################### Profile class #################### + + + + + +class Profiles: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.pubkey = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.pubkey) or len(self.pubkey) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document SET to send + def configDocSet(self, name, description, city, address, pos, socials): + timeSent = int(time.time()) + + data = {} + if name: data['title'] = name + if description: data['description'] = description + if address: data['address'] = address + if city: data['city'] = city + if pos: + geoPoint = {} + geoPoint['lat'] = pos[0] + geoPoint['lon'] = pos[1] + data['geoPoint'] = geoPoint + if socials: + data['socials'] = [] + data['socials'].append({}) + data['socials'][0]['type'] = "web" + data['socials'][0]['url'] = socials + data['time'] = timeSent + data['issuer'] = self.pubkey + data['version'] = 2 + data['tags'] = [] + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + # Configure JSON document GET to send + def configDocGet(self, profile, scope='title'): + + data = { + "query": { + "bool": { + "should":[ + { + "match":{ + scope:{ + "query": profile,"boost":2 + } + } + },{ + "prefix": {scope: profile} + } + ] + } + },"highlight": { + "fields": { + "title":{}, + "tags":{} + } + },"from":0, + "size":100, + "_source":["title","avatar._content_type","description","city","address","socials.url","creationTime","membersCount","type"], + "indices_boost":{"user":100,"page":1,"group":0.01 + } + } + + document = json.dumps(data) + + return document + + + def sendDocument(self, document, type): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + if type == 'set': + reqQuery = '{0}/user/profile?pubkey={1}/_update?pubkey={1}'.format(self.pod, self.pubkey) + elif type == 'get': + reqQuery = '{0}/user,page,group/profile,record/_search'.format(self.pod) + + result = requests.post(reqQuery, headers=headers, data=document) + if result.status_code == 200: + # print(result.text) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document...\n" + result.text + '\n') + + def parseJSON(self, doc): + doc = json.loads(doc)['hits']['hits'][0] #['_source'] + pubkey = { "pubkey": doc['_id'] } + rest = doc['_source'] + final = {**pubkey, **rest} + + return json.dumps(final, indent=2) + + + def set(self, name=None, description=None, ville=None, adresse=None, position=None, site=None): + document = self.configDocSet(name, description, ville, adresse, position, site) + result = self.sendDocument(document,'set') + + print(result) + return result + + def get(self, profile=None): + if not profile: + profile = self.pubkey + if not re.match(PUBKEY_REGEX, profile) or len(profile) > 45: + scope = 'title' + else: + scope = '_id' + + document = self.configDocGet(profile, scope) + resultJSON = self.sendDocument(document, 'get') + result = self.parseJSON(resultJSON) + + print(result) + return result + + def erase(self): + document = self.configDocSet(None, None, None, None, None, None) + result = self.sendDocument(document,'set') + + print(result) + return result \ No newline at end of file