2
0
mirror of https://github.com/ComradCollective/Comrad synced 2024-11-17 21:25:37 +00:00
Comrad/komrade/backend/the_operator.py

814 lines
26 KiB
Python
Raw Normal View History

2020-09-06 06:50:23 +00:00
"""
There is only one operator!
Running on node prime.
"""
# internal imports
import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
from komrade import *
2020-09-06 20:17:47 +00:00
from komrade.backend import *
2020-09-15 11:24:15 +00:00
from komrade.backend.messages import Message
2020-09-06 06:50:23 +00:00
# print(PATH_OPERATOR_WEB_KEYS_URL)
2020-09-14 06:02:17 +00:00
# def TheOperator(*x,**y):
# from komrade.backend.operators import Komrade
# return Komrade(OPERATOR_NAME,*x,**y)
2020-09-14 19:42:37 +00:00
OP_PRIVKEY = None
2020-09-06 06:50:23 +00:00
class TheOperator(Operator):
"""
2020-09-06 20:05:03 +00:00
The remote operator
2020-09-06 06:50:23 +00:00
"""
2020-09-06 20:17:47 +00:00
@property
def phone(self):
2020-09-06 20:25:18 +00:00
global TELEPHONE
2020-09-06 20:30:36 +00:00
from komrade.backend.the_telephone import TheTelephone
2020-09-07 17:27:46 +00:00
if not TELEPHONE: TELEPHONE=TheTelephone()
2020-09-06 20:17:47 +00:00
return TELEPHONE
2020-09-06 06:50:23 +00:00
2020-09-12 19:17:07 +00:00
def __init__(self, name = OPERATOR_NAME, passphrase=None):
2020-09-06 06:50:23 +00:00
"""
Boot up the operator. Requires knowing or setting a password of memory.
"""
2020-09-14 19:42:37 +00:00
global OP_PRIVKEY
2020-09-07 17:27:46 +00:00
super().__init__(
name,
path_crypt_keys=PATH_CRYPT_OP_KEYS,
2020-09-09 14:38:37 +00:00
path_crypt_data=PATH_CRYPT_OP_DATA
)
2020-09-12 17:00:06 +00:00
from komrade.backend.phonelines import check_phonelines
keychain = check_phonelines()[OPERATOR_NAME]
2020-09-13 19:25:43 +00:00
self._keychain = {**self.load_keychain_from_bytes(keychain)}
2020-09-14 17:43:56 +00:00
2020-09-14 18:11:13 +00:00
if not keychain.get('pubkey'):
2020-09-14 18:28:26 +00:00
raise KomradeException('Operator cannot find its own public key? Shutting down.')
2020-09-14 18:11:13 +00:00
# check I match what's on op page
pub_web = komrade_request(PATH_OPERATOR_WEB_KEYS_URL)
2020-09-14 18:28:26 +00:00
if pub_web.status_code!=200:
raise KomradeException("Can't verify Komrade Operator. Shutting down.")
2020-09-14 20:32:17 +00:00
# print('Public key on komrade.app/pub: ',pub_web.text)
# print('Public key hardcoded in client: ',keychain.get('pubkey').data_b64_s)
2020-09-14 18:28:26 +00:00
if pub_web.text == keychain.get('pubkey').data_b64_s:
# print('Pubs match')
pass
2020-09-14 18:11:13 +00:00
else:
raise KomradeException('Public key for Operator on app and one at {PATH_OPERATOR_WEB_KEYS_URL} do not match. Shutting down.')
2020-09-14 17:43:56 +00:00
privkey=None
if os.path.exists(PATH_SUPER_SECRET_OP_KEY):
2020-09-14 19:42:37 +00:00
if OP_PRIVKEY:
privkey=OP_PRIVKEY
else:
print('Dare I claim to be the one true Operator?')
with open(PATH_SUPER_SECRET_OP_KEY,'rb') as f:
2020-09-14 20:18:47 +00:00
#pass_encr=f.read()
privkey = f.read()
# try:
# privkey=KomradeSymmetricKeyWithPassphrase().decrypt(pass_encr)
# if privkey: OP_PRIVKEY = privkey
# except ThemisError:
# exit('invalid password. operator shutting down.')
2020-09-14 17:43:56 +00:00
if privkey:
2020-09-14 20:25:28 +00:00
self._keychain['privkey']=KomradeAsymmetricPrivateKey(b64dec(privkey))
2020-09-14 20:32:17 +00:00
# print(self._keychain['privkey'],'??')
2020-09-13 19:25:43 +00:00
self._keychain = {**self.keychain()}
2020-09-13 07:09:03 +00:00
# self.log('@Operator booted with keychain:',dict_format(self._keychain),'and passphrase',self.passphrase)
2020-09-14 19:42:37 +00:00
# clear_screen()
2020-09-14 06:25:43 +00:00
2020-09-13 11:25:30 +00:00
2020-09-12 19:15:58 +00:00
2020-09-08 12:11:13 +00:00
def ring(self,
2020-09-07 21:29:02 +00:00
from_caller=None,
to_caller=None,
json_phone2phone={},
json_caller2phone={}, # (person) -> operator or operator -> (person)
json_caller2caller={}):
2020-09-08 12:11:13 +00:00
encr_msg_to_send = super().ring(
2020-09-07 21:29:02 +00:00
from_phone=self,
to_phone=self.phone,
from_caller=from_caller,
to_caller=to_caller,
json_phone2phone=json_phone2phone,
json_caller2phone=json_caller2phone, # (person) -> operator
json_caller2caller=json_caller2caller)
return self.send(encr_msg_to_send)
2020-09-08 15:44:17 +00:00
# ends the ring_ring() chain
2020-09-09 21:38:32 +00:00
def answer_phone(self,data_b):
2020-09-08 15:44:17 +00:00
# route incoming call from the switchboard
2020-09-13 07:34:01 +00:00
from komrade.cli.artcode import ART_OLDPHONE4
2020-09-15 11:24:15 +00:00
2020-09-13 09:02:42 +00:00
2020-09-13 08:21:36 +00:00
self.log(f'''Hello, this is the Operator.{ART_OLDPHONE4}I heard you say:\n\n {b64enc_s(data_b)}''')
2020-09-14 18:57:30 +00:00
#woops
2020-09-09 14:47:26 +00:00
# unseal
2020-09-13 09:42:22 +00:00
# self.log('got:',data_b)
2020-09-13 09:08:33 +00:00
msg_d = {
2020-09-13 09:09:28 +00:00
'msg':data_b,
2020-09-13 09:10:34 +00:00
'from_name':self.phone.name,
2020-09-13 09:09:28 +00:00
'from':self.phone.pubkey.data,
'to_name':self.name,
'to':self.pubkey.data,
2020-09-13 09:08:33 +00:00
}
# msg_d = pickle.loads(data_b)
2020-09-13 09:42:22 +00:00
# self.log('msg_d',msg_d)
2020-09-14 06:02:17 +00:00
msg_obj = Message(msg_d)
2020-09-14 12:52:06 +00:00
2020-09-13 09:42:22 +00:00
self.log(f'Decoding the binary, I discovered an encrypted message from {self.phone}\n: {msg_obj}')
2020-09-09 19:31:57 +00:00
2020-09-09 23:01:08 +00:00
# decrypt?
msg_obj.decrypt()
2020-09-09 15:14:51 +00:00
# carry out message instructions
2020-09-13 10:21:59 +00:00
resp_msg_obj = self.route_msg(msg_obj,reencrypt=True) #,route=msg_obj.route)
2020-09-13 10:35:55 +00:00
self.log('Response from message routing:',resp_msg_obj)
2020-09-09 15:14:51 +00:00
# send back down encrypted
2020-09-13 10:35:55 +00:00
# self.log('route msgd',dict_format(resp_msg_obj.msg_d))
# self.log('route msg',resp_msg_obj.msg)
2020-09-13 10:30:28 +00:00
# self.log('route msg data',resp_msg_obj.data)
2020-09-13 10:35:55 +00:00
# self.log('route msg obj',resp_msg_obj)
2020-09-13 10:21:59 +00:00
2020-09-13 07:07:39 +00:00
2020-09-13 10:30:28 +00:00
msg_sealed = pickle.dumps(resp_msg_obj.msg_d)
2020-09-13 10:35:55 +00:00
# self.log('msg_sealed =',msg_sealed)
2020-09-09 15:14:51 +00:00
# return back to phone and back down to chain
return msg_sealed
2020-09-13 10:51:01 +00:00
def has_user(self,name=None,pubkey=None):
2020-09-14 06:31:23 +00:00
nm,pk = name,pubkey
if pubkey: pk=self.crypt_keys.get(
2020-09-13 11:52:17 +00:00
name,
prefix='/pubkey/'
)
2020-09-14 06:31:23 +00:00
if name: nm=self.crypt_keys.get(
2020-09-13 11:52:17 +00:00
b64enc(pubkey),
prefix='/name/'
)
2020-09-14 06:31:23 +00:00
self.log(f'checking whether I have user {name} and {pubkey},\n I discovered I had {nm} and {pk} on record')
2020-09-13 14:15:06 +00:00
# self.log('pks:',pubkey,pk)
# self.log('nms:',name,nm)
2020-09-14 06:31:23 +00:00
return (pubkey and pk) or (name and nm)
2020-09-13 10:51:01 +00:00
2020-09-14 07:58:26 +00:00
def send(self,encr_data_b):
self.log(type(encr_data_b),encr_data_b,'sending!')
return encr_data_b
### ROUTES
2020-09-14 13:19:02 +00:00
2020-09-14 07:58:26 +00:00
def does_username_exist(self,msg_obj):
data=msg_obj.data
name=data.get('name')
pubkey=self.crypt_keys.get(name,prefix='/pubkey/')
self.log(f'looking for {name}, found {pubkey} as pubkey')
return bool(pubkey)
def login(self,msg_obj):
data=msg_obj.data
name=data.get('name')
pubkey=data.get('pubkey')
secret_login=data.get('secret_login')
2020-09-13 12:49:10 +00:00
name=name.encode() if type(name)==str else name
2020-09-15 15:19:45 +00:00
uri = b64enc(pubkey)
secret_login = b64enc(secret_login)
2020-09-13 12:39:09 +00:00
name_record = self.crypt_keys.get(
2020-09-13 12:40:26 +00:00
uri,
2020-09-13 11:18:04 +00:00
prefix='/name/'
2020-09-13 12:39:09 +00:00
)
2020-09-15 15:18:31 +00:00
print(uri,name,name_record,'??')
2020-09-13 12:45:47 +00:00
pubkey_record = b64enc(self.crypt_keys.get(
2020-09-13 12:39:09 +00:00
name,
prefix='/pubkey/'
2020-09-13 12:45:47 +00:00
))
secret_record = b64enc(self.crypt_keys.get(
2020-09-13 12:40:26 +00:00
uri,
2020-09-13 12:39:09 +00:00
prefix='/secret_login/'
2020-09-13 12:45:47 +00:00
))
2020-09-13 12:39:09 +00:00
self.log(f'''Checking inputs:
2020-09-17 12:57:12 +00:00
{name} (input)
vs.
{name_record} (record)
{uri} (input)
vs.
{pubkey_record} (record)
{secret_login} (input)
vs.
{secret_record} (record)
''')
2020-09-13 12:45:47 +00:00
# stop
# check name?
2020-09-13 12:49:10 +00:00
if name != name_record:
2020-09-13 12:45:47 +00:00
self.log('names did not match!')
success = False
2020-09-13 12:40:26 +00:00
# # check pubkey?
2020-09-13 13:07:36 +00:00
elif uri != pubkey_record:
self.log('pubkeys did not match!',uri,pubkey_record)
success = False
elif secret_login != secret_record:
self.log('secrets did not match!')
success = False
else:
success = True
## return res
if success:
return {
'success': True,
2020-09-16 10:53:55 +00:00
'status':f'Welcome back, Komrade @{name.decode()}.',
2020-09-17 12:57:12 +00:00
'status_type':'login',
2020-09-17 12:58:59 +00:00
'name':name_record.decode(),
2020-09-17 12:57:12 +00:00
'pubkey':pubkey_record
2020-09-13 13:07:36 +00:00
}
else:
return {
'success': False,
2020-09-16 10:53:55 +00:00
'status':'Login failed.',
2020-09-17 12:57:12 +00:00
'status_type':'login',
2020-09-13 13:07:36 +00:00
}
2020-09-13 11:18:04 +00:00
2020-09-14 07:58:26 +00:00
def register_new_user(self,msg_obj):
2020-09-10 12:52:07 +00:00
# self.log('setting pubkey under name')
2020-09-14 07:58:26 +00:00
data=msg_obj.data
name=data.get('name')
pubkey=data.get('pubkey')
2020-09-13 10:51:01 +00:00
# is user already there?
if self.has_user(name=name,pubkey=pubkey):
return {
'success':False,
2020-09-16 16:26:43 +00:00
'status': f"{OPERATOR_INTRO}I'm sorry, but I can't register the name of {name}. This komrade already exists."
2020-09-13 10:51:01 +00:00
}
2020-09-13 06:25:31 +00:00
2020-09-13 10:51:01 +00:00
# generate shared secret
2020-09-13 12:59:13 +00:00
shared_secret = get_random_binary_id()
2020-09-13 11:53:46 +00:00
self.log(f'{self}: Generated shared secret between {name} and me:\n\n{make_key_discreet(shared_secret)}')
2020-09-13 10:51:01 +00:00
# ok then set what we need
2020-09-13 11:52:17 +00:00
uri_id = b64enc(pubkey)
2020-09-13 10:51:01 +00:00
pubkey_b = b64dec(pubkey)
2020-09-13 10:53:17 +00:00
r1=self.crypt_keys.set(name,pubkey_b,prefix='/pubkey/')
r2=self.crypt_keys.set(uri_id,name,prefix='/name/')
2020-09-13 10:51:01 +00:00
# hide secret as key
2020-09-13 12:51:46 +00:00
r3=self.crypt_keys.set(uri_id,shared_secret,prefix='/secret_login/')
2020-09-13 10:53:17 +00:00
# success?
success = r1 and r2 and r3
if not success:
return {
'success':False,
'status': f"{OPERATOR_INTRO}I'm sorry, but I can't register the name of {name}."
}
2020-09-13 10:51:01 +00:00
2020-09-17 05:01:59 +00:00
# save QR also?
self.save_uri_as_qrcode(uri_id=uri_id,name=name)
2020-09-13 10:51:01 +00:00
# compose result
2020-09-10 12:52:07 +00:00
res = {
'success':success,
2020-09-13 13:02:14 +00:00
'pubkey':pubkey_b,
2020-09-13 11:52:39 +00:00
'secret_login':shared_secret,
2020-09-10 12:52:07 +00:00
'name':name,
2020-09-16 17:31:09 +00:00
'status':f'Name @{name} was successfully registered.\n\nIt has been permanently linked to the following public key:\n\n{uri_id.decode()}'
2020-09-10 12:52:07 +00:00
}
2020-09-13 11:30:36 +00:00
# res_safe = {
# **res,
# **{
# 'secret_login':make_key_discreet(
# res['secret_login']
# )
# }
# }
2020-09-13 10:51:01 +00:00
# return
2020-09-13 11:30:36 +00:00
self.log('Operator returning result:',dict_format(res,tab=4))
2020-09-13 08:47:25 +00:00
return res
2020-09-10 12:52:07 +00:00
2020-09-10 14:16:53 +00:00
2020-09-10 12:52:07 +00:00
## success msg
2020-09-10 14:16:53 +00:00
#
2020-09-13 11:18:04 +00:00
# cvb64=cv_b64#b64encode(cv).decode()
# qrstr=self.qr_str(cvb64)
# res['status']=self.status(f'''{OPERATOR_INTRO}I have successfully registered Komrade {name}.
2020-09-10 14:16:53 +00:00
2020-09-13 11:18:04 +00:00
# If you're interested, here's what I did. I stored the public key you gave me, {cvb64}, under the name of "{name}". However, I never save that name directly, but record it only in a disguised, "hashed" form: {ck}. I scrambled "{name}" by running it through a 1-way hashing function, which will always yield the same result: provided you know which function I'm using, and what the secret "salt" is that I add to all the input, a string of text which I keep protected and encrypted on my local hard drive.
2020-09-07 18:55:34 +00:00
2020-09-13 11:18:04 +00:00
# The content of your data will therefore not only be encrypted, but its location in my database is obscured even to me. There's no way for me to reverse-engineer the name of {name} from the record I stored it under, {ck}. Unless you explictly ask me for the public key of {name}, I will have no way of accessing that information.
2020-09-10 14:16:53 +00:00
2020-09-13 11:18:04 +00:00
# Your name ({name}) and your public key ({cvb64}) are the first two pieces of information you've given me about yourself. Your public key is your 'address' in Komrade: in order for anyone to write to you, or for them to receive messages from you, they'll need to know your public key (and vise versa). The Komrade app should store your public key on your device as a QR code, under ~/.komrade/.contacts/{name}.png. It will look something like this:{qrstr}You can then send this image to anyone by a secure channel (Signal, IRL, etc), or tell them the code directly ({cvb64}).
2020-09-10 14:16:53 +00:00
2020-09-13 11:18:04 +00:00
# By default, if anyone asks me what your public key is, I won't tell them--though I won't be able to avoid hinting that a user exists under this name should someone try to register under that name and I deny them). Instead, if the person who requested your public key insists, I will send you a message (encrypted end-to-end so only you can read it) that the user who met someone would like to introduce themselves to you; I will then send you their name and public key. It's now your move: up to you whether to save them back your public key.
2020-09-10 14:16:53 +00:00
2020-09-13 11:18:04 +00:00
# If you'd like to change this default behavior, e.g. by instead allowing anyone to request your public key, except for those whom you explcitly block, I have also created a super secret administrative record for you to change various settings on your account. This is protected by a separate encryption key which I have generated for you; and this key which is itself encrypted with the password you entered earlier. Don't worry: I never saw that password you typed, since it was given to me already hashed and disguised. Without that hashed passphrase, no one will be able to unlock the administration key; and without the administration key, they won't be able to find the hashed record I stored your user settings under, since I also salted that hash with your own hashed passphrase. Even if someone found the record I stored them under, they wouldn't be able to decrypt the existing settings; and if they can't do that, I won't let them overwrite the record.''')
2020-09-10 14:16:53 +00:00
2020-09-13 11:18:04 +00:00
# self.log('Operator returning result:',dict_format(res,tab=2))
2020-09-17 04:16:48 +00:00
2020-09-17 06:19:41 +00:00
2020-09-17 04:16:48 +00:00
2020-09-17 05:34:35 +00:00
def mass_deliver_msg(self,post_msg_d,contacts):
def do_mass_deliver_msg(contact,post_msg_d=post_msg_d):
self.log(f'<- delivering to {contact} the post: {post_msg_d}')
2020-09-17 05:15:54 +00:00
msg_from_op = Message(
2020-09-17 05:34:35 +00:00
{ # op -> komrade
'to':post_msg_d.get('to'),
'to_name':post_msg_d.get('from'),
2020-09-17 05:15:54 +00:00
'from':self.uri,
'from_name':self.name,
2020-09-17 05:34:35 +00:00
'msg':post_msg_d.get
2020-09-17 05:15:54 +00:00
}
)
2020-09-17 05:25:59 +00:00
self.log(f'prepared msg for {contact}: {msg_from_op.msg}')
2020-09-17 05:15:54 +00:00
# encrypt
msg_from_op.encrypt()
self.log('encrypted to:',msg_from_op.msg)
# actually deliver
2020-09-17 05:19:23 +00:00
res=self.actually_deliver_msg(msg_from_op)
self.log('delivery res =',res)
2020-09-17 05:15:54 +00:00
for contact in contacts:
do_mass_deliver_msg(contact)
2020-09-17 05:19:23 +00:00
return {
'success':True,
'status':f'Delivered post to {len(contacts)}.',
}
2020-09-17 05:15:54 +00:00
2020-09-17 04:16:48 +00:00
2020-09-17 08:16:52 +00:00
def validate_msg(self,msg_d):
2020-09-17 08:24:13 +00:00
from komrade.backend.messages import is_valid_msg_d
2020-09-17 08:16:52 +00:00
if not is_valid_msg_d(msg_d): return False
# alleged
(
alleged_to_name,
alleged_to_pub,
alleged_from_name,
alleged_from_pub
) = (
msg_d.get('to_name'),
msg_d.get('to'),
msg_d.get('from_name'),
msg_d.get('from')
)
# recorded
(
real_to_name,
real_to_pub,
real_from_name,
real_from_pub
) = (
self.find_name(alleged_to_pub),
2020-09-17 08:29:50 +00:00
self.find_pubkey(alleged_to_name).data_b64,
2020-09-17 08:16:52 +00:00
self.find_name(alleged_from_pub),
2020-09-17 08:29:50 +00:00
self.find_pubkey(alleged_from_name).data_b64
2020-09-17 08:16:52 +00:00
)
2020-09-17 04:16:48 +00:00
2020-09-17 08:26:28 +00:00
self.log(f'''
alleged_to_name = {alleged_to_name}
alleged_to_pub = {alleged_to_pub}
alleged_from_name = {alleged_from_name}
alleged_from_pub = {alleged_from_pub}
real_to_name = {real_to_name}
real_to_pub = {real_to_pub}
real_from_name = {real_from_name}
real_from_pub = {real_from_pub}
''')
2020-09-17 08:16:52 +00:00
try:
assert alleged_to_name == real_to_name
assert alleged_to_pub == real_to_pub
assert alleged_from_name == real_from_name
assert alleged_from_pub == real_from_pub
except AssertionError:
return False
return True
2020-09-17 04:16:48 +00:00
2020-09-14 07:58:26 +00:00
def deliver_msg(self,msg_to_op):
data = msg_to_op.data
2020-09-17 08:16:52 +00:00
deliver_msg_d = data.get('deliver_msg')
2020-09-14 20:37:29 +00:00
2020-09-17 08:16:52 +00:00
# is valid?
if not self.validate_msg(deliver_msg_d):
2020-09-17 08:41:09 +00:00
res = {
2020-09-17 08:16:52 +00:00
'status':'Message was not valid. Records between Komrade and Operator do not match.',
'success':False
}
2020-09-17 08:41:09 +00:00
self.log('-->',res)
2020-09-14 20:37:29 +00:00
2020-09-17 08:16:52 +00:00
# add type
deliver_msg_d['type']='DM'
2020-09-14 21:16:58 +00:00
2020-09-17 08:16:52 +00:00
# package
2020-09-15 07:07:42 +00:00
from komrade.backend.messages import Message
2020-09-15 07:04:01 +00:00
msg_from_op = Message(
2020-09-15 07:07:42 +00:00
from_whom=self,
msg_d = {
2020-09-17 08:16:52 +00:00
'to':deliver_msg_d.get('to'),
'to_name':deliver_msg_d.get('to_name'),
2020-09-17 08:45:21 +00:00
'msg':deliver_msg_d
2020-09-15 07:04:01 +00:00
}
2020-09-15 07:05:02 +00:00
)
2020-09-15 07:04:01 +00:00
self.log(f'{self}: Prepared this msg for delivery:\n{msg_from_op}')
2020-09-14 21:21:14 +00:00
2020-09-15 07:04:01 +00:00
# encrypt
msg_from_op.encrypt()
2020-09-15 11:24:15 +00:00
2020-09-17 08:16:52 +00:00
# deliver
2020-09-15 11:24:15 +00:00
return self.actually_deliver_msg(msg_from_op)
2020-09-15 11:25:39 +00:00
def actually_deliver_msg(self,msg_from_op):
2020-09-15 08:07:57 +00:00
msg_from_op_b_encr = msg_from_op.msg #.msg_b # pickle of msg_d
2020-09-17 09:05:52 +00:00
self.log('<-',msg_from_op_b_encr)
2020-09-15 11:24:15 +00:00
deliver_to = msg_from_op.to_pubkey
deliver_to_b = b64dec(deliver_to)
2020-09-14 21:16:58 +00:00
2020-09-14 21:31:07 +00:00
# save new post
post_id = get_random_binary_id()
2020-09-17 08:16:52 +00:00
self.crypt_data.set(
2020-09-15 06:49:53 +00:00
post_id,
msg_from_op_b_encr,
prefix='/post/'
)
2020-09-15 12:39:34 +00:00
self.log(f'put {msg_from_op} (or {msg_from_op_b_encr}) in {post_id}')
2020-09-14 21:31:07 +00:00
2020-09-15 07:17:12 +00:00
# get inbox
2020-09-17 09:26:47 +00:00
inbox_crypt = self.get_inbox_crypt(pubkey_b=deliver_to_b)
2020-09-17 09:05:52 +00:00
self.log('inbox_crypt',inbox_crypt)
self.log('inbox_crypt.values',inbox_crypt.values)
res_inbox = inbox_crypt.prepend(post_id)
2020-09-17 09:08:59 +00:00
self.log('inbox_crypt.values v2',inbox_crypt.values)
2020-09-17 09:05:52 +00:00
res = {
2020-09-15 07:17:12 +00:00
'status':'Message delivered.',
'success':True,
2020-09-17 09:05:52 +00:00
'post_id':post_id,
'res_inbox':res_inbox
2020-09-15 07:17:12 +00:00
}
2020-09-17 09:05:52 +00:00
self.log('->',res)
2020-09-17 09:07:30 +00:00
return res
2020-09-14 07:58:26 +00:00
2020-09-14 22:00:13 +00:00
2020-09-17 10:37:58 +00:00
###
# LETS SIMPLIFY THIS
# Komrade -> Op: get_updates()
# gets new DMs, new posts,
# both index/inbox and content/body
###
def require_login(self,msg_to_op,do_login=True):
2020-09-14 22:00:13 +00:00
# logged in?
2020-09-16 10:56:00 +00:00
if do_login:
login_res = self.login(msg_to_op)
if not login_res.get('success'):
return login_res
2020-09-17 10:37:58 +00:00
return {'success':True,'status':'Logged in.'}
else:
return {'success':True,'status':'Login not required.'}
# (0) get updates
# user enters here
2020-09-17 12:23:11 +00:00
def get_updates(self,
msg_to_op=None,
inbox_uri=None,
do_login=True,
include_posts=True):
self.log('<-',msg_to_op,inbox_uri)
# uri?
if not inbox_uri and not msg_to_op:
return {'success':False, 'status':'Updates for whom?'}
uri = inbox_uri if inbox_uri else msg_to_op.from_pubkey
# req login?
if do_login:
if not msg_to_op:
return {'success':False, 'status':'Cannot login outside of message context.'}
res_login=self.require_login(msg_to_op,do_login=do_login)
2020-09-17 12:57:12 +00:00
if not res_login.get('success'):
return {'res_login':res_login}
2020-09-17 10:37:58 +00:00
# (1) get inbox
2020-09-17 12:23:11 +00:00
res_inbox=self.get_inbox(uri)
if not res_inbox.get('success'): return res_inbox
inbox=res_inbox.get('inbox',[])
# (2) get msgs
res_msgs = self.get_msgs(inbox)
2020-09-17 12:57:12 +00:00
if not res_msgs.get('success'):
return {
'res_login':res_login,
'res_msgs':res_msgs
}
2020-09-17 12:23:11 +00:00
msgs=res_msgs.get('posts')
# (3) get posts
posts=[]
if include_posts and self.name!=WORLD_NAME:
res_posts = self.get_posts()
2020-09-17 12:57:12 +00:00
if not res_posts.get('success'):
return {
'res_login':res_login,
'res_msgs':res_msgs,
'res_posts':res_posts
}
posts=res_posts.get('posts',[])
2020-09-17 12:23:11 +00:00
# return
res={
'success': True,
'status': f'You have {len(msgs)} new messages, and {len(posts)} new posts.',
2020-09-17 12:57:12 +00:00
# 'posts': posts,
# 'msgs': msgs,
'res_login':res_login,
# 'res_inbox':res_inbox,
'res_msgs':res_msgs,
'res_posts':res_posts,
2020-09-17 12:23:11 +00:00
}
self.log('-->',res)
return res
## posts
def get_posts(self):
world=Komrade(WORLD_NAME)
# (1) get inbox
res_inbox=self.get_inbox(world.uri)
if not res_inbox.get('success'): return res_inbox
inbox=res_inbox.get('inbox',[])
# (2) read msgs
id2post={}
for post_id in inbox:
res_read_msg = world.read_msg(post_id)
if res_read_msg.get('success'):
2020-09-17 12:57:12 +00:00
post=res_read_msg.get('msg')
2020-09-17 12:23:11 +00:00
if post:
post[post_id]=post
self.log('id2post for world',id2post)
2020-09-17 12:57:12 +00:00
stop
2020-09-17 12:23:11 +00:00
return id2post
2020-09-17 10:37:58 +00:00
2020-09-17 12:23:11 +00:00
# (3) reencrypt for requester
# inbox=res_inbox.get('inbox',[])
2020-09-17 10:37:58 +00:00
# (1) get inbox
2020-09-17 12:23:11 +00:00
def get_inbox(self,inbox_uri):
2020-09-14 22:00:13 +00:00
# ok, then find the inbox?
2020-09-17 09:26:47 +00:00
inbox=self.get_inbox_crypt(
2020-09-17 12:23:11 +00:00
uri=inbox_uri,
pubkey_b=b64dec(inbox_uri)
2020-09-15 12:44:46 +00:00
)
2020-09-17 09:26:47 +00:00
2020-09-15 12:44:46 +00:00
res = {
2020-09-14 22:07:35 +00:00
'status':'Succeeded in getting inbox.',
'success':True,
2020-09-17 10:37:58 +00:00
'inbox':inbox.values if inbox else []
2020-09-14 22:07:35 +00:00
}
2020-09-17 10:37:58 +00:00
self.log(f'--> {res}')
2020-09-15 12:44:46 +00:00
return res
2020-09-14 07:58:26 +00:00
2020-09-17 12:23:11 +00:00
def get_msgs(self,post_ids):
2020-09-17 10:37:58 +00:00
posts={}
2020-09-15 06:10:22 +00:00
for post_id in post_ids:
2020-09-17 10:37:58 +00:00
post = self.crypt_data.get(
post_id,
prefix='/post/'
)
if post: posts[post_id] = post
2020-09-15 06:10:22 +00:00
self.log(f'I {self} found {len(posts)} for {msg_to_op.from_name}')
2020-09-17 10:37:58 +00:00
2020-09-16 14:34:21 +00:00
res = {
2020-09-17 10:37:58 +00:00
'status':'Succeeded in getting new messages and posts.',
2020-09-16 14:34:21 +00:00
'success':True,
2020-09-17 12:23:11 +00:00
'msgs':posts
2020-09-16 14:34:21 +00:00
}
2020-09-17 10:37:58 +00:00
self.log(f'--> {res}')
2020-09-16 14:34:21 +00:00
return res
2020-09-17 10:37:58 +00:00
def delete_posts(self,post_ids,inbox_uri=None):
# delete from posts
deleted_post_ids=[]
2020-09-16 14:34:21 +00:00
for post_id in post_ids:
2020-09-17 10:37:58 +00:00
if self.crypt_data.delete(
2020-09-16 14:34:21 +00:00
post_id,
prefix='/post/'
2020-09-17 10:37:58 +00:00
):
deleted_post_ids.append(post_id)
self.log('deleted_post_ids',deleted_post_ids,'...')
res = {
'deleted':post_ids,
}
2020-09-16 14:34:21 +00:00
2020-09-17 10:37:58 +00:00
# delete from inbox
2020-09-16 14:34:21 +00:00
if inbox_uri:
2020-09-17 10:37:58 +00:00
inbox_db=self.get_inbox_crypt(
uri=inbox_uri,
pubkey_b=b64dec(inbox_uri)
2020-09-16 14:34:21 +00:00
)
2020-09-17 10:37:58 +00:00
res['deleted_from_inbox']=inbox_db.remove(
deleted_post_ids
2020-09-16 14:39:56 +00:00
)
2020-09-17 10:37:58 +00:00
self.log('-->',res)
return res
2020-09-17 12:23:11 +00:00
def get_posts(self):
2020-09-17 10:37:58 +00:00
# get posts by personating world
world = Komrade(WORLD_NAME)
world_inbox_res = world.inbox()
self.log('world_inbox_res',world_inbox_res)
if not world_inbox_res.get('success'):
return world_inbox_res
world_msgs = world_inbox_res.get('msgs')
self.log('world_msgs',world_msgs)
# encrypt to sender from world
world_msgs_b = BSEP.join([msg.msg_b for msg in world_msgs])
world_msg_to_sender = Message(
from_whom=world,
to_whom=msg_to_op.from_whom,
msg=world_msgs_b
)
self.log(world_msg_to_sender,'<- world_msg_to_sender')
# encrypt
world_msg_to_sender.encrypt()
self.log(world_msg_to_sender,'<- world_msg_to_sender encrypted')
return world_msg_to_sender.msg_d
2020-09-16 14:34:21 +00:00
2020-09-14 03:50:05 +00:00
2020-09-17 10:37:58 +00:00
###
# INTRODUCTIONS/MEETING
###
2020-09-16 14:34:21 +00:00
2020-09-17 12:23:11 +00:00
def introduce(self,msg_to_op):
2020-09-15 15:20:50 +00:00
# # logged in?
# self.log('introduce got:',msg_to_op)
# login_res = self.login(msg_to_op)
# self.log('introduce got login-res:',login_res)
# if not login_res.get('success'):
# return login_res
2020-09-15 11:24:15 +00:00
2020-09-15 11:32:32 +00:00
data=msg_to_op.data
2020-09-15 11:40:53 +00:00
self.log('Op sees data:',dict_format(data))
2020-09-15 11:36:52 +00:00
meet_pubkey = self.crypt_keys.get(
data.get('meet_name'),
'/pubkey/'
)
2020-09-16 19:30:46 +00:00
if not meet_pubkey:
return {
'success':False,
'status':'Are you sure this komrade exists? If you are, try contacting them by another secure channel and asking for their public key there.'
}
2020-09-15 11:40:53 +00:00
self.log('found in crypt:',meet_pubkey)
2020-09-15 13:01:55 +00:00
meet_name = data.get('meet_name')
2020-09-15 13:09:16 +00:00
meet_uri = b64enc(meet_pubkey)
2020-09-15 13:31:31 +00:00
meet_from_name = data.get('name')
meet_from_uri = data.get('pubkey')
2020-09-15 15:07:15 +00:00
returning = data.get('returning')
if returning:
2020-09-16 13:38:15 +00:00
txt=f'''Komrade @{meet_from_name} has agreed to make your acquaintance.\n\nTheir public key is:\n{meet_from_uri.decode()}.'''
2020-09-15 15:07:15 +00:00
else:
2020-09-16 13:38:15 +00:00
txt=f'''Komrade @{meet_from_name} would like to make your acquaintance.\n\nTheir public key is:\n{meet_from_uri.decode()}.'''
2020-09-15 15:07:15 +00:00
2020-09-15 13:09:16 +00:00
msg_from_op = Message(
msg_d = {
'to':meet_uri,
2020-09-15 13:01:55 +00:00
'to_name':meet_name,
2020-09-15 13:10:00 +00:00
'from':self.uri,
2020-09-15 13:10:18 +00:00
'from_name':self.name,
2020-09-15 13:09:16 +00:00
'msg':{
'to':meet_uri,
2020-09-15 13:10:00 +00:00
'to_name':meet_name,
2020-09-15 13:09:16 +00:00
'from':self.uri,
2020-09-15 13:10:18 +00:00
'from_name':self.name,
2020-09-15 13:31:31 +00:00
2020-09-15 13:09:16 +00:00
2020-09-15 13:49:20 +00:00
'msg':{
2020-09-15 15:07:15 +00:00
'txt':txt,
2020-09-15 13:49:20 +00:00
'type':'prompt',
'prompt_id':'addcontact',
'meet_name':meet_from_name,
'meet':meet_from_uri,
}
2020-09-15 13:09:16 +00:00
}
2020-09-15 13:01:55 +00:00
}
)
self.log('formed msg:',msg_from_op)
2020-09-15 13:09:16 +00:00
msg_from_op.encrypt()
self.log('encrypted formed msg:',msg_from_op)
2020-09-16 10:26:42 +00:00
res = self.actually_deliver_msg(msg_from_op)
res['msg_sent']=txt
return res
2020-09-15 11:24:15 +00:00
2020-09-17 04:16:48 +00:00
2020-09-06 06:50:23 +00:00
def test_op():
2020-09-06 19:39:44 +00:00
from komrade.backend.the_telephone import TheTelephone
2020-09-13 06:01:06 +00:00
from getpass import getpass
2020-09-13 07:00:21 +00:00
op = TheOperator()
2020-09-06 19:39:44 +00:00
# op.boot()
2020-09-13 06:00:28 +00:00
keychain_op = op.keychain()
2020-09-06 19:39:44 +00:00
2020-09-06 19:19:44 +00:00
2020-09-06 19:39:44 +00:00
phone = TheTelephone()
# phone.boot()
2020-09-13 06:00:28 +00:00
keychain_ph = phone.keychain()
2020-09-06 19:19:44 +00:00
2020-09-06 19:18:43 +00:00
from pprint import pprint
2020-09-06 19:19:44 +00:00
print('REASSEMBLED OPERATOR KEYCHAIN')
pprint(keychain_op)
# stop
2020-09-06 19:18:43 +00:00
print('REASSEMBLED TELEPHONE KEYCHAIN')
2020-09-06 19:19:44 +00:00
pprint(keychain_ph)
2020-09-06 19:18:43 +00:00
2020-09-06 17:44:08 +00:00
# print(op.pubkey(keychain=keychain))
2020-09-06 18:52:47 +00:00
# print(op.crypt_keys.get(op.pubkey(), prefix='/privkey_encr/'))
2020-09-06 18:51:25 +00:00
# print(op.crypt_keys.get(op.name, prefix='/pubkey_encr/'))
2020-09-06 19:18:43 +00:00
# print(op.pubkey_)
2020-09-06 18:52:47 +00:00
2020-09-06 17:42:57 +00:00
2020-09-06 19:18:43 +00:00
# stop
2020-09-06 06:50:23 +00:00
2020-09-06 19:18:43 +00:00
# pubkey = op.keychain()['pubkey']
# pubkey_b64 = b64encode(pubkey)
# print(pubkey)
2020-09-06 06:50:23 +00:00
if __name__ == '__main__': test_op()