From 11c60766909fc0c3cc1233804b96a30ee088fe1b Mon Sep 17 00:00:00 2001 From: Till Steinbach Date: Fri, 28 Jan 2022 20:52:07 +0100 Subject: [PATCH] Fix for login page, prepare 0.35.1 release --- CHANGELOG.md | 7 +++++- weconnect/weconnect.py | 53 +++++++++++++++++++++--------------------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2e98ff5..795f962 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ All notable changes to this project will be documented in this file. ## [Unreleased] - No unreleased changes so far +## [0.35.1] - 2022-01-28 +### Fixed +- Quick fix for login problem due to changes of the login page + ## [0.35.0] - 2022-01-24 ### Changed - Better tracking of several parallel requests @@ -469,7 +473,8 @@ Minor fix in observer interface ## [0.1.0] - 2021-05-26 Initial release -[unreleased]: https://github.com/tillsteinbach/WeConnect-python/compare/v0.35.0...HEAD +[unreleased]: https://github.com/tillsteinbach/WeConnect-python/compare/v0.35.1...HEAD +[0.35.1]: https://github.com/tillsteinbach/WeConnect-python/releases/tag/v0.35.1 [0.35.0]: https://github.com/tillsteinbach/WeConnect-python/releases/tag/v0.35.0 [0.34.0]: https://github.com/tillsteinbach/WeConnect-python/releases/tag/v0.34.0 [0.33.0]: https://github.com/tillsteinbach/WeConnect-python/releases/tag/v0.33.0 diff --git a/weconnect/weconnect.py b/weconnect/weconnect.py index 5aa1b57..32e3b19 100644 --- a/weconnect/weconnect.py +++ b/weconnect/weconnect.py @@ -365,45 +365,44 @@ def login(self) -> None: # noqa: C901 # pylint: disable=R0914, R0912, too-many- # Post form content and retrieve credentials page login2Response: requests.Response = self.__session.post(login2Url, headers=loginHeadersForm, data=formData, allow_redirects=True, timeout=self.timeout) + if login2Response.status_code != requests.codes['ok']: # pylint: disable=E1101 if login2Response.status_code == requests.codes['internal_server_error']: raise RetrievalError('Temporary server error during login') raise APICompatibilityError('Retrieving credentials page was not successfull,' f' status code: {login2Response.status_code}') - # Find credentials form on page to obtain inputs - credentialsFormRegex = r'[^\"]+)\"[^>]*>' \ - r'(?P.+?(?=))' - match = re.search(credentialsFormRegex, login2Response.text, flags=re.DOTALL) + credentialsTemplateRegex = r'))\s+\};\s+' + match = re.search(credentialsTemplateRegex, login2Response.text, flags=re.DOTALL) if match is None: - formErrorRegex = r'.*' \ - r'(?P.+?(?=)).*' - errorMatch: Optional[Match[str]] = re.search(formErrorRegex, login2Response.text, flags=re.DOTALL) - if errorMatch is not None: - raise AuthentificationError(errorMatch.groupdict()['errorMessage']) - - accountNotFoundRegex = r'.*
.*
' \ - r'(?P.+?(?=
))
.*.*' - errorMatch = re.search(accountNotFoundRegex, login2Response.text, flags=re.DOTALL) - if errorMatch is not None: - errorMessage: str = re.sub('<[^<]+?>', '', errorMatch.groupdict()['errorMessage']) - raise AuthentificationError(errorMessage) raise APICompatibilityError('No credentials form found') - # retrieve target url from form - target = match.groupdict()['formAction'] - - # Find all inputs and put those in formData dictionary - input2Regex = r'[^\"]+)\"([\\n\\r\s]value=\"(?P[^\"]+)\")?[^/]*/>' - form2Data: Dict[str, str] = {} - for match in re.finditer(input2Regex, match.groupdict()['formContent']): - if match.groupdict()['name']: - form2Data[match.groupdict()['name']] = match.groupdict()['value'] + if match.groupdict()['templateModel']: + lineRegex = r'\s*(?P[^\:]+)\:\s+[\'\{]?(?P.+)[\'\}][,]?' + form2Data: Dict[str, str] = {} + for match in re.finditer(lineRegex, match.groupdict()['templateModel']): + if match.groupdict()['name'] == 'templateModel': + templateModelString = '{' + match.groupdict()['value'] + '}' + if templateModelString.endswith(','): + templateModelString = templateModelString[:-len(',')] + templateModel = json.loads(templateModelString) + if 'relayState' in templateModel: + form2Data['relayState'] = templateModel['relayState'] + if 'hmac' in templateModel: + form2Data['hmac'] = templateModel['hmac'] + if 'emailPasswordForm' in templateModel and 'email' in templateModel['emailPasswordForm']: + form2Data['email'] = templateModel['emailPasswordForm']['email'] + if 'errorCode' in templateModel: + raise AuthentificationError('Error during login, is the username correct?') + elif match.groupdict()['name'] == 'csrf_token': + form2Data['_csrf'] = match.groupdict()['value'] + form2Data['password'] = self.password if not all(x in ['_csrf', 'relayState', 'hmac', 'email', 'password'] for x in form2Data): raise APICompatibilityError('Could not find all required input fields in login page') form2Data['password'] = self.password - # build url from form action - login3Url: str = 'https://identity.vwgroup.io' + target + # TODO improve build url from form action + login3Url = 'https://identity.vwgroup.io/signin-service/v1/a24fba63-34b3-4d43-b181-942111e6bda8@apps_vw-dilab_com/login/authenticate' # Post form content and retrieve userId in forwarding Location login3Response: requests.Response = self.__session.post(login3Url, headers=loginHeadersForm, data=form2Data, allow_redirects=False,