Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ chaquopy {
// wheel for desktop platforms and a pure-Python `py3-none-any` one as well.
// There is no Android wheel, so pip falls back to the pure-Python build - which
// is correct but markedly slower. The messages here are small enough not to care.
install("git+https://github.com/parawanderer/FindMy.py@7969003ca785658369b650f75d9e7ca519566bf6")
install("git+https://github.com/parawanderer/FindMy.py@00fd962a0e8ec9f5601fe0b9db3f16ccea252a3c")

install("NSKeyedUnArchiver==1.5")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -169,6 +170,29 @@ public void theRetryGoesThroughTheServerAndIsRecordedAsRemote() {
UserSettings.ANISETTE_REMOTE, storedSettings().getAnisetteMode());
}

/**
* Issue #248: refused before the password was used, on a device whose local Anisette works.
*
* <p>The button appears, and the sentence says the password is not the problem while keeping
* Apple's text, which carries the {@code ec} a report needs.
*/
@Test
public void aRefusalBeforeThePasswordOffersTheRemoteServer() {
AppDependencies.replaceAuthService(
this.apple = FakeAppleAuthService.refusesBeforeThePasswordOnceThenSignsIn());

launch();
signInExpectingAnotherLoginCall();

Eventually.check(() -> onView(withId(R.id.login_error_try_remote_anisette))
.perform(scrollTo()).check(matches(isDisplayed())));

final String expected = getInstrumentation().getTargetContext().getString(
R.string.login_failed_refused_before_password,
"Email verification failed (ec=-1): This action cannot be completed at this time.");
onView(withId(R.id.login_error_message_text)).check(matches(withText(expected)));
}

/**
* A wrong password does not offer it: a different Anisette identity would not fix a bad
* credential, and saying otherwise sends people to change a setting that cannot help.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,21 @@ public static FakeAppleAuthService declinesOnceThenSignsIn() {
return fake;
}

/**
* Apple refuses the first step of the sign-in, before the password is used, then serves the
* next - issue #248's shape, followed by the retry through a server getting past it.
*
* <p>Fails as {@code REFUSED_BEFORE_PASSWORD}, which the remote-server button is also gated on.
*/
public static FakeAppleAuthService refusesBeforeThePasswordOnceThenSignsIn() {
final FakeAppleAuthService fake = new FakeAppleAuthService(LOGIN_STATE.LOGGED_IN, null);
fake.loginFailsWith = new PythonAccountLoginException(
"Email verification failed (ec=-1): This action cannot be completed at this time.",
PythonAccountLoginException.REASON_REFUSED_BEFORE_PASSWORD);
fake.loginFailuresRemaining = 1;
return fake;
}

/** The password is wrong, or Apple is unreachable. */
public static FakeAppleAuthService rejectsTheSignIn(final String message) {
final FakeAppleAuthService fake =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -747,15 +747,18 @@ private void showLoginFailure(final Throwable error) {
((TextView) this.findViewById(R.id.login_error_message_text))
.setText(this.describeLoginFailure(error));

// Offer the server route only when Apple itself refused the sign-in - a 429 or 503 -
// and not for a wrong password or a dead network, where a different Anisette identity
// changes nothing. That is the one failure where local Anisette worked and Apple still
// said no, so switching the machine identity a server presents is the thing left to try.
// Offer the server route only when Apple itself refused the sign-in - a 429 or 503, or
// a refusal before the password was used (#248) - and not for a wrong password or a dead
// network, where a different Anisette identity changes nothing. Those are the failures
// where local Anisette worked and Apple still said no, so switching the machine identity
// a server presents is the thing left to try.
final String reason = error instanceof PythonAccountLoginException
? ((PythonAccountLoginException) error).getReason()
: PythonAccountLoginException.REASON_UNKNOWN;
final boolean appleRefused = PythonAccountLoginException.REASON_APPLE_DECLINED.equals(reason)
|| PythonAccountLoginException.REASON_REFUSED_BEFORE_PASSWORD.equals(reason);
this.findViewById(R.id.login_error_try_remote_anisette).setVisibility(
PythonAccountLoginException.REASON_APPLE_DECLINED.equals(reason) ? VISIBLE : GONE);
appleRefused ? VISIBLE : GONE);
}

/**
Expand Down Expand Up @@ -825,6 +828,13 @@ private String describeLoginFailure(final Throwable error) {
return this.getString(R.string.login_failed_apple_declined);
}

// Refused before the password was used (#248), so the sentence says that retyping it
// will not help. Apple's own text follows, because it carries the ec a report needs.
if (PythonAccountLoginException.REASON_REFUSED_BEFORE_PASSWORD.equals(reason)) {
return this.getString(R.string.login_failed_refused_before_password,
error.getLocalizedMessage());
}

// Apple took the password and the code, then would not open iCloud for the account. Kept
// apart from the terms sentence because the remedy is elsewhere entirely, and apart from
// "Apple declined" because that one says to wait - see REASON_ICLOUD_REFUSED.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ public class PythonAccountLoginException extends RuntimeException {
*/
public static final String REASON_APPLE_DECLINED = "apple_declined";

/**
* Apple refused the first step of the sign-in, before the password was used. Matches
* {@code REASON_REFUSED_BEFORE_PASSWORD}.
*
* <p>Issue #248: shown verbatim as "Email verification failed", which reads as a typo in the
* Apple ID or password and is neither. Like {@link #REASON_APPLE_DECLINED} it offers the
* remote-server route, because local Anisette worked and Apple still said no - so the machine
* identity is the thing left to change. Unlike it, nothing here says to wait.
*/
public static final String REASON_REFUSED_BEFORE_PASSWORD = "refused_before_password";

/** Anything not recognised. The detail is shown as-is rather than guessed at. */
public static final String REASON_UNKNOWN = "unknown";

Expand Down
26 changes: 25 additions & 1 deletion app/src/main/python/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import NSKeyedUnArchiver

from findmy import FindMyAccessory, MobileMeDelegateError
from findmy.errors import AppleServiceUnavailableError
from findmy.errors import AppleServiceUnavailableError, GrandSlamRefusedError
from findmy.accessory import FixedRollingKeyPairAccessory
from findmy.keys import KeyPairType
from findmy.reports import (
Expand Down Expand Up @@ -511,6 +511,25 @@ def assertAnisetteIsSupported(serializedAccountData: str) -> str | None:
end is wasted effort.
"""

REASON_REFUSED_BEFORE_PASSWORD = "refused_before_password"
"""
Grand Slam refused the first step of the sign-in, which carries the username and the Anisette
headers and no password.
**So the password is not the problem, and the screen must not suggest it is.** This arrived as
an `InvalidCredentialsError` reading "Email verification failed", shown verbatim, on a device
whose local Anisette was working - OpenTagViewer#248. What is left is the account itself, or the
machine identity the Anisette data presents, and the second is the one thing this app can change:
the screen offers the remote-server route for it, as it does for :data:`REASON_APPLE_DECLINED`.
**Which `ec` means what is not established.** Apple's text here is generic, and the code was not
kept until the fork started carrying it, so nothing branches on a value. The detail keeps the
code, so a report carries it.
**A wrong password is not this.** That is refused at the second step, `complete`, and stays
:data:`REASON_UNKNOWN` - a different Anisette identity cannot fix a bad credential.
"""

REASON_UNKNOWN = "unknown"
"""Anything else. The detail is shown as-is, because a wrong guess is worse than raw text."""

Expand Down Expand Up @@ -557,6 +576,11 @@ def classifyLoginFailure(error: BaseException) -> str:
if isinstance(error, AppleServiceUnavailableError):
return REASON_APPLE_DECLINED

# Only the first step. The same type reports a wrong password at the second, and that one
# must not offer a different Anisette server as though it could help.
if isinstance(error, GrandSlamRefusedError) and error.before_password:
return REASON_REFUSED_BEFORE_PASSWORD

if isinstance(error, (asyncio.TimeoutError, asyncio.CancelledError)):
return REASON_NETWORK
if isinstance(error, _NETWORK_ERRORS):
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-de/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@ Wenn Apple die Anfrage lediglich abgelehnt hat, lohnt es sich, zu warten und es
<string name="anisette_login_crashes_here">Apples Anmeldebibliothek stürzt auf diesem Handy ab, daher übernimmt ein Server diesen Schritt. Alles andere funktioniert normal. Warum das auf manchen Handys passiert, wissen wir noch nicht – ein Bericht mit deinem Modell hilft.</string>
<string name="anisette_report_crash">Dieses Handy melden</string>
<string name="anisette_crash_report_body">Apples Anmeldebibliothek stürzt auf diesem Handy ab, sobald sie geladen wird. Auf den meisten Handys funktioniert sie, und wir wissen noch nicht, was die betroffenen gemeinsam haben.\n\nOpenTagViewer nutzt für diesen Schritt jetzt einen Server, du kannst dich also anmelden und die App normal verwenden. Ein Bericht mit den Angaben unten hilft uns, die Ursache zu finden.</string>
<string name="login_failed_refused_before_password">Apple hat die Anmeldung mit dieser Apple-ID abgelehnt, bevor dein Passwort geprüft wurde. Es erneut einzugeben hilft also nicht. Prüfe, ob du dich auf account.apple.com anmelden kannst und ob Apple dort etwas von dir verlangt. Ein Remote-Server stellt sich Apple als anderes Gerät vor, einen Versuch ist das wert. Apple meldete: %1$s</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-en/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@ If Apple simply refused the request, waiting and trying again is worth doing fir
<string name="anisette_login_crashes_here">Apple\'s sign-in library crashes on this phone, so a server does this step instead. Everything else works normally. We don\'t know why it happens on some phones yet, and a report naming yours helps.</string>
<string name="anisette_report_crash">Report this phone</string>
<string name="anisette_crash_report_body">Apple\'s sign-in library crashes as soon as it loads on this phone. It works on most phones, and we do not know yet what the affected ones have in common.\n\nOpenTagViewer now uses a server for that step, so you can sign in and use the app normally. A report with the details below is what helps us find the cause.</string>
<string name="login_failed_refused_before_password">Apple refused to start signing in to this Apple ID before your password was checked, so retyping it will not help. Check that you can sign in at account.apple.com and whether Apple asks anything of you there. Trying a remote server presents Apple with a different device, which is worth a try. Apple said: %1$s</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@ Si Apple a simplement refusé la requête, il vaut mieux attendre et réessayer.
<string name="anisette_login_crashes_here">La bibliothèque de connexion d\'Apple plante sur ce téléphone, un serveur se charge donc de cette étape. Tout le reste fonctionne normalement. Nous ne savons pas encore pourquoi cela arrive sur certains téléphones : un rapport indiquant le vôtre nous aide.</string>
<string name="anisette_report_crash">Signaler ce téléphone</string>
<string name="anisette_crash_report_body">La bibliothèque de connexion d\'Apple plante dès son chargement sur ce téléphone. Elle fonctionne sur la plupart des téléphones, et nous ne savons pas encore ce que les appareils concernés ont en commun.\n\nOpenTagViewer utilise désormais un serveur pour cette étape : vous pouvez vous connecter et utiliser l\'app normalement. Un rapport avec les informations ci-dessous nous aide à trouver la cause.</string>
<string name="login_failed_refused_before_password">Apple a refusé de commencer la connexion à cet identifiant Apple avant même de vérifier votre mot de passe : le saisir à nouveau ne changera rien. Vérifiez que vous pouvez vous connecter sur account.apple.com et si Apple vous y demande quelque chose. Un serveur distant présente un autre appareil à Apple, cela vaut la peine d\'essayer. Apple a répondu : %1$s</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@ Appleが要求を拒否しただけなら、少し待ってからもう一度お
<string name="anisette_login_crashes_here">Apple のサインイン用ライブラリがこの端末ではクラッシュするため、この手順はサーバーが代わりに行います。その他の機能は通常どおり使えます。一部の端末で起きる原因はまだ分かっていません。お使いの機種を添えて報告していただけると助かります。</string>
<string name="anisette_report_crash">この端末を報告</string>
<string name="anisette_crash_report_body">この端末では、Apple のサインイン用ライブラリが読み込み直後にクラッシュします。ほとんどの端末では問題なく動作しており、影響を受ける端末の共通点はまだ分かっていません。\n\nOpenTagViewer はこの手順にサーバーを使うようになったため、サインインもアプリの利用も通常どおりできます。下の情報を添えて報告していただくと、原因の特定に役立ちます。</string>
<string name="login_failed_refused_before_password">Apple はパスワードを確認する前に、この Apple ID へのサインインの開始を拒否しました。パスワードを入力し直しても解決しません。account.apple.com にサインインできるか、そこで Apple から何か求められていないかを確認してください。リモートサーバーを使うと Apple には別のデバイスとして見えるので、試す価値があります。Apple の応答: %1$s</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-ko/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@ Apple이 요청을 거부한 것뿐이라면 잠시 기다렸다가 다시 시
<string name="anisette_login_crashes_here">Apple의 로그인 라이브러리가 이 휴대폰에서 충돌하므로 이 단계는 서버가 대신 처리합니다. 나머지는 모두 정상적으로 작동합니다. 일부 휴대폰에서 왜 이런 일이 생기는지 아직 모릅니다. 기기 모델을 알려 주시면 도움이 됩니다.</string>
<string name="anisette_report_crash">이 휴대폰 신고하기</string>
<string name="anisette_crash_report_body">이 휴대폰에서는 Apple의 로그인 라이브러리가 불러오자마자 충돌합니다. 대부분의 휴대폰에서는 정상 작동하며, 영향을 받는 기기들의 공통점은 아직 모릅니다.\n\n이제 OpenTagViewer가 이 단계에 서버를 사용하므로 로그인하고 앱을 정상적으로 쓸 수 있습니다. 아래 정보와 함께 신고해 주시면 원인을 찾는 데 도움이 됩니다.</string>
<string name="login_failed_refused_before_password">Apple이 비밀번호를 확인하기 전에 이 Apple ID의 로그인 시작을 거부했습니다. 비밀번호를 다시 입력해도 해결되지 않습니다. account.apple.com에 로그인할 수 있는지, 그리고 Apple이 그곳에서 무언가를 요구하는지 확인하세요. 원격 서버를 사용하면 Apple에 다른 기기로 보이므로 시도해 볼 만합니다. Apple 응답: %1$s</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-nl/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@ Als Apple het verzoek simpelweg weigerde, is even wachten en opnieuw proberen he
<string name="anisette_login_crashes_here">Apples aanmeldbibliotheek crasht op deze telefoon, dus doet een server deze stap. Al het andere werkt gewoon. We weten nog niet waarom dit op sommige telefoons gebeurt; een melding met jouw model helpt.</string>
<string name="anisette_report_crash">Deze telefoon melden</string>
<string name="anisette_crash_report_body">Apples aanmeldbibliotheek crasht op deze telefoon zodra ze geladen wordt. Op de meeste telefoons werkt ze, en we weten nog niet wat de getroffen toestellen gemeen hebben.\n\nOpenTagViewer gebruikt voor die stap nu een server, dus je kunt gewoon aanmelden en de app normaal gebruiken. Een melding met de gegevens hieronder helpt ons de oorzaak te vinden.</string>
<string name="login_failed_refused_before_password">Apple weigerde het inloggen met deze Apple ID te starten voordat je wachtwoord werd gecontroleerd, dus opnieuw intypen helpt niet. Controleer of je kunt inloggen op account.apple.com en of Apple daar iets van je vraagt. Een externe server stelt zich bij Apple voor als een ander apparaat, dat is het proberen waard. Apple meldde: %1$s</string>
</resources>
1 change: 1 addition & 0 deletions app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -393,4 +393,5 @@
<string name="anisette_login_crashes_here">Библиотека входа Apple падает на этом телефоне, поэтому этот шаг выполняет сервер. Всё остальное работает как обычно. Мы пока не знаем, почему это происходит на некоторых телефонах, — отчёт с моделью вашего телефона поможет.</string>
<string name="anisette_report_crash">Сообщить об этом телефоне</string>
<string name="anisette_crash_report_body">На этом телефоне библиотека входа Apple падает сразу после загрузки. На большинстве телефонов она работает, и мы пока не знаем, что общего у затронутых устройств.\n\nТеперь OpenTagViewer использует для этого шага сервер, так что вы можете войти и пользоваться приложением как обычно. Отчёт с данными ниже поможет нам найти причину.</string>
<string name="login_failed_refused_before_password">Apple отказалась начинать вход с этим Apple ID ещё до проверки пароля, поэтому вводить его заново бесполезно. Проверьте, можете ли вы войти на account.apple.com и не требует ли Apple там чего-либо. Удалённый сервер представляется Apple другим устройством — это стоит попробовать. Ответ Apple: %1$s</string>
</resources>
Loading
Loading