From 92665551cfd8d70d9006b66ffa8b59cd0d4c1ffe Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Mon, 3 May 2021 01:46:26 +0200 Subject: [PATCH 01/11] Add get encryption method --- sagemcom_api/client.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index afa71d2..4db2bb2 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -264,7 +264,7 @@ async def __api_request_async(self, actions, priority=False): raise ConnectionError(str(exception)) from exception async def login(self): - """TODO.""" + """Login to the SagemCom F@st router using a username and password.""" actions = { "id": 0, "method": "logIn", @@ -293,7 +293,7 @@ async def login(self): response = await self.__api_request_async([actions], True) except asyncio.TimeoutError as exception: raise LoginTimeoutException( - "Request timed-out. This is mainly due to using the wrong encryption method." + "Login request timed-out. This could be caused by using the wrong encryption method, or using a (non) SSL connection." ) from exception data = self.__get_response(response) @@ -315,6 +315,18 @@ async def logout(self): self._server_nonce = "" self._request_id = -1 + async def get_encryption_method(self): + """Automatically decide which encryption method to use for authentication.""" + for encryption_method in EncryptionMethod: + try: + self.authentication_method = encryption_method + await self.login() + return encryption_method + except LoginTimeoutException: + pass + + return None + async def get_value_by_xpath(self, xpath: str, options: dict | None = None) -> dict: """ Retrieve raw value from router using XPath. From 9d6094ef6965cb273d0d7118d640426f17e30ad2 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Mon, 3 May 2021 02:05:21 +0200 Subject: [PATCH 02/11] Fix get_encryption_method --- sagemcom_api/client.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index 4db2bb2..e052d8f 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -77,6 +77,7 @@ def __init__( self.host = host self.username = username self.authentication_method = authentication_method + self.password = password self._password_hash = self.__generate_hash(password) self.protocol = "https" if ssl else "http" @@ -316,10 +317,12 @@ async def logout(self): self._request_id = -1 async def get_encryption_method(self): - """Automatically decide which encryption method to use for authentication.""" + """Determine which encryption method to use for authentication and set it directly.""" for encryption_method in EncryptionMethod: try: self.authentication_method = encryption_method + self._password_hash = self.__generate_hash(self.password) + await self.login() return encryption_method except LoginTimeoutException: From 3951a2d7fe8722b9879d40fbdfbff75f2a3dc586 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Mon, 3 May 2021 02:05:36 +0200 Subject: [PATCH 03/11] Update documentation --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 85fdf4e..373951c 100644 --- a/README.md +++ b/README.md @@ -112,7 +112,9 @@ asyncio.run(main()) ### Determine the EncryptionMethod -(not supported yet) +If you are not sure which encryption method to use, you can leave it empty or pass `None` and use `get_encryption_method` to determine the encryption method. + +`get_encryption_method` will return an `EncryptionMethod` or `None` when no match is found. Best would be to use this function only during your initial investigation. ### Handle exceptions From 2d7308306748f4eb1f6726fde8187b2474e91128 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Mon, 3 May 2021 02:12:08 +0200 Subject: [PATCH 04/11] Improve function --- README.md | 4 +++- sagemcom_api/client.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 373951c..1980753 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,9 @@ asyncio.run(main()) If you are not sure which encryption method to use, you can leave it empty or pass `None` and use `get_encryption_method` to determine the encryption method. -`get_encryption_method` will return an `EncryptionMethod` or `None` when no match is found. Best would be to use this function only during your initial investigation. +`get_encryption_method` will return an `EncryptionMethod` when a match is found. Best would be to use this function only during your initial investigation. + +This function will throw a `LoginTimeoutException` when no match is found, since this is still a HTTP Time Out. We are not sure if this is caused by the wrong encrypton method, or by using an inaccessible host. ### Handle exceptions diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index e052d8f..f1153ac 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -328,7 +328,7 @@ async def get_encryption_method(self): except LoginTimeoutException: pass - return None + raise LoginTimeoutException async def get_value_by_xpath(self, xpath: str, options: dict | None = None) -> dict: """ From 48c4b78f29c225dd190d12783e4d07a60595c547 Mon Sep 17 00:00:00 2001 From: Mick Vleeshouwer Date: Mon, 3 May 2021 02:15:44 +0200 Subject: [PATCH 05/11] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1980753..9aadfff 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ If you are not sure which encryption method to use, you can leave it empty or pa `get_encryption_method` will return an `EncryptionMethod` when a match is found. Best would be to use this function only during your initial investigation. -This function will throw a `LoginTimeoutException` when no match is found, since this is still a HTTP Time Out. We are not sure if this is caused by the wrong encrypton method, or by using an inaccessible host. +This function will throw a `LoginTimeoutException` when no match is found, since this is still a HTTP Time Out. This could caused by the wrong encryption method, but also by trying to connect to an inaccessible host. ### Handle exceptions From 58e5aafa897cb26a76db51e2958f859952a00b74 Mon Sep 17 00:00:00 2001 From: Mick Date: Sun, 7 Jan 2024 18:00:57 +0000 Subject: [PATCH 06/11] No default method required --- sagemcom_api/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index f1153ac..4116e7b 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -60,7 +60,7 @@ def __init__( host: str, username: str, password: str, - authentication_method: EncryptionMethod, + authentication_method: EncryptionMethod | None = None, session: ClientSession | None = None, ssl: bool | None = False, verify_ssl: bool | None = True, @@ -329,7 +329,7 @@ async def get_encryption_method(self): pass raise LoginTimeoutException - + async def get_value_by_xpath(self, xpath: str, options: dict | None = None) -> dict: """ Retrieve raw value from router using XPath. From 9b894b24a293050cc2394dddfc058db59692e7ed Mon Sep 17 00:00:00 2001 From: Mick Date: Sun, 7 Jan 2024 18:02:47 +0000 Subject: [PATCH 07/11] Fix merge lint --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9aadfff..01ec20c 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,7 @@ asyncio.run(main()) If you are not sure which encryption method to use, you can leave it empty or pass `None` and use `get_encryption_method` to determine the encryption method. -`get_encryption_method` will return an `EncryptionMethod` when a match is found. Best would be to use this function only during your initial investigation. +`get_encryption_method` will return an `EncryptionMethod` when a match is found. Best would be to use this function only during your initial investigation. This function will throw a `LoginTimeoutException` when no match is found, since this is still a HTTP Time Out. This could caused by the wrong encryption method, but also by trying to connect to an inaccessible host. From 2acc1bd01f55041cf3178978ca09495ffbbd8951 Mon Sep 17 00:00:00 2001 From: Mick Date: Sun, 7 Jan 2024 18:05:32 +0000 Subject: [PATCH 08/11] Improve function --- sagemcom_api/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index 4116e7b..5b89648 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -325,10 +325,10 @@ async def get_encryption_method(self): await self.login() return encryption_method - except LoginTimeoutException: + except (LoginTimeoutException, AuthenticationException): pass - raise LoginTimeoutException + return None async def get_value_by_xpath(self, xpath: str, options: dict | None = None) -> dict: """ From fd5d89bb27eb307a85ccbab0a177b420abd02e9e Mon Sep 17 00:00:00 2001 From: Mick Date: Sat, 8 Jun 2024 19:06:05 +0000 Subject: [PATCH 09/11] Improve login methods --- sagemcom_api/client.py | 15 ++++++++++++--- sagemcom_api/const.py | 2 ++ sagemcom_api/exceptions.py | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index 5b89648..f2ea17e 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -1,4 +1,5 @@ """Client to communicate with Sagemcom F@st internal APIs.""" + from __future__ import annotations import asyncio @@ -26,6 +27,7 @@ DEFAULT_USER_AGENT, XMO_ACCESS_RESTRICTION_ERR, XMO_AUTHENTICATION_ERR, + XMO_LOGIN_RETRY_ERR, XMO_MAX_SESSION_COUNT_ERR, XMO_NO_ERR, XMO_NON_WRITABLE_PARAMETER_ERR, @@ -38,6 +40,7 @@ AccessRestrictionException, AuthenticationException, BadRequestException, + LoginRetryErrorException, LoginTimeoutException, MaximumSessionCountException, NonWritableParameterException, @@ -79,7 +82,6 @@ def __init__( self.authentication_method = authentication_method self.password = password self._password_hash = self.__generate_hash(password) - self.protocol = "https" if ssl else "http" self._current_nonce = None @@ -321,11 +323,18 @@ async def get_encryption_method(self): for encryption_method in EncryptionMethod: try: self.authentication_method = encryption_method - self._password_hash = self.__generate_hash(self.password) + self._password_hash = self.__generate_hash( + self.password, encryption_method + ) await self.login() + return encryption_method - except (LoginTimeoutException, AuthenticationException): + except ( + LoginTimeoutException, + AuthenticationException, + LoginRetryErrorException, + ): pass return None diff --git a/sagemcom_api/const.py b/sagemcom_api/const.py index 7475eb2..f0add30 100644 --- a/sagemcom_api/const.py +++ b/sagemcom_api/const.py @@ -1,4 +1,5 @@ """Constants for the Sagemcom F@st client.""" + API_ENDPOINT = "/cgi/json-req" DEFAULT_TIMEOUT = 7 @@ -12,3 +13,4 @@ XMO_REQUEST_NO_ERR = "XMO_REQUEST_NO_ERR" XMO_UNKNOWN_PATH_ERR = "XMO_UNKNOWN_PATH_ERR" XMO_MAX_SESSION_COUNT_ERR = "XMO_MAX_SESSION_COUNT_ERR" +XMO_LOGIN_RETRY_ERR = "XMO_LOGIN_RETRY_ERR" diff --git a/sagemcom_api/exceptions.py b/sagemcom_api/exceptions.py index e99b07b..3906745 100644 --- a/sagemcom_api/exceptions.py +++ b/sagemcom_api/exceptions.py @@ -10,6 +10,10 @@ class AuthenticationException(Exception): """Raised when authentication is not correct.""" +class LoginRetryErrorException(Exception): + """Raised when too many login retries are attempted.""" + + class LoginTimeoutException(Exception): """Raised when a timeout is encountered during login.""" From 4f166496c2f56eec631b414198a928233100f821 Mon Sep 17 00:00:00 2001 From: Mick Date: Sat, 8 Jun 2024 19:16:54 +0000 Subject: [PATCH 10/11] Fix get encryption method --- sagemcom_api/client.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index f2ea17e..d426359 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -268,6 +268,7 @@ async def __api_request_async(self, actions, priority=False): async def login(self): """Login to the SagemCom F@st router using a username and password.""" + actions = { "id": 0, "method": "logIn", @@ -329,6 +330,10 @@ async def get_encryption_method(self): await self.login() + self._server_nonce = "" + self._session_id = 0 + self._request_id = -1 + return encryption_method except ( LoginTimeoutException, From 5bd2c38613d3151fa0856715dfbe5693b1ba736c Mon Sep 17 00:00:00 2001 From: Mick Date: Sat, 8 Jun 2024 19:20:44 +0000 Subject: [PATCH 11/11] Add LoginRetryError --- sagemcom_api/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/sagemcom_api/client.py b/sagemcom_api/client.py index d426359..b4fea4c 100644 --- a/sagemcom_api/client.py +++ b/sagemcom_api/client.py @@ -232,6 +232,9 @@ async def __post(self, url, data): if action_error_desc == XMO_MAX_SESSION_COUNT_ERR: raise MaximumSessionCountException(action_error) + if action_error_desc == XMO_LOGIN_RETRY_ERR: + raise LoginRetryErrorException(action_error) + raise UnknownException(action_error) return result