mirror of
https://github.com/norohind/FDEV-CAPI-Handler.git
synced 2026-10-22 23:12:11 +03:00
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:
8 files changed
+192
-35
No files matched your search
+21
-10
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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'])
|
||||||
@@ -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())
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user