Add usumbufu hoba capable opener object

This commit is contained in:
nolash 2021-11-06 14:04:29 +01:00
parent e3c002082b
commit 8568e352be
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
5 changed files with 109 additions and 19 deletions

View File

@ -13,6 +13,7 @@ from chainlib.chain import ChainSpec
import clicada.cli.user as cmd_user import clicada.cli.user as cmd_user
import clicada.cli.tag as cmd_tag import clicada.cli.tag as cmd_tag
from clicada.cli.auth import PGPAuthCrypt from clicada.cli.auth import PGPAuthCrypt
from clicada.cli.http import HTTPSession
script_dir = os.path.dirname(os.path.realpath(__file__)) script_dir = os.path.dirname(os.path.realpath(__file__))
data_dir = os.path.join(script_dir, '..', 'data') data_dir = os.path.join(script_dir, '..', 'data')
@ -68,9 +69,12 @@ class CmdCtrl:
else: else:
self.config = chainlib.eth.cli.Config.from_args(self.cmd_args, base_config_dir=base_config_dir, extra_args=extra_args) self.config = chainlib.eth.cli.Config.from_args(self.cmd_args, base_config_dir=base_config_dir, extra_args=extra_args)
self.__auth = self.auth(self.get('AUTH_TYPE')) self.auth(self.get('AUTH_TYPE'))
self.config.add(False, '_SEQ') self.config.add(False, '_SEQ')
self.config.censor('AUTH_PASSPHRASE')
logger.debug('loaded config:\n{}'.format(self.config)) logger.debug('loaded config:\n{}'.format(self.config))
self.chain_spec = ChainSpec.from_chain_str(self.config.get('CHAIN_SPEC')) self.chain_spec = ChainSpec.from_chain_str(self.config.get('CHAIN_SPEC'))
@ -78,6 +82,9 @@ class CmdCtrl:
self.rpc = chainlib.eth.cli.Rpc() self.rpc = chainlib.eth.cli.Rpc()
self.__conn = self.rpc.connect_by_config(self.config) self.__conn = self.rpc.connect_by_config(self.config)
self.remote_openers = {
'meta': HTTPSession(self.get('META_URL'), auth=self.__auth),
}
def auth(self, typ): def auth(self, typ):
@ -110,3 +117,7 @@ class CmdCtrl:
def execute(self): def execute(self):
self.cmd_mod.execute(self) self.cmd_mod.execute(self)
def opener(self, k):
return self.remote_openers[k]

View File

@ -5,6 +5,7 @@ import logging
# external imports # external imports
import gnupg import gnupg
from usumbufu.client.hoba import HobaClientSession
# local imports # local imports
from clicada.error import AuthError from clicada.error import AuthError
@ -12,7 +13,10 @@ from clicada.error import AuthError
logg = logging.getLogger(__name__) logg = logging.getLogger(__name__)
class PGPAuthCrypt: class PGPAuthCrypt(HobaClientSession):
typ = 'gnupg'
alg = '969'
def __init__(self, db_dir, auth_key, pgp_dir=None): def __init__(self, db_dir, auth_key, pgp_dir=None):
self.db_dir = db_dir self.db_dir = db_dir
@ -51,3 +55,21 @@ class PGPAuthCrypt:
if not self.secret.ok: if not self.secret.ok:
raise AuthError('could not decrypt encryption secret. wrong password?') raise AuthError('could not decrypt encryption secret. wrong password?')
f.close() f.close()
def sign_auth_challenge(self, plaintext, hoba, encoding):
r = self.gpg.sign(plaintext, passphrase=self.passphrase, detach=True)
if encoding == 'base64':
r = r.data
hoba.signature = r
return str(hoba)
def __str__(self):
return 'clicada hoba/pgp auth'
def __repr__(self):
return 'clicada hoba/pgp auth'

52
clicada/cli/http.py Normal file
View File

@ -0,0 +1,52 @@
# standard imports
import hashlib
import urllib.parse
import os
import logging
# external imports
from usumbufu.client.base import (
ClientSession,
BaseTokenStore,
)
from usumbufu.client.bearer import BearerClientSession
logg = logging.getLogger(__name__)
class HTTPSession:
token_dir = '/run/user/{}/clicada/usumbufu/.token'.format(os.getuid())
def __init__(self, url, auth=None):
logg.debug('auth auth {}'.format(auth))
self.base_url = url
url_parts = urllib.parse.urlparse(self.base_url)
self.origin = urllib.parse.urljoin(url_parts[0], url_parts[1])
h = hashlib.sha256()
h.update(self.base_url.encode('utf-8'))
z = h.digest()
token_store_dir = os.path.join(self.token_dir, z.hex())
self.token_store = BaseTokenStore(path=token_store_dir)
self.session = ClientSession(self.origin, token_store=self.token_store)
bearer_handler = BearerClientSession(self.origin, token_store=self.token_store)
self.session.add_subhandler(bearer_handler)
if auth != None:
self.session.add_subhandler(auth)
self.opener = urllib.request.build_opener(self.session)
def open(self, endpoint):
url = urllib.parse.urljoin(self.base_url, endpoint)
logg.debug('open {} with opener {}'.format(url, self))
r = self.opener.open(url)
return r.read().decode('utf-8')
def __str__(self):
return str(self.session)

View File

@ -26,7 +26,7 @@ logg = logging.getLogger(__name__)
def process_args(argparser): def process_args(argparser):
argparser.add_argument('-m', '--method', type=str, help='lookup method') argparser.add_argument('-m', '--method', type=str, help='lookup method')
argparser.add_argument('--meta-url', dest='meta_url', type=str, help='Url to retrieve metadata from') argparser.add_argument('--meta-url', dest='meta_url', type=str, help='Url to retrieve metadata from')
argparser.add_argument('-f', '--force-update', dest='force_update', type=str, help='Update records of mutable entries') argparser.add_argument('-f', '--force-update', dest='force_update', action='store_true', help='Update records of mutable entries')
argparser.add_argument('identifier', type=str, help='user identifier') argparser.add_argument('identifier', type=str, help='user identifier')
@ -55,7 +55,7 @@ def execute(ctrl):
store_path = '.clicada' store_path = '.clicada'
user_phone_file_label = 'phone' user_phone_file_label = 'phone'
user_phone_store = FileUserStore(ctrl.chain(), user_phone_file_label, store_path, int(ctrl.get('FILESTORE_TTL'))) user_phone_store = FileUserStore(ctrl.opener('meta'), ctrl.chain(), user_phone_file_label, store_path, int(ctrl.get('FILESTORE_TTL')))
user_address = user_phone_store.by_phone(ctrl.get('_IDENTIFIER'), update=ctrl.get('_FORCE')) user_address = user_phone_store.by_phone(ctrl.get('_IDENTIFIER'), update=ctrl.get('_FORCE'))
if user_address == None: if user_address == None:
@ -74,9 +74,9 @@ def execute(ctrl):
token_store = FileTokenStore(ctrl.chain(), ctrl.conn(), 'token', store_path) token_store = FileTokenStore(ctrl.chain(), ctrl.conn(), 'token', store_path)
user_address_file_label = 'address' user_address_file_label = 'address'
user_address_store = FileUserStore(ctrl.chain(), user_address_file_label, store_path, int(ctrl.get('FILESTORE_TTL'))) user_address_store = FileUserStore(ctrl.opener('meta'), ctrl.chain(), user_address_file_label, store_path, int(ctrl.get('FILESTORE_TTL')))
r = user_address_store.by_address(user_address) r = user_address_store.by_address(user_address, update=ctrl.get('_FORCE'))
print("""Phone: {} print("""Phone: {}
Network address: {} Network address: {}

View File

@ -33,12 +33,13 @@ class Account(Person):
def apply_custom(self, custom_data): def apply_custom(self, custom_data):
self.tags = custom_data['tags'] self.tags = custom_data['tags']
logg.debug('tags are now {}'.format(self.tags))
class FileUserStore: class FileUserStore:
def __init__(self, chain_spec, label, store_base_path, ttl): def __init__(self, metadata_opener, chain_spec, label, store_base_path, ttl):
invalidate_before = datetime.datetime.now() - datetime.timedelta(seconds=ttl) invalidate_before = datetime.datetime.now() - datetime.timedelta(seconds=ttl)
self.invalidate_before = int(invalidate_before.timestamp()) self.invalidate_before = int(invalidate_before.timestamp())
self.have_xattr = False self.have_xattr = False
@ -53,6 +54,7 @@ class FileUserStore:
self.memstore_entity = MemDictStore() self.memstore_entity = MemDictStore()
os.makedirs(self.store_path, exist_ok=True) os.makedirs(self.store_path, exist_ok=True)
self.__validate_dir() self.__validate_dir()
self.metadata_opener = metadata_opener
def __validate_dir(self): def __validate_dir(self):
@ -177,7 +179,7 @@ class FileUserStore:
logg.debug('no address found for phone {}: {}'.format(phone, e)) logg.debug('no address found for phone {}: {}'.format(phone, e))
return None return None
self.put(phone_file, user_address) self.put(phone_file, user_address, force=update)
return user_address return user_address
@ -189,12 +191,12 @@ class FileUserStore:
try: try:
v = self.get(address, ignore_expired=ignore_expired) v = self.get(address, ignore_expired=ignore_expired)
v = json.loads(v) v = json.loads(v)
person = Person() person = Account()
try: try:
person_data = person.deserialize(person_data=v) person_data = person.deserialize(person_data=v)
except Exception as e: except Exception as e:
person_data = v person_data = v
return str(person_data) return person_data
except FileNotFoundError: except FileNotFoundError:
pass pass
except ExpiredRecordError as e: except ExpiredRecordError as e:
@ -202,25 +204,28 @@ class FileUserStore:
pass pass
address = strip_0x(address) address = strip_0x(address)
getter = MetadataRequestsHandler(MetadataPointer.PERSON, bytes.fromhex(address)) getter = self.metadata_opener
ptr = generate_metadata_pointer(bytes.fromhex(address), MetadataPointer.PERSON)
r = None r = None
try: try:
r = getter.query() r = getter.open(ptr)
except requests.exceptions.HTTPError as e: except Exception as e:
logg.debug('no metadata found for {}: {}'.format(address, e)) logg.debug('no metadata found for {}: {}'.format(address, e))
return address return address
data = r.json() data = json.loads(r)
person = Account() person = Account()
person_data = person.deserialize(person_data=data) person_data = person.deserialize(person_data=data)
self.put(address, json.dumps(person_data.serialize())) self.put(address, json.dumps(person_data.serialize()), force=update)
getter = MetadataRequestsHandler(MetadataPointer.CUSTOM, bytes.fromhex(address)) ptr = generate_metadata_pointer(bytes.fromhex(address), MetadataPointer.CUSTOM)
r = None r = None
try: try:
r = getter.query() r = getter.open(ptr)
person_data.apply_custom(r.json()) o = json.loads(r)
except requests.exceptions.HTTPError as e: person_data.apply_custom(o)
except Exception as e:
pass pass
return person_data return person_data