mirror of
https://github.com/malmeloo/FindMy.py.git
synced 2026-04-17 22:53:56 +02:00
feat: export all classes in top-level package
This commit is contained in:
@@ -1,15 +1,71 @@
|
||||
"""A package providing everything you need to work with Apple's FindMy network."""
|
||||
|
||||
from . import errors, keys, plist, reports, scanner
|
||||
from .accessory import FindMyAccessory
|
||||
from .keys import KeyPair
|
||||
from .accessory import FindMyAccessory, FindMyAccessoryMapping, RollingKeyPairSource
|
||||
from .errors import (
|
||||
InvalidCredentialsError,
|
||||
InvalidStateError,
|
||||
UnauthorizedError,
|
||||
UnhandledProtocolError,
|
||||
)
|
||||
from .keys import HasHashedPublicKey, HasPublicKey, KeyPair, KeyPairMapping, KeyPairType
|
||||
from .reports import (
|
||||
AccountStateMapping,
|
||||
AppleAccount,
|
||||
AsyncAppleAccount,
|
||||
AsyncSmsSecondFactor,
|
||||
AsyncTrustedDeviceSecondFactor,
|
||||
BaseAnisetteProvider,
|
||||
BaseAppleAccount,
|
||||
BaseSecondFactorMethod,
|
||||
LocalAnisetteMapping,
|
||||
LocalAnisetteProvider,
|
||||
LoginState,
|
||||
RemoteAnisetteMapping,
|
||||
RemoteAnisetteProvider,
|
||||
SmsSecondFactorMethod,
|
||||
SyncSmsSecondFactor,
|
||||
SyncTrustedDeviceSecondFactor,
|
||||
TrustedDeviceSecondFactorMethod,
|
||||
)
|
||||
from .scanner import (
|
||||
NearbyOfflineFindingDevice,
|
||||
OfflineFindingDevice,
|
||||
OfflineFindingScanner,
|
||||
SeparatedOfflineFindingDevice,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"AccountStateMapping",
|
||||
"AppleAccount",
|
||||
"AsyncAppleAccount",
|
||||
"AsyncSmsSecondFactor",
|
||||
"AsyncTrustedDeviceSecondFactor",
|
||||
"BaseAnisetteProvider",
|
||||
"BaseAppleAccount",
|
||||
"BaseSecondFactorMethod",
|
||||
"FindMyAccessory",
|
||||
"FindMyAccessoryMapping",
|
||||
"HasHashedPublicKey",
|
||||
"HasPublicKey",
|
||||
"InvalidCredentialsError",
|
||||
"InvalidStateError",
|
||||
"KeyPair",
|
||||
"errors",
|
||||
"keys",
|
||||
"plist",
|
||||
"reports",
|
||||
"scanner",
|
||||
"KeyPairMapping",
|
||||
"KeyPairType",
|
||||
"LocalAnisetteMapping",
|
||||
"LocalAnisetteProvider",
|
||||
"LoginState",
|
||||
"NearbyOfflineFindingDevice",
|
||||
"OfflineFindingDevice",
|
||||
"OfflineFindingScanner",
|
||||
"RemoteAnisetteMapping",
|
||||
"RemoteAnisetteProvider",
|
||||
"RollingKeyPairSource",
|
||||
"SeparatedOfflineFindingDevice",
|
||||
"SmsSecondFactorMethod",
|
||||
"SyncSmsSecondFactor",
|
||||
"SyncTrustedDeviceSecondFactor",
|
||||
"TrustedDeviceSecondFactorMethod",
|
||||
"UnauthorizedError",
|
||||
"UnhandledProtocolError",
|
||||
)
|
||||
|
||||
@@ -16,7 +16,7 @@ from typing_extensions import override
|
||||
from findmy.util.abc import Serializable
|
||||
from findmy.util.files import read_data_json, read_data_plist, save_and_return_json
|
||||
|
||||
from .keys import KeyGenerator, KeyPair, KeyType
|
||||
from .keys import KeyGenerator, KeyPair, KeyPairType
|
||||
from .util import crypto
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -116,8 +116,8 @@ class FindMyAccessory(RollingKeyPairSource, Serializable[FindMyAccessoryMapping]
|
||||
:param skn: The SKN for the primary key.
|
||||
:param sks: The SKS for the secondary key.
|
||||
"""
|
||||
self._primary_gen = AccessoryKeyGenerator(master_key, skn, KeyType.PRIMARY)
|
||||
self._secondary_gen = AccessoryKeyGenerator(master_key, sks, KeyType.SECONDARY)
|
||||
self._primary_gen = _AccessoryKeyGenerator(master_key, skn, KeyPairType.PRIMARY)
|
||||
self._secondary_gen = _AccessoryKeyGenerator(master_key, sks, KeyPairType.SECONDARY)
|
||||
self._paired_at: datetime = paired_at
|
||||
if self._paired_at.tzinfo is None:
|
||||
self._paired_at = self._paired_at.astimezone()
|
||||
@@ -368,14 +368,14 @@ class FindMyAccessory(RollingKeyPairSource, Serializable[FindMyAccessoryMapping]
|
||||
raise ValueError(msg) from None
|
||||
|
||||
|
||||
class AccessoryKeyGenerator(KeyGenerator[KeyPair]):
|
||||
class _AccessoryKeyGenerator(KeyGenerator[KeyPair]):
|
||||
"""KeyPair generator. Uses the same algorithm internally as FindMy accessories do."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
master_key: bytes,
|
||||
initial_sk: bytes,
|
||||
key_type: KeyType = KeyType.UNKNOWN,
|
||||
key_type: KeyPairType = KeyPairType.UNKNOWN,
|
||||
) -> None:
|
||||
"""
|
||||
Initialize the key generator.
|
||||
@@ -411,7 +411,7 @@ class AccessoryKeyGenerator(KeyGenerator[KeyPair]):
|
||||
return self._initial_sk
|
||||
|
||||
@property
|
||||
def key_type(self) -> KeyType:
|
||||
def key_type(self) -> KeyPairType:
|
||||
"""The type of key this generator produces."""
|
||||
return self._key_type
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ if TYPE_CHECKING:
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
class KeyType(Enum):
|
||||
class KeyPairType(Enum):
|
||||
"""Enum of possible key types."""
|
||||
|
||||
UNKNOWN = 0
|
||||
@@ -133,7 +133,7 @@ class KeyPair(HasPublicKey, Serializable[KeyPairMapping]):
|
||||
def __init__(
|
||||
self,
|
||||
private_key: bytes,
|
||||
key_type: KeyType = KeyType.UNKNOWN,
|
||||
key_type: KeyPairType = KeyPairType.UNKNOWN,
|
||||
name: str | None = None,
|
||||
) -> None:
|
||||
"""Initialize the :meth:`KeyPair` with the private key bytes."""
|
||||
@@ -147,7 +147,7 @@ class KeyPair(HasPublicKey, Serializable[KeyPairMapping]):
|
||||
self._name = name
|
||||
|
||||
@property
|
||||
def key_type(self) -> KeyType:
|
||||
def key_type(self) -> KeyPairType:
|
||||
"""Type of this key."""
|
||||
return self._key_type
|
||||
|
||||
@@ -217,7 +217,7 @@ class KeyPair(HasPublicKey, Serializable[KeyPairMapping]):
|
||||
try:
|
||||
return cls(
|
||||
private_key=base64.b64decode(val["private_key"]),
|
||||
key_type=KeyType(val["key_type"]),
|
||||
key_type=KeyPairType(val["key_type"]),
|
||||
name=val["name"],
|
||||
)
|
||||
except KeyError as e:
|
||||
|
||||
@@ -1,16 +1,40 @@
|
||||
"""Code related to fetching location reports."""
|
||||
|
||||
from .account import AppleAccount, AsyncAppleAccount
|
||||
from .anisette import BaseAnisetteProvider, RemoteAnisetteProvider
|
||||
from .account import AccountStateMapping, AppleAccount, AsyncAppleAccount, BaseAppleAccount
|
||||
from .anisette import (
|
||||
BaseAnisetteProvider,
|
||||
LocalAnisetteMapping,
|
||||
LocalAnisetteProvider,
|
||||
RemoteAnisetteMapping,
|
||||
RemoteAnisetteProvider,
|
||||
)
|
||||
from .state import LoginState
|
||||
from .twofactor import SmsSecondFactorMethod, TrustedDeviceSecondFactorMethod
|
||||
from .twofactor import (
|
||||
AsyncSmsSecondFactor,
|
||||
AsyncTrustedDeviceSecondFactor,
|
||||
BaseSecondFactorMethod,
|
||||
SmsSecondFactorMethod,
|
||||
SyncSmsSecondFactor,
|
||||
SyncTrustedDeviceSecondFactor,
|
||||
TrustedDeviceSecondFactorMethod,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"AccountStateMapping",
|
||||
"AppleAccount",
|
||||
"AsyncAppleAccount",
|
||||
"AsyncSmsSecondFactor",
|
||||
"AsyncTrustedDeviceSecondFactor",
|
||||
"BaseAnisetteProvider",
|
||||
"BaseAppleAccount",
|
||||
"BaseSecondFactorMethod",
|
||||
"LocalAnisetteMapping",
|
||||
"LocalAnisetteProvider",
|
||||
"LoginState",
|
||||
"RemoteAnisetteMapping",
|
||||
"RemoteAnisetteProvider",
|
||||
"SmsSecondFactorMethod",
|
||||
"SyncSmsSecondFactor",
|
||||
"SyncTrustedDeviceSecondFactor",
|
||||
"TrustedDeviceSecondFactorMethod",
|
||||
)
|
||||
|
||||
@@ -106,7 +106,7 @@ _A = TypeVar("_A", bound="BaseAppleAccount")
|
||||
_F = Callable[Concatenate[_A, _P], _R]
|
||||
|
||||
|
||||
def require_login_state(*states: LoginState) -> Callable[[_F], _F]:
|
||||
def _require_login_state(*states: LoginState) -> Callable[[_F], _F]:
|
||||
"""Enforce a login state as precondition for a method."""
|
||||
|
||||
def decorator(func: _F) -> _F:
|
||||
@@ -403,7 +403,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
return self._login_state
|
||||
|
||||
@property
|
||||
@require_login_state(
|
||||
@_require_login_state(
|
||||
LoginState.LOGGED_IN,
|
||||
LoginState.AUTHENTICATED,
|
||||
LoginState.REQUIRE_2FA,
|
||||
@@ -414,7 +414,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
return self._account_info["account_name"] if self._account_info else None
|
||||
|
||||
@property
|
||||
@require_login_state(
|
||||
@_require_login_state(
|
||||
LoginState.LOGGED_IN,
|
||||
LoginState.AUTHENTICATED,
|
||||
LoginState.REQUIRE_2FA,
|
||||
@@ -425,7 +425,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
return self._account_info["first_name"] if self._account_info else None
|
||||
|
||||
@property
|
||||
@require_login_state(
|
||||
@_require_login_state(
|
||||
LoginState.LOGGED_IN,
|
||||
LoginState.AUTHENTICATED,
|
||||
LoginState.REQUIRE_2FA,
|
||||
@@ -496,7 +496,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
except (RuntimeError, OSError, ConnectionError) as e:
|
||||
logger.warning("Error closing HTTP session: %s", e)
|
||||
|
||||
@require_login_state(LoginState.LOGGED_OUT)
|
||||
@_require_login_state(LoginState.LOGGED_OUT)
|
||||
@override
|
||||
async def login(self, username: str, password: str) -> LoginState:
|
||||
"""See :meth:`BaseAppleAccount.login`."""
|
||||
@@ -508,7 +508,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
# AUTHENTICATED -> LOGGED_IN
|
||||
return await self._login_mobileme()
|
||||
|
||||
@require_login_state(LoginState.REQUIRE_2FA)
|
||||
@_require_login_state(LoginState.REQUIRE_2FA)
|
||||
@override
|
||||
async def get_2fa_methods(self) -> Sequence[AsyncSecondFactorMethod]:
|
||||
"""See :meth:`BaseAppleAccount.get_2fa_methods`."""
|
||||
@@ -537,7 +537,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
|
||||
return methods
|
||||
|
||||
@require_login_state(LoginState.REQUIRE_2FA)
|
||||
@_require_login_state(LoginState.REQUIRE_2FA)
|
||||
@override
|
||||
async def sms_2fa_request(self, phone_number_id: int) -> None:
|
||||
"""See :meth:`BaseAppleAccount.sms_2fa_request`."""
|
||||
@@ -549,7 +549,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
data,
|
||||
)
|
||||
|
||||
@require_login_state(LoginState.REQUIRE_2FA)
|
||||
@_require_login_state(LoginState.REQUIRE_2FA)
|
||||
@override
|
||||
async def sms_2fa_submit(self, phone_number_id: int, code: str) -> LoginState:
|
||||
"""See :meth:`BaseAppleAccount.sms_2fa_submit`."""
|
||||
@@ -574,7 +574,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
# AUTHENTICATED -> LOGGED_IN
|
||||
return await self._login_mobileme()
|
||||
|
||||
@require_login_state(LoginState.REQUIRE_2FA)
|
||||
@_require_login_state(LoginState.REQUIRE_2FA)
|
||||
@override
|
||||
async def td_2fa_request(self) -> None:
|
||||
"""See :meth:`BaseAppleAccount.td_2fa_request`."""
|
||||
@@ -588,7 +588,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
headers=headers,
|
||||
)
|
||||
|
||||
@require_login_state(LoginState.REQUIRE_2FA)
|
||||
@_require_login_state(LoginState.REQUIRE_2FA)
|
||||
@override
|
||||
async def td_2fa_submit(self, code: str) -> LoginState:
|
||||
"""See :meth:`BaseAppleAccount.td_2fa_submit`."""
|
||||
@@ -612,7 +612,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
# AUTHENTICATED -> LOGGED_IN
|
||||
return await self._login_mobileme()
|
||||
|
||||
@require_login_state(LoginState.LOGGED_IN)
|
||||
@_require_login_state(LoginState.LOGGED_IN)
|
||||
async def fetch_raw_reports(
|
||||
self,
|
||||
devices: list[tuple[list[str], list[str]]],
|
||||
@@ -740,7 +740,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
keys: Sequence[HasHashedPublicKey | RollingKeyPairSource],
|
||||
) -> dict[HasHashedPublicKey | RollingKeyPairSource, LocationReport | None]: ...
|
||||
|
||||
@require_login_state(LoginState.LOGGED_IN)
|
||||
@_require_login_state(LoginState.LOGGED_IN)
|
||||
@override
|
||||
async def fetch_location(
|
||||
self,
|
||||
@@ -759,7 +759,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
|
||||
return {dev: sorted(reports)[-1] if reports else None for dev, reports in hist.items()}
|
||||
|
||||
@require_login_state(LoginState.LOGGED_OUT, LoginState.REQUIRE_2FA, LoginState.LOGGED_IN)
|
||||
@_require_login_state(LoginState.LOGGED_OUT, LoginState.REQUIRE_2FA, LoginState.LOGGED_IN)
|
||||
async def _gsa_authenticate(
|
||||
self,
|
||||
username: str | None = None,
|
||||
@@ -856,7 +856,7 @@ class AsyncAppleAccount(BaseAppleAccount):
|
||||
msg = f"Unknown auth value: {au}"
|
||||
raise UnhandledProtocolError(msg)
|
||||
|
||||
@require_login_state(LoginState.AUTHENTICATED)
|
||||
@_require_login_state(LoginState.AUTHENTICATED)
|
||||
async def _login_mobileme(self) -> LoginState:
|
||||
logger.info("Logging into com.apple.mobileme")
|
||||
data = plistlib.dumps(
|
||||
|
||||
@@ -17,7 +17,7 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from typing_extensions import override
|
||||
|
||||
from findmy.accessory import RollingKeyPairSource
|
||||
from findmy.keys import HasHashedPublicKey, KeyPair, KeyPairMapping, KeyType
|
||||
from findmy.keys import HasHashedPublicKey, KeyPair, KeyPairMapping, KeyPairType
|
||||
from findmy.util.abc import Serializable
|
||||
from findmy.util.files import read_data_json, save_and_return_json
|
||||
|
||||
@@ -463,10 +463,10 @@ class LocationReportsFetcher:
|
||||
# split into primary and secondary keys
|
||||
# (UNKNOWN keys are filed as primary)
|
||||
new_keys_primary: set[str] = {
|
||||
key.hashed_adv_key_b64 for key in key_batch if key.key_type == KeyType.PRIMARY
|
||||
key.hashed_adv_key_b64 for key in key_batch if key.key_type == KeyPairType.PRIMARY
|
||||
}
|
||||
new_keys_secondary: set[str] = {
|
||||
key.hashed_adv_key_b64 for key in key_batch if key.key_type != KeyType.PRIMARY
|
||||
key.hashed_adv_key_b64 for key in key_batch if key.key_type != KeyPairType.PRIMARY
|
||||
}
|
||||
|
||||
# 290 seems to be the maximum number of keys that Apple accepts in a single request,
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
from .scanner import (
|
||||
NearbyOfflineFindingDevice,
|
||||
OfflineFindingDevice,
|
||||
OfflineFindingScanner,
|
||||
SeparatedOfflineFindingDevice,
|
||||
)
|
||||
|
||||
__all__ = (
|
||||
"NearbyOfflineFindingDevice",
|
||||
"OfflineFindingDevice",
|
||||
"OfflineFindingScanner",
|
||||
"SeparatedOfflineFindingDevice",
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user