separated auth storage from sys storage

This commit is contained in:
əlemi 2022-04-20 00:02:24 +02:00
parent 6a39e61e03
commit eefa1bf211
No known key found for this signature in database
GPG key ID: BBCBFE5D7244634E
2 changed files with 55 additions and 14 deletions

View file

@ -3,18 +3,28 @@ import json
import sqlite3 import sqlite3
from dataclasses import dataclass from dataclasses import dataclass
from typing import Optional, Any from typing import Optional, Any, Dict
from datetime import datetime
__DATE_FORMAT__ : str = "%Y-%m-%d %H:%M:%S.%f"
@dataclass @dataclass
class SystemState: class SystemState:
name : str name : str
token : str version : str
start_time : int start_time : int
@dataclass
class AuthenticatorState:
date : datetime
token : Dict[str, Any]
legacy : bool = False
class Storage: class Storage:
name : str name : str
db : sqlite3.Connection db : sqlite3.Connection
def __init__(self, name:str): def __init__(self, name:str):
self.name = name self.name = name
init = not os.path.isfile(f"{name}.session") init = not os.path.isfile(f"{name}.session")
@ -30,14 +40,21 @@ class Storage:
def _init_db(self): def _init_db(self):
cur = self.db.cursor() cur = self.db.cursor()
cur.execute('CREATE TABLE system (name TEXT, token TEXT, start_time LONG)') cur.execute('CREATE TABLE system (name TEXT PRIMARY KEY, version TEXT, start_time LONG)')
cur.execute('CREATE TABLE documents (name TEXT, value TEXT)') cur.execute('CREATE TABLE documents (name TEXT PRIMARY KEY, value TEXT)')
cur.execute('CREATE TABLE authenticator (date TEXT PRIMARY KEY, token TEXT, legacy BOOL')
self.db.commit() self.db.commit()
def _set_state(self, state:SystemState): def _set_state(self, state:SystemState):
cur = self.db.cursor() cur = self.db.cursor()
cur.execute('DELETE FROM system') cur.execute('DELETE FROM system')
cur.execute('INSERT INTO system VALUES (?, ?, ?)', (state.name, state.token, state.start_time)) cur.execute('INSERT INTO system VALUES (?, ?, ?)', (state.name, state.version, int(state.start_time)))
self.db.commit()
def _set_auth(self, state:AuthenticatorState):
cur = self.db.cursor()
cur.execute('DELETE FROM authenticator')
cur.execute('INSERT INTO authenticator VALUES (?, ?, ?)', (state.date.strftime(__DATE_FORMAT__), json.dumps(state.token), state.legacy))
self.db.commit() self.db.commit()
def system(self) -> Optional[SystemState]: def system(self) -> Optional[SystemState]:
@ -47,10 +64,21 @@ class Storage:
return None return None
return SystemState( return SystemState(
name=val[0][0], name=val[0][0],
token=val[0][1], version=val[0][1],
start_time=val[0][2] start_time=val[0][2]
) )
def auth(self) -> Optional[AuthenticatorState]:
cur = self.db.cursor()
val = cur.execute('SELECT * FROM authenticator').fetchall()
if not val:
return None
return AuthenticatorState(
date=datetime.strptime(val[0][0], __DATE_FORMAT__),
token=json.loads(val[0][1]),
legacy=val[0][2] or False
)
def get(self, key:str) -> Optional[Any]: def get(self, key:str) -> Optional[Any]:
cur = self.db.cursor() cur = self.db.cursor()
val = cur.execute("SELECT * FROM documents WHERE name = ?", (key,)).fetchall() val = cur.execute("SELECT * FROM documents WHERE name = ?", (key,)).fetchall()

View file

@ -4,6 +4,7 @@ import logging
import asyncio import asyncio
import datetime import datetime
import uuid import uuid
import pkg_resources
from typing import List, Dict, Optional, Any, Type, get_args, get_origin, get_type_hints, Set, Callable from typing import List, Dict, Optional, Any, Type, get_args, get_origin, get_type_hints, Set, Callable
from time import time from time import time
@ -15,9 +16,11 @@ from apscheduler.schedulers.asyncio import AsyncIOScheduler
from aiocraft.mc.packet import Packet from aiocraft.mc.packet import Packet
from aiocraft.mc.auth import AuthInterface, AuthException, MojangAuthenticator, MicrosoftAuthenticator, OfflineAuthenticator from aiocraft.mc.auth import AuthInterface, AuthException, MojangAuthenticator, MicrosoftAuthenticator, OfflineAuthenticator
from .storage import Storage, SystemState from .storage import Storage, SystemState, AuthenticatorState
from .game import GameState, GameChat, GameInventory, GameTablist, GameWorld from .game import GameState, GameChat, GameInventory, GameTablist, GameWorld
__VERSION__ = pkg_resources.get_distribution('treepuncher').version
def parse_with_hint(val:str, hint:Any) -> Any: def parse_with_hint(val:str, hint:Any) -> Any:
if hint is bool: if hint is bool:
if val.lower() in ['1', 'true', 't', 'on', 'enabled']: if val.lower() in ['1', 'true', 't', 'on', 'enabled']:
@ -181,11 +184,20 @@ class Treepuncher(
super().__init__(opt('server', required=True), online_mode=online_mode, authenticator=authenticator) super().__init__(opt('server', required=True), online_mode=online_mode, authenticator=authenticator)
prev = self.storage.system() # if this isn't 1st time, this won't be None. Load token from there prev = self.storage.system() # if this isn't 1st time, this won't be None. Load token from there
state = SystemState(self.name, __VERSION__, 0)
if prev: if prev:
state.start_time = prev.start_time
if self.name != prev.name: if self.name != prev.name:
self.logger.warning("Saved session belong to another user") self.logger.warning("Saved session belong to another user")
authenticator.deserialize(json.loads(prev.token)) if prev.version != state.version:
self.logger.info("Loaded authenticated session") self.logger.warning("Saved session uses a different version")
prev_auth = self.storage.auth()
if prev_auth:
if prev_auth.legacy ^ isinstance(authenticator, MicrosoftAuthenticator):
self.logger.warning("Saved session is incompatible with configured authenticator")
authenticator.deserialize(prev_auth.token)
self.logger.info("Loaded session from %s", prev_auth.date)
self.storage._set_state(state)
@property @property
@ -194,12 +206,12 @@ class Treepuncher(
async def authenticate(self): async def authenticate(self):
await super().authenticate() await super().authenticate()
state = SystemState( state = AuthenticatorState(
name=self.name, date=datetime.datetime.now(),
token=json.dumps(self.authenticator.serialize()), token=self.authenticator.serialize(),
start_time=int(time()) legacy=isinstance(self.authenticator, MojangAuthenticator)
) )
self.storage._set_state(state) self.storage._set_auth(state)
async def start(self): async def start(self):
# if self.started: # TODO readd check # if self.started: # TODO readd check
@ -213,6 +225,7 @@ class Treepuncher(
self._worker = asyncio.get_event_loop().create_task(self._work()) self._worker = asyncio.get_event_loop().create_task(self._work())
self.scheduler.resume() self.scheduler.resume()
self.logger.info("Treepuncher started") self.logger.info("Treepuncher started")
self.storage._set_state(SystemState(self.name, __VERSION__, time()))
async def stop(self, force: bool = False): async def stop(self, force: bool = False):
self._processing = False self._processing = False