You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Comrad/comrad/app/screens/login/login.py

423 lines
16 KiB
Python

import os,sys; sys.path.append(os.path.abspath(os.path.join(os.path.abspath(os.path.join(os.path.dirname(__file__),'..')),'..')))
from comrad import *
from screens.base import *
from kivymd.uix.boxlayout import *
from kivymd.uix.textfield import *
from kivymd.uix.button import *
from kivymd.uix.label import *
from kivymd.uix.card import *
from kivy.uix.label import *
from kivymd.uix.dialog import *
from main import *
4 years ago
from misc import *
from kivy.app import *
import logging
logger = logging.getLogger(__name__)
4 years ago
class LoginBoxLayout(MDBoxLayout): pass
class LoginButtonLayout(MDBoxLayout): pass
class UsernameField(MDTextField): pass
class PasswordField(MDTextField): pass
class LoginButton(MDRectangleFlatButton): pass
class RegisterButton(MDRectangleFlatButton,Logger):
def enter(self):
4 years ago
un=self.parent.parent.parent.username_field.text
# pw=self.parent.parent.parent.password_field.text
login_screen = self.parent.parent.parent
time.sleep(0.1)
asyncio.create_task(login_screen.boot(un))
# logger.info('types',type(self.parent),type(self.parent.parent.parent))
# app=App.get_running_app()
# app.boot(un)
# app.change_screen_from_uri(f'/inbox/{un}')
4 years ago
pass
4 years ago
class LoginStatus(MDLabel): pass
class UsernameLayout(MDBoxLayout): pass
class UsernameLabel(MDLabel): pass
4 years ago
class WelcomeLabel(MDLabel): pass
class PasswordPopup(MDDialog): pass
class LoginScreen(BaseScreen):
#def on_pre_enter(self):
# global app
# if app.is_logged_in():
# app.root.change_screen('feed')
4 years ago
def on_pre_enter(self):
#log(self.ids)
#log('hello?')
self.dialog=None
self.pass_added=False
4 years ago
self.layout = LoginBoxLayout()
4 years ago
self.label_title = WelcomeLabel()
4 years ago
self.label_title.font_name='assets/font.otf'
4 years ago
# self.label_title.font_size='20sp'
4 years ago
self.label_title.bold=True
4 years ago
self.label_title.markup=True
self.label_title.color=rgb(*COLOR_TEXT)
4 years ago
self.label_title.text='Welcome,'
4 years ago
self.layout.add_widget(get_separator('20sp'))
4 years ago
self.layout.add_widget(self.label_title)
4 years ago
self.layout.add_widget(get_separator('30sp'))
# self.layout.add_widget(MySeparator())
4 years ago
self.layout_username = UsernameLayout()
self.label_username = UsernameLabel(text="Comrad @")
4 years ago
self.username_field = UsernameField()
4 years ago
self.username_field.line_color_focus=rgb(*COLOR_TEXT)
self.username_field.line_color_normal=rgb(*COLOR_TEXT,a=0.25)
self.username_field.font_name='assets/font.otf'
self.layout_username.add_widget(self.label_username)
self.layout_username.add_widget(self.username_field)
self.layout.add_widget(self.layout_username)
#log(self.username_field)
4 years ago
# self.username_field.text='hello????'
# self.layout_password = UsernameLayout()
# self.label_password = UsernameLabel(text='password:')
# self.label_password.font_name='assets/font.otf'
self.label_username.font_name='assets/font.otf'
# self.password_field = PasswordField()
# self.password_field.line_color_focus=rgb(*COLOR_TEXT)
# self.password_field.line_color_normal=rgb(*COLOR_TEXT,a=0.25)
# self.password_field.font_name='assets/font.otf'
# self.layout_password.add_widget(self.label_password)
# self.layout_password.add_widget(self.password_field)
# self.layout.add_widget(self.layout_password)
4 years ago
self.layout_buttons = LoginButtonLayout()
4 years ago
self.layout.add_widget(get_separator('20sp'))
4 years ago
self.layout.add_widget(self.layout_buttons)
self.login_button = LoginButton()
self.login_button.font_name='assets/font.otf'
4 years ago
# self.layout_buttons.add_widget(self.login_button)
4 years ago
self.register_button = RegisterButton()
self.register_button.font_name='assets/font.otf'
4 years ago
# self.register_button =
self.layout_buttons.add_widget(self.register_button)
self.login_status = LoginStatus()
self.login_status.font_name='assets/font.otf'
4 years ago
self.layout.add_widget(self.login_status)
self.label_title.font_size='24sp'
# self.label_password.font_size='18sp'
self.label_username.font_size='22sp'
self.login_button.font_size='12sp'
4 years ago
self.register_button.font_size='9sp'
4 years ago
self.register_button.text='enter'
self.username_field.font_size='24sp'
4 years ago
self.label_username.padding_x=(10,20)
self.username_field.padding_x=(20,10)
# self.username_field.padding_y=(25,0)
self.username_field.pos_hint={'center_y':0.5}
self.label_username.halign='right'
4 years ago
4 years ago
## add all
self.add_widget(self.layout)
#pass
# def on_enter(self):
# un=self.app.get_username()
# if un: self.username_field.text = un
def show_pass_opt(self):
if not self.pass_added:
self.layout.add_widget(self.layout_password,index=2)
self.pass_added=True
def show_pass_opt1(self,button_text='login'):
if not self.dialog:
self.dialog = PasswordPopup(
title="password:",
type="custom",
content_cls=MDTextField(password=True),
buttons=[
MDFlatButton(
text="login"
),
],
)
self.dialog.open()
def getpass_func(self,why_msg,passphrase=None):
return self.password_field.text if not passphrase else passphrase
async def boot(self,un,pw=None):
# await self.stat('hello',img_src='/home/ryan/comrad/data/contacts/marxxx.png',comrad_name='Keymaker')
4 years ago
# await self.app.get_input('hello?',get_pass=True,title='gimme your passwrdd')
# await self.app.get_input('hello?',get_pass=False,title='gimme your fav color bitch')
# return
4 years ago
# self.remove_widget(self.layout)
# from screens.map import MapWidget,default_places
# map = MapWidget()
# map.open()
# map.add_point(*default_places['Cambridge'],desc='Cambridge')
# map.draw()
# await asyncio.sleep(1)
# map.add_point(*default_places['San Francisco'],desc='San Francisco')
# map.draw()
# await asyncio.sleep(1)
# map.add_point(*default_places['Reykjavik'],desc='Reykjavik')
# map.draw()
# await asyncio.sleep(1)
# return
4 years ago
4 years ago
# remove login layout for now
self.remove_widget(self.layout)
4 years ago
# return
name=un
from comrad.backend import Comrad
4 years ago
kommie = Comrad(un)
self.log('KOMMIE!?!?',kommie)
self.log('wtf',PATH_CRYPT_CA_KEYS)
logger.info(f'booted kommie: {kommie}')
if kommie.exists_locally_as_account():
pw='marx' # @HACK FOR NOW
#pw=await self.app.get_input('Welcome back.',get_pass=True)
4 years ago
kommie.keychain(passphrase=pw)
logger.info(f'updated keychain: {dict_format(kommie.keychain())}')
logger.info(f'is account')
# self.login_status.text='You should be able to log into this account.'
if kommie.privkey:
logger.info(f'passkey login succeeded')
self.login_status.text=f'Welcome back, Comrad @{un}'
self.app.is_logged_in=True
self.app.username=kommie.name
self.app.comrad=kommie
self.root.change_screen('feed')
else:
logger.info(f'passkey login failed')
self.login_status.text='Login failed...'
4 years ago
# self.layout.add_widget(self.layout_password)
elif kommie.exists_locally_as_contact():
await self.app.stat('This is a contact of yours')
self.login_status.text='Comrad exists as a contact of yours.'
4 years ago
# self.app.change_screen('feed')
4 years ago
self.app.change_screen('login')
else:
4 years ago
# await self.app.stat('Account does not exist on hardware, maybe not on server. Try to register?')
# self.login_status.text='Comrad not known on this device. Registering...'
### REGISTER
self.remove_widget(self.layout)
res = await self.register(un)
if kommie.privkey:
self.login_status.text='Registered'
self.app.is_logged_in=True
self.app.username=kommie.name
self.app.comrad=kommie
self.remove_widget(self.layout)
self.app.change_screen('feed')
else:
self.login_status.text = 'Sign up failed...'
4 years ago
self.app.change_screen('login')
return 1
async def register(self,name):
async def logfunc(*x,**y):
if not 'comrad_name' in y: y['comrad_name']='Keymaker'
4 years ago
await self.app.stat(*x,**y)
kommie = Comrad(name)
# already have it?
if kommie.exists_locally_as_account():
return {'success':False, 'status':'You have already created this account.'}
if kommie.exists_locally_as_contact():
return {'success':False, 'status':'This is already a contact of yours'}
4 years ago
#
await logfunc(f'Hello, this is Comrad @{name}. I would like to join the socialist network.',pause=True,comrad_name=name)
4 years ago
await logfunc(f'Welcome, Comrad @{name}. To help us communicate safely, I have cut for you a matching pair of encryption keys.',pause=True,clear=True,comrad_name='Keymaker')
4 years ago
# ## 2) Make pub public/private keys
from comrad.backend.keymaker import ComradAsymmetricKey
from comrad.cli.artcode import ART_KEY_PAIR
keypair = ComradAsymmetricKey()
logger.info('cut keypair!')
pubkey,privkey = keypair.pubkey_obj,keypair.privkey_obj
4 years ago
uri_id = pubkey.data_b64
uri_s = pubkey.data_b64_s
fnfn = kommie.save_uri_as_qrcode(uri_id=uri_id,name=name)
# await logfunc(f'Here. I have cut for you a private and public asymmetric key pair, using the iron-clad Elliptic curve algorithm:',comrad_name='Keymaker')
4 years ago
await logfunc(f'The first is your "public key", which you can share with anyone. With it, someone can write you an encrypted message.',comrad_name='Keymaker')
4 years ago
4 years ago
# delete qr!
os.remove(fnfn)
4 years ago
# await logfunc(f'(1) {pubkey} -- and -- (2) {privkey}',clear=True,pause=True,comrad_name='Keymaker')
4 years ago
# await logfunc(f'(1) You may store your public key both on your device hardware, as well as share it with anyone you wish: {pubkey.data_b64_s}') #\n\nIt will also be stored as a QR code on your device:\n{qr_str}',pause=True,clear=True)
kommie._keychain['pubkey']=pubkey
kommie._keychain['privkey']=privkey
from comrad.utils import dict_format
self.log('My keychain now looks like:' + dict_format(kommie.keychain()))
# return
4 years ago
### PRIVATE KEY
4 years ago
4 years ago
# await logfunc(f"In fact this private encryption is so sensitive we'll encrypt it itself before storing it on your device -- locking the key itself away with a password.",pause=True,use_prefix=False)
# @HACK FOR NOW
passphrase = 'marx'
4 years ago
while not passphrase:
passphrase = await self.app.get_input('Please enter a memorable password.',
get_pass=True
)
passhash = hasher(passphrase)
privkey_decr = ComradSymmetricKeyWithPassphrase(passhash=passhash)
print()
4 years ago
# await logfunc(f'''We immediately whatever you typed through a 1-way hashing algorithm (SHA-256), scrambling it into (redacted):\n{make_key_discreet_str(passhash)}''',pause=True,clear=False)
privkey_encr = privkey_decr.encrypt(privkey.data)
privkey_encr_obj = ComradEncryptedAsymmetricPrivateKey(privkey_encr)
kommie._keychain['privkey_encr']=privkey_encr_obj
self.log('My keychain now looks like v2:',dict_format(kommie.keychain()))
4 years ago
# await logfunc(f'With this scrambled password we can encrypt your super-sensitive private key, from this:\n{privkey.discreet}to this:\n{privkey_encr_obj.discreet}',pause=True,clear=False)
# ### PUBLIC KEY
await logfunc('You must now register your username and public key with Comrad @Operator on the remote server.',pause=False,clear=False)
await logfunc('Connecting you to the @Operator...',comrad_name='Telephone')
## CALL OP WITH PUBKEY
4 years ago
# self.app.open_dialog('Calling @Operator...')
logger.info('got here!')
4 years ago
resp_msg_d = await self.app.ring_ring(
{
'name':name,
'pubkey': pubkey.data,
},
4 years ago
route='register_new_user',
kommie=kommie
)
4 years ago
4 years ago
# self.app.close_dialog()
4 years ago
# print()
await logfunc(resp_msg_d.get('status'),comrad_name='Operator',pause=True)
if not resp_msg_d.get('success'):
self.app.comrad=None
4 years ago
self.app.is_logged_in=False
self.app.username=''
# await logfunc('''That's too bad. Cancelling registration for now.''',pause=True,clear=True)
# self.app.change_screen('feed')
self.app.change_screen('login')
return
# clear_screen()
await logfunc('Great. Comrad @Operator now has your name and public key on file (and nothing else!).',pause=True,clear=True)
4 years ago
kommie.name=resp_msg_d.get('name')
pubkey_b = resp_msg_d.get('pubkey')
assert pubkey_b == pubkey.data
uri_id = pubkey.data_b64
sec_login = resp_msg_d.get('secret_login')
# stop
4 years ago
# await logfunc(f'''Saving keys to device:\n(1) {pubkey}\n(2) {privkey_encr_obj}\n(3) [Shared Login Secret with @Operator]\n({make_key_discreet(sec_login)}''',pause=True)
# await logfunc(f'''Saving keys to device''',pause=True)
# print()
kommie.crypt_keys.set(name, pubkey_b, prefix='/pubkey/')
kommie.crypt_keys.set(uri_id, name, prefix='/name/')
kommie.crypt_keys.set(uri_id,sec_login,prefix='/secret_login/')
# store privkey pieces
kommie.crypt_keys.set(uri_id, privkey_encr_obj.data, prefix='/privkey_encr/')
# just to show we used a passphrase -->
kommie.crypt_keys.set(uri_id, ComradSymmetricKeyWithPassphrase.__name__, prefix='/privkey_decr/')
# save qr too:
_fnfn=kommie.save_uri_as_qrcode(uri_id)
4 years ago
# await logfunc(f'Saving public key, encrypted private key, and login secret to hardware-only database. Also saving public key as QR code to: {_fnfn}.',pause=True,clear=False,use_prefix=False)
await logfunc(f'You can share it by pasting it to someone in a secure message:\n{uri_s}',comrad_name='Keymaker')
4 years ago
await logfunc(f'You can also share it IRL, phone to phone, as a QR code. It is saved to {fnfn} and looks like this.',img_src=fnfn,comrad_name='Keymaker')
4 years ago
await logfunc(f"(2) Your PRIVATE encryption key, on the other hand, will be stored encrypted on your device hardware. Do not it this with anyone or across any network whatsoever.")
# done!
await logfunc(f'Congratulations. Welcome, {kommie}.',pause=True,clear=True)
4 years ago
# remove all dialogs!!!!!!!!
# last minute: get posts
if 'res_posts' in resp_msg_d and resp_msg_d['res_posts'].get('success'):
id2post=resp_msg_d.get('res_posts').get('posts',{})
if id2post:
kommie.log('found starter posts:',list(id2post.keys()))
kommie.save_posts(id2post)
resp_msg_d['status']+=f' You\'ve got {len(id2post)} new posts and 0 new messages.'
4 years ago
# await logfunc('Returning...')
4 years ago
from comrad.app.screens.map import MapWidget
4 years ago
if self.app.map:
self.app.map.dismiss()
self.app.map=None
self.app.change_screen('feed')
return resp_msg_d