Skip to content

Commit

Permalink
version 1.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
FriendsOfGalaxy committed Sep 23, 2021
1 parent 745f040 commit 7bf93c7
Show file tree
Hide file tree
Showing 18 changed files with 291 additions and 113 deletions.
12 changes: 6 additions & 6 deletions src/backend_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ class BackendConfiguration(configparser.ConfigParser):
}
}
_HEADER_END_MARKER = "; === END OF AUTOGENERATED PART ===\n"
_README_HEADER = (
_INFO_HEADER = (
dedent(
"""\
; === START OF AUTOGENERATED PART ===
Expand All @@ -64,10 +64,10 @@ class BackendConfiguration(configparser.ConfigParser):
; DESCRIPTION:
;
; This file contains configuration values used for setting an initial way and a fallback way to communicate with Steam.
; These ways of communication are internally referred to as `backends`. The core idea of this approach is to switch to the fallback backend
; in case the initial one fails.
; These ways of communication are internally referred to as `backends`.
; The initial backend is loaded each time the plugin starts. If authentication fails, the backend is switched to the fallback one.
;
; A list of currently supported backends is available in the README of Steam plugin folder.
; More information about currently supported backends is available in the README of the Steam plugin project.
;
;
; STRUCTURE:
Expand All @@ -86,7 +86,7 @@ class BackendConfiguration(configparser.ConfigParser):
"""
) + _HEADER_END_MARKER
)

def __init__(self):
super().__init__()
self.read_dict(self._DEFAULT_CONFIG, source="<defaults from dict>")
Expand Down Expand Up @@ -131,7 +131,7 @@ def regenerate_user_config(cls, config_path: pathlib.Path):

new_content = cls._config_content_without_header(initial_content)
with open(config_path, "w") as f:
f.write(cls._README_HEADER)
f.write(cls._INFO_HEADER)
f.write(new_content)

@property
Expand Down
42 changes: 20 additions & 22 deletions src/backend_steam_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import ssl
from contextlib import suppress
from distutils.util import strtobool
from typing import Callable, List, Any, Dict
from typing import Callable, List, Any, Dict, Union
from urllib import parse

from galaxy.api.errors import (
Expand All @@ -27,7 +27,7 @@
Achievement,
GameLibrarySettings,
GameTime,
Authentication,
Authentication, NextStep,
)

from backend_interface import BackendInterface
Expand All @@ -46,8 +46,7 @@
from steam_network.user_info_cache import UserInfoCache
from steam_network.websocket_client import WebSocketClient, UserActionRequired
from steam_network.websocket_list import WebSocketList
from user_profile import UserProfileChecker, ProfileIsNotPublic, ProfileDoesNotExist, ParseError, \
NotPublicGameDetailsOrUserHasNoGames
from user_profile import UserProfileChecker, ProfileIsNotPublic, ProfileDoesNotExist, NotPublicGameDetailsOrUserHasNoGames

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -224,11 +223,7 @@ async def _handle_login_finished(self, credentials):
if result == UserActionRequired.NoActionRequired:
self._auth_data = None
self._store_credentials(self._user_info_cache.to_dict())
if not await self._is_public_profile():
return next_step_response(StartUri.PUBLIC_PROFILE_PROMPT, EndUri.PUBLIC_PROMPT_FINISHED)
return Authentication(
self._user_info_cache.steam_id, self._user_info_cache.persona_name
)
return await self._check_public_profile()
if result == UserActionRequired.EmailTwoFactorInputRequired:
return next_step_response(StartUri.TWO_FACTOR_MAIL, EndUri.TWO_FACTOR_MAIL_FINISHED)
if result == UserActionRequired.PhoneTwoFactorInputRequired:
Expand All @@ -251,11 +246,7 @@ async def _handle_two_step(self, params, fail, finish):
else:
self._auth_data = None
self._store_credentials(self._user_info_cache.to_dict())
if not await self._is_public_profile():
return next_step_response(StartUri.PUBLIC_PROFILE_PROMPT, EndUri.PUBLIC_PROMPT_FINISHED)
return Authentication(
self._user_info_cache.steam_id, self._user_info_cache.persona_name
)
return await self._check_public_profile()

async def _handle_two_step_mobile_finished(self, credentials):
parsed_url = parse.urlsplit(credentials["end_uri"])
Expand Down Expand Up @@ -283,24 +274,31 @@ async def _handle_public_prompt_finished(self, credentials):
parsed_url = parse.urlsplit(credentials["end_uri"])
params = dict(parse.parse_qsl(parsed_url.query))
user_wants_pp_fallback = strtobool(params.get("public_profile_fallback"))
if user_wants_pp_fallback and not await self._is_public_profile():
return next_step_response(StartUri.PUBLIC_PROFILE_PROMPT, EndUri.PUBLIC_PROMPT_FINISHED)
if user_wants_pp_fallback:
return await self._check_public_profile()
return Authentication(self._user_info_cache.steam_id, self._user_info_cache.persona_name)

async def _is_public_profile(self) -> bool:
async def _check_public_profile(self) -> Union[Authentication, NextStep]:
try:
return await self._user_profile_checker.check_is_public_by_steam_id(self._user_info_cache.steam_id)
await self._user_profile_checker.check_is_public_by_steam_id(self._user_info_cache.steam_id)
except ProfileIsNotPublic:
logger.debug(f"Profile with Steam64 ID: `{self._user_info_cache.steam_id}` is not public")
return next_step_response(StartUri.PP_PROMPT__PROFILE_IS_NOT_PUBLIC, EndUri.PUBLIC_PROMPT_FINISHED)
except NotPublicGameDetailsOrUserHasNoGames:
logger.debug(f"Profile with Steam64 ID: `{self._user_info_cache.steam_id}` has private games library or has no games")
return next_step_response(StartUri.PP_PROMPT__NOT_PUBLIC_GAME_DETAILS_OR_USER_HAS_NO_GAMES, EndUri.PUBLIC_PROMPT_FINISHED)
except ProfileDoesNotExist:
logger.warning(f"Profile with provided Steam64 ID: `{self._user_info_cache.steam_id}` does not exist")
raise UnknownBackendResponse()
except ValueError:
logger.warning(f"Incorrect provided Steam64 ID: `{self._user_info_cache.steam_id}`")
except NotPublicGameDetailsOrUserHasNoGames:
logger.debug(f"Profile with Steam64 ID: `{self._user_info_cache.steam_id}` has private games library or has no games")
except ParseError:
raise UnknownBackendResponse()
return False
except Exception:
return next_step_response(StartUri.PP_PROMPT__UNKNOWN_ERROR, EndUri.PUBLIC_PROMPT_FINISHED)
else:
return Authentication(
self._user_info_cache.steam_id, self._user_info_cache.persona_name
)

async def authenticate(self, stored_credentials=None):
if not stored_credentials:
Expand Down
11 changes: 10 additions & 1 deletion src/commonWeb/css/common.css
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,10 @@ body {
margin-right: 10px;
}

.error-icon--aligned-top {
margin-top: 3px;
}

/* FORM */

.main-form legend {
Expand Down Expand Up @@ -343,13 +347,18 @@ body {
align-items: end;
font-size: 13px;
line-height: 19px;
color: #f74638;
font-weight: 700;
color: rgb(255, 94, 81);
}

.error-message--centered {
align-items: center;
}

.error-message--start {
align-items: start;
}

.resend-code {
font-size: 13px;
color: #f2f2f2;
Expand Down
2 changes: 1 addition & 1 deletion src/commonWeb/img/icon-error.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
39 changes: 27 additions & 12 deletions src/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
Timestamp = NewType("Timestamp", int)

COOLDOWN_TIME = 5
AUTH_SETUP_ON_VERSION__CACHE_KEY = "auth_setup_on_version"

BACKEND_MAP = {
BackendMode.PublicProfiles: PublicProfilesBackend,
Expand Down Expand Up @@ -113,10 +114,12 @@ def handshake_complete(self):
with suppress(OSError):
self._backend_config.regenerate_user_config(USER_CONFIG_LOCATION)

def _switch_backend(self, backend: BackendMode):
def _switch_backend(self, backend: Optional[BackendMode]):
logger.info(f"Requested backend switch from {self.__backend_mode} to {backend}")
if backend is None:
raise ValueError("Backend switch stopped as requested.")
if backend == self.__backend_mode:
raise ValueError(f"Backend switch refused: already on ({backend})")
logger.info(f"Setting backend mode from {self.__backend_mode} to {backend}")
raise ValueError(f"Backend switch refused: alredy on {backend}.")
self._load_backend(backend)

def _load_backend(self, backend_mode: BackendMode):
Expand All @@ -139,16 +142,27 @@ def _load_backend(self, backend_mode: BackendMode):
except KeyError:
raise ValueError(f"Unknown backend mode: {backend_mode}")
self.__backend_mode = backend_mode

async def pass_login_credentials(self, step, credentials, cookies):
return await self._backend.pass_login_credentials(step, credentials, cookies)

result = await self._backend.pass_login_credentials(step, credentials, cookies)
self.__store_current_version_in_cache(key=AUTH_SETUP_ON_VERSION__CACHE_KEY)
return result

def __store_current_version_in_cache(self, key: str):
if self.persistent_cache.get(key) != __version__:
self.persistent_cache[key] = __version__
self.push_cache()

async def authenticate(self, stored_credentials=None):
def credentials_problem_handler(on_immediete_failure: Callable = self.lost_authentication):

def credentials_problem_handler(fallback: Callable = self.lost_authentication):
try:
self._switch_backend(self._backend_config.fallback_mode)
except ValueError:
on_immediete_failure()
fallback()
except Exception as e:
logger.error(f"Unexpected problem during backend switch: {e!r}")
fallback()
else:
self._backend.register_auth_lost_callback(credentials_problem_handler)

Expand All @@ -157,13 +171,14 @@ def raise_exception(exc):

try:
auth = await self._backend.authenticate(stored_credentials)
except NetworkError:
raise # casuses "Offline. Retry"
except NetworkError: # casuses "Offline. Retry"
raise
except (
InvalidCredentials, AccessDenied, # re-raised would cause "Connection Lost"
Exception # re-raised would cause "Offline. Retry"
) as err:
credentials_problem_handler(partial(raise_exception, err))
) as e:
logger.warning(f"Authentication for initial backend failed with {e!r}")
credentials_problem_handler(partial(raise_exception, e))
auth = await self._backend.authenticate(stored_credentials)
else:
self._backend.register_auth_lost_callback(credentials_problem_handler)
Expand Down
10 changes: 5 additions & 5 deletions src/public_profiles/custom_login/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ <h1 class="title">Connect with Steam</h1>
<label for="steam-id-error">Steam64 ID</label>
<input class="basic-input login-input errored" type="text" id="steam-id-error" name="steam_id" disabled="disabled">
</div>
<p class="error-message">
<p class="error-message error-message--centered">
<img src="../../commonWeb/img/icon-error.svg" alt="" class="error-icon"> Provided Steam64 ID is incorrect.
</p>
</fieldset>
Expand All @@ -47,8 +47,8 @@ <h1 class="title">Connect with Steam</h1>
<label for="steam-id-profile-does-not-exist">Steam64 ID</label>
<input class="basic-input login-input errored" type="text" id="steam-id-profile-does-not-exist" name="steam_id" disabled="disabled">
</div>
<p class="error-message">
<img src="../../commonWeb/img/icon-error.svg" alt="" class="error-icon"> Profile with provided Steam64 ID does not exist. Make sure that your provided ID is correct.
<p class="error-message error-message--start">
<img src="../../commonWeb/img/icon-error.svg" alt="" class="error-icon error-icon--aligned-top"> Profile with provided Steam64 ID does not exist. Make sure that your provided ID is correct.
</p>
</fieldset>
<fieldset id="loginProfileIsNotPublicFieldset" style="display: none;">
Expand All @@ -57,8 +57,8 @@ <h1 class="title">Connect with Steam</h1>
<label for="steam-id-profile-is-not-public">Steam64 ID</label>
<input class="basic-input login-input errored" type="text" id="steam-id-profile-is-not-public" name="steam_id" disabled="disabled">
</div>
<p class="error-message">
<img src="../../commonWeb/img/icon-error.svg" alt="" class="error-icon"> Your Steam profile is not public.
<p class="error-message error-message--start"">
<img src="../../commonWeb/img/icon-error.svg" alt="" class="error-icon error-icon--aligned-top"> Your Steam profile is not public.
</p>
<br>
<p>
Expand Down
16 changes: 9 additions & 7 deletions src/steam_network/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
class StartUri:
__INDEX = DIRNAME / 'custom_login' / 'index.html'

LOGIN = __INDEX % {'view': 'login'}
LOGIN_FAILED = __INDEX % {'view': 'login', 'errored': 'true'}
TWO_FACTOR_MAIL = __INDEX % {'view': 'steamguard'}
TWO_FACTOR_MAIL_FAILED = __INDEX % {'view': 'steamguard', 'errored': 'true'}
TWO_FACTOR_MOBILE = __INDEX % {'view': 'steamauthenticator'}
TWO_FACTOR_MOBILE_FAILED = __INDEX % {'view': 'steamauthenticator', 'errored': 'true'}
PUBLIC_PROFILE_PROMPT = __INDEX % {'view': 'publicprofileprompt'}
LOGIN = __INDEX % {'view': 'login'}
LOGIN_FAILED = __INDEX % {'view': 'login', 'errored': 'true'}
TWO_FACTOR_MAIL = __INDEX % {'view': 'steamguard'}
TWO_FACTOR_MAIL_FAILED = __INDEX % {'view': 'steamguard', 'errored': 'true'}
TWO_FACTOR_MOBILE = __INDEX % {'view': 'steamauthenticator'}
TWO_FACTOR_MOBILE_FAILED = __INDEX % {'view': 'steamauthenticator', 'errored': 'true'}
PP_PROMPT__PROFILE_IS_NOT_PUBLIC = __INDEX % {'view': 'pp_prompt__profile_is_not_public'}
PP_PROMPT__NOT_PUBLIC_GAME_DETAILS_OR_USER_HAS_NO_GAMES = __INDEX % {'view': 'pp_prompt__not_public_game_details_or_user_has_no_games'}
PP_PROMPT__UNKNOWN_ERROR = __INDEX % {'view': 'pp_prompt__unknown_error'}


class EndUri:
Expand Down
7 changes: 1 addition & 6 deletions src/steam_network/custom_login/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,6 @@ body.windows::-webkit-resizer {
height: 100%;
}

/* ICONS */

.error-icon--aligned-top {
margin-top: 3px;
}

/* FORM */

.main-form legend.prompt-legend {
Expand All @@ -58,6 +52,7 @@ body.windows::-webkit-resizer {

.main-form legend.centered {
display: flex;
align-items: center;
}

/* COMMONS */
Expand Down
Loading

0 comments on commit 7bf93c7

Please sign in to comment.