FID as main identifier, not nickname

Because user can don't have a game so no nickname, but his
FDEV tokens still suits for not CAPI requests (i.e. squadrons api)
This commit is contained in:
norohind committed 2022-02-21 00:31:46 +03:00
1 parent d50df50ca1
commit b6392f8d20
8 files changed
+192 -35

No files matched your search

+21 -10
View File
@@ -76,27 +76,35 @@ class CAPIAuthorizer:
self.model.set_tokens(access_token, refresh_token, expires_in, timestamp_got_expires_in, state) self.model.set_tokens(access_token, refresh_token, expires_in, timestamp_got_expires_in, state)
try: nickname = utils.get_nickname(access_token)
nickname = utils.get_nickname(access_token) self.model.set_nickname(nickname, state)
if nickname is None:
logger.warning(f"Couldn't get or set nickname for state: {state!r}") # msg inform: no nickname
except Exception as e: fid = utils.get_fid(access_token)
logger.warning(f"Couldn't get nickname for state: {state!r}", exc_info=e) if fid is None:
raise KeyError(f"Couldn't get nickname for state: {state!r}") logger.error(f"Couldn't get FID for state: {state!r}")
raise exceptions.NoFID(f'No FID for {state!r}')
msg = {'status': 'ok', 'description': '', 'state': ''} msg = {'status': 'ok', 'description': '', 'state': ''}
if not self.model.set_nickname(nickname, state): if not self.model.set_fid(fid, state):
msg['description'] = 'Tokens updated' msg['description'] = 'Tokens updated'
else: else:
msg['description'] = 'Tokens saved' msg['description'] = 'Tokens saved'
msg['state'] = self.model.get_state_by_nickname(nickname) msg['state'] = self.model.get_state_by_fid(fid)
return msg return msg
def get_token_by_state(self, state: str) -> Union[dict, None]: def get_token_by_state(self, state: str) -> Union[dict, None]:
self.refresh_by_state(state) try:
self.refresh_by_state(state)
except exceptions.CAPIException:
return None
row = self.model.get_token_for_user(state) row = self.model.get_token_for_user(state)
if row is None: if row is None:
@@ -189,11 +197,14 @@ class CAPIAuthorizer:
def delete_by_state(self, state: str) -> None: def delete_by_state(self, state: str) -> None:
self.model.delete_row(state) self.model.delete_row(state)
def list_all_users(self) -> list[dict]: def list_all_valid_users(self) -> list[dict]:
return self.model.list_all_records() return self.model.list_all_valid_records()
def cleanup_orphans(self) -> None: def cleanup_orphans(self) -> None:
self.model.cleanup_orphans_records() self.model.cleanup_orphans_records()
def list_all_records(self) -> list[dict]:
return self.model.list_all_records()
capi_authorizer = CAPIAuthorizer(model.Model()) capi_authorizer = CAPIAuthorizer(model.Model())
+9 -1
View File
@@ -1,6 +1,14 @@
class RefreshFail(Exception): class CAPIException(Exception):
pass
class RefreshFail(CAPIException):
def __init__(self, message: str, status: str, state: str): def __init__(self, message: str, status: str, state: str):
self.message = message self.message = message
self.status = status self.status = status
self.state = state self.state = state
super().__init__(str(self.message) + ' for ' + str(self.state)) super().__init__(str(self.message) + ' for ' + str(self.state))
class NoFID(Exception):
pass
+28 -9
View File
@@ -56,11 +56,11 @@ class Model:
'state': state 'state': state
}) })
def set_nickname(self, nickname: str, state: str) -> bool: def set_fid(self, fid: str, state: str) -> bool:
""" """
Return True if inserted successfully, False if catch sqlite3.IntegrityError Return True if inserted successfully, False if catch sqlite3.IntegrityError
:param nickname: :param fid:
:param state: :param state:
:return: :return:
""" """
@@ -68,28 +68,35 @@ class Model:
try: try:
with self.db: with self.db:
self.db.execute( self.db.execute(
sqlite_requests.set_nickname_by_state, sqlite_requests.set_fid_by_state,
{'nickname': nickname, 'state': state} {'fid': fid, 'state': state}
) )
return True return True
except sqlite3.IntegrityError: except sqlite3.IntegrityError:
""" """
let's migrate new received data to old state let's migrate new received data to old state
1. Get old state by nickname 1. Get old state by fid
2. Remove row with old state 2. Remove row with old state
3. Set old state where new state 3. Set old state where new state
""" """
state_to_set: str = self.get_state_by_nickname(nickname) state_to_set: str = self.get_state_by_fid(fid)
self.delete_row(state_to_set) self.delete_row(state_to_set)
self.set_new_state(state_to_set, state) self.set_new_state(state_to_set, state)
with self.db: with self.db:
self.db.execute( self.db.execute(
sqlite_requests.set_nickname_by_state, sqlite_requests.set_fid_by_state,
{'nickname': nickname, 'state': state_to_set} {'fid': fid, 'state': state_to_set}
) )
return False return False
def set_nickname(self, nickname: Union[str, None], state: str) -> None:
with self.db:
self.db.execute(
sqlite_requests.set_nickname_by_state,
{'nickname': nickname, 'state': state}
)
def get_state_by_nickname(self, nickname: str) -> Union[None, str]: def get_state_by_nickname(self, nickname: str) -> Union[None, str]:
with self.db: with self.db:
nickname_f1 = self.db.execute(sqlite_requests.get_state_by_nickname, {'nickname': nickname}).fetchone() nickname_f1 = self.db.execute(sqlite_requests.get_state_by_nickname, {'nickname': nickname}).fetchone()
@@ -99,6 +106,15 @@ class Model:
return nickname_f1['state'] return nickname_f1['state']
def get_state_by_fid(self, fid: str) -> Union[None, str]:
with self.db:
fid_req_dict = self.db.execute(sqlite_requests.get_state_by_fid, {'fid': fid}).fetchone()
if fid_req_dict is None:
return None
return fid_req_dict['state']
def set_new_state(self, new_state: str, old_state: str) -> None: def set_new_state(self, new_state: str, old_state: str) -> None:
with self.db: with self.db:
self.db.execute(sqlite_requests.update_state_by_state, {'new_state': new_state, 'state': old_state}) self.db.execute(sqlite_requests.update_state_by_state, {'new_state': new_state, 'state': old_state})
@@ -118,9 +134,12 @@ class Model:
return token return token
def list_all_records(self) -> list: def list_all_valid_records(self) -> list:
return self.db.execute(sqlite_requests.select_nickname_state_all).fetchall() return self.db.execute(sqlite_requests.select_nickname_state_all).fetchall()
def cleanup_orphans_records(self) -> None: def cleanup_orphans_records(self) -> None:
with self.db: with self.db:
self.db.execute(sqlite_requests.del_orphans) self.db.execute(sqlite_requests.del_orphans)
def list_all_records(self) -> list[dict]:
return self.db.execute(sqlite_requests.select_all).fetchall()
+14 -6
View File
@@ -7,9 +7,10 @@ schema = """create table if not exists authorizations (
refresh_token text, refresh_token text,
expires_in text, expires_in text,
timestamp_got_expires_in text, timestamp_got_expires_in text,
nickname text unique, nickname text,
refresh_tries int default 0, refresh_tries int default 0,
usages int default 0 usages int default 0,
fid text unique
);""" );"""
insert_auth_init = """insert into authorizations insert_auth_init = """insert into authorizations
@@ -32,10 +33,14 @@ set
refresh_tries = 0 refresh_tries = 0
where state = :state;""" where state = :state;"""
set_nickname_by_state = """update authorizations set nickname = :nickname where state = :state;""" set_fid_by_state = """update authorizations set fid = :fid where state = :state;"""
set_nickname_by_state = "update authorizations set nickname = :nickname where state = :state;"
get_state_by_nickname = """select state from authorizations where nickname = :nickname;""" get_state_by_nickname = """select state from authorizations where nickname = :nickname;"""
get_state_by_fid = """select state from authorizations where fid = :fid;"""
update_state_by_state = """update authorizations set state = :new_state where state = :state;""" update_state_by_state = """update authorizations set state = :new_state where state = :state;"""
refresh_times_increment = """update authorizations set refresh_tries = refresh_tries + 1 where state = :state;""" refresh_times_increment = """update authorizations set refresh_tries = refresh_tries + 1 where state = :state;"""
@@ -43,11 +48,14 @@ refresh_times_increment = """update authorizations set refresh_tries = refresh_t
get_token_for_user = """select get_token_for_user = """select
access_token, access_token,
timestamp_got_expires_in + expires_in as expires_on, timestamp_got_expires_in + expires_in as expires_on,
nickname nickname,
fid
from authorizations where state = :state;""" from authorizations where state = :state;"""
select_nickname_state_all = """select nickname, state from authorizations where nickname is not null;""" select_nickname_state_all = """select nickname, fid, state from authorizations where fid is not null;"""
del_orphans = """delete from authorizations where nickname is null;""" del_orphans = """delete from authorizations where fid is null;"""
increment_usages = "update authorizations set usages = usages + 1 where state = :state;" increment_usages = "update authorizations set usages = usages + 1 where state = :state;"
select_all = """select * from authorizations;"""
+40 -6
View File
@@ -1,8 +1,13 @@
import hashlib import hashlib
import base64 import base64
from typing import Union
from EDMCLogging import get_main_logger
import requests import requests
import config import config
logger = get_main_logger()
def get_tokens_request(code, code_verifier) -> requests.Response: def get_tokens_request(code, code_verifier) -> requests.Response:
""" """
@@ -14,7 +19,8 @@ def get_tokens_request(code, code_verifier) -> requests.Response:
""" """
token_request: requests.Response = requests.post( token_request: requests.Response = requests.post(
url=config.TOKEN_URL, url=config.TOKEN_URL,
headers={ headers=
{
'Content-Type': 'application/x-www-form-urlencoded', 'Content-Type': 'application/x-www-form-urlencoded',
'User-Agent': config.PROPER_USER_AGENT 'User-Agent': config.PROPER_USER_AGENT
}, },
@@ -27,11 +33,39 @@ def get_tokens_request(code, code_verifier) -> requests.Response:
return token_request return token_request
def get_nickname(access_token: str) -> str: def get_nickname(access_token: str) -> Union[str, None]:
return requests.get( nickname = None
url='https://companion.orerve.net/profile', try:
headers={'Authorization': f'Bearer {access_token}', nickname = requests.get(
'User-Agent': config.PROPER_USER_AGENT}).json()["commander"]["name"] url='https://companion.orerve.net/profile',
headers=
{
'Authorization': f'Bearer {access_token}',
'User-Agent': config.PROPER_USER_AGENT
}
).json()["commander"]["name"]
except Exception as e:
logger.warning(f"Couldn't get or set nickname", exc_info=e)
return nickname
def get_fid(access_token: str) -> str:
fid = None
try:
fid = requests.get(
url='https://auth.frontierstore.net/me',
headers={
'Authorization': f'Bearer {access_token}',
'User-Agent': config.PROPER_USER_AGENT
}
).json()["customer_id"]
except Exception as e:
logger.exception(f"Couldn't get FID", exc_info=e)
return fid
def refresh_request(refresh_token: str) -> requests.Response: def refresh_request(refresh_token: str) -> requests.Response:
+1 -1
View File
@@ -13,7 +13,7 @@ access_key = os.getenv('access_key')
REDIRECT_URL = requests.utils.quote(os.getenv('REDIRECT_URL', '')) REDIRECT_URL = requests.utils.quote(os.getenv('REDIRECT_URL', ''))
AUTH_URL = 'https://auth.frontierstore.net/auth' AUTH_URL = 'https://auth.frontierstore.net/auth'
TOKEN_URL = 'https://auth.frontierstore.net/token' TOKEN_URL = 'https://auth.frontierstore.net/token'
PROPER_USER_AGENT = 'EDCD-a31-0.2' PROPER_USER_AGENT = 'EDCD-a31-0.3'
REDIRECT_HTML_TEMPLATE = """ REDIRECT_HTML_TEMPLATE = """
<!DOCTYPE HTML> <!DOCTYPE HTML>
<html> <html>
+52
View File
@@ -0,0 +1,52 @@
import sqlite3
import capi
db = sqlite3.connect('companion-api.sqlite')
backup_table_request = "ALTER TABLE authorizations RENAME TO authorizations_bak;"
restore_table_request = """
INSERT INTO authorizations (
code_verifier,
state,
timestamp_init,
code,
access_token,
refresh_token,
expires_in,
timestamp_got_expires_in,
nickname,
refresh_tries,
usages
)
SELECT
code_verifier,
state,
timestamp_init,
code,
access_token,
refresh_token,
expires_in,
timestamp_got_expires_in,
nickname,
refresh_tries,
usages
FROM authorizations_bak;
"""
drop_old_table_request = "drop table authorizations_bak;"
with db:
db.execute(backup_table_request)
db.execute(capi.sqlite_requests.schema)
db.execute(restore_table_request)
db.execute(drop_old_table_request)
db.close() # schema migration ended, begin inserting fids
all_records = capi.capi_authorizer.list_all_records()
for record in all_records:
if record['fid'] is None and record['nickname'] is not None:
fid = capi.utils.get_fid(record['access_token'])
capi.capi_authorizer.model.set_fid(fid, record['state'])
+27 -2
View File
@@ -2,6 +2,7 @@ import falcon
import waitress import waitress
import json import json
import capi
from capi import capi_authorizer from capi import capi_authorizer
import config import config
from EDMCLogging import get_main_logger from EDMCLogging import get_main_logger
@@ -82,6 +83,29 @@ class TokenByNickname:
capi_authorizer.delete_by_state(state) capi_authorizer.delete_by_state(state)
class TokenByFID:
NOT_FOUND_DESCRIPTION = 'No such FID was found'
@falcon.before(check_secret)
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response, fid: str):
resp.content_type = falcon.MEDIA_JSON
state = capi_authorizer.model.get_state_by_fid(fid)
if state is None:
raise falcon.HTTPNotFound(description=self.NOT_FOUND_DESCRIPTION)
tokens = capi.capi_authorizer.get_token_by_state(state)
resp.text = json.dumps(tokens)
@falcon.before(check_secret)
def on_delete(self, req: falcon.request.Request, resp: falcon.response.Response, fid: str):
state = capi_authorizer.model.get_state_by_fid(fid)
if state is None:
raise falcon.HTTPNotFound(description=self.NOT_FOUND_DESCRIPTION)
capi_authorizer.delete_by_state(state)
class CleanOrphanRecords: class CleanOrphanRecords:
def on_post(self, req: falcon.request.Request, resp: falcon.response.Response): def on_post(self, req: falcon.request.Request, resp: falcon.response.Response):
capi_authorizer.cleanup_orphans() capi_authorizer.cleanup_orphans()
@@ -91,7 +115,7 @@ class ListTokens:
@falcon.before(check_secret) @falcon.before(check_secret)
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response): def on_get(self, req: falcon.request.Request, resp: falcon.response.Response):
resp.content_type = falcon.MEDIA_JSON resp.content_type = falcon.MEDIA_JSON
resp.text = json.dumps(capi_authorizer.list_all_users()) resp.text = json.dumps(capi_authorizer.list_all_valid_users())
class RandomToken: class RandomToken:
@@ -100,7 +124,7 @@ class RandomToken:
def on_get(self, req: falcon.request.Request, resp: falcon.response.Response): def on_get(self, req: falcon.request.Request, resp: falcon.response.Response):
resp.content_type = falcon.MEDIA_JSON resp.content_type = falcon.MEDIA_JSON
import random import random
list_users = capi_authorizer.list_all_users() list_users = capi_authorizer.list_all_valid_users()
if len(list_users) == 0: if len(list_users) == 0:
raise falcon.HTTPNotFound(description='No users in DB') raise falcon.HTTPNotFound(description='No users in DB')
@@ -117,6 +141,7 @@ application.add_route('/users/{state}', TokenByState()) # for legacy reasons
application.add_route('/random_token', RandomToken()) # for legacy reasons, subject to decommissioning application.add_route('/random_token', RandomToken()) # for legacy reasons, subject to decommissioning
application.add_route('/users/by-state/{state}', TokenByState()) application.add_route('/users/by-state/{state}', TokenByState())
application.add_route('/users/by-nickname/{nickname}', TokenByNickname()) application.add_route('/users/by-nickname/{nickname}', TokenByNickname())
application.add_route('/users/by-fid/{fid}', TokenByFID())
application.add_route('/users', ListTokens()) application.add_route('/users', ListTokens())
application.add_route('/tools/clean-orphan-records', CleanOrphanRecords()) application.add_route('/tools/clean-orphan-records', CleanOrphanRecords())