Class CloudAuthManager
/ulticloud login|logout|status.
As of plan 16-09 (D-17) this class owns no state of its own and exposes no generation-shaped or
TokenEntity-shaped public surface -- everything that used to be a fine-grained public
static (the generation triple, saveToken/clearToken/refreshToken/
loadSavedToken, the start/stop pairs for polling and refresh) is gone outright, not
deprecated: CloudSession instance methods now carry that behaviour, reached only through
login(Runnable, Consumer, Runnable, Consumer, Consumer), logout() and
status() below. Keeping a delegating shim for any of them would leave the generation
convention callable, which is exactly what issue #298's second acceptance criterion requires to
stop being true (see plan 16-08's CloudSession javadoc for the mechanism this replaced).
CredentialStaticSurfaceInvariantTest enforces this structurally: no public static method
anywhere in this package may accept or return a TokenEntity or a generation (D-18), so a
static bypass of CloudSession.commit(TokenEntity) cannot be reintroduced unnoticed.
Marked ApiStatus.Internal -- this class exists to serve CloudLoginCommand's three
@CmdMapping methods and is not part of the framework's public API surface for module
authors.
-
Nested Class Summary
Nested Classes -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic voidlogin(Runnable onAlreadyLoggedIn, Consumer<Long> onRateLimited, Runnable onRequesting, Consumer<String> onSuccess, Consumer<String> onError) Attempts a magic-link login on a fresh session, covering the already-logged-in check, the rate-limit check, the magic-link request and starting the poll -- exactly the sequence/ulticloud loginperformed before this plan, just no longer expressed through separate public statics on this class.static booleanlogout()Tears down cloud features unconditionally, then clears the persisted credential if one existed.static CloudAuthManager.CloudStatusstatus()
-
Constructor Details
-
CloudAuthManager
private CloudAuthManager()
-
-
Method Details
-
login
public static void login(Runnable onAlreadyLoggedIn, Consumer<Long> onRateLimited, Runnable onRequesting, Consumer<String> onSuccess, Consumer<String> onError) Attempts a magic-link login on a fresh session, covering the already-logged-in check, the rate-limit check, the magic-link request and starting the poll -- exactly the sequence/ulticloud loginperformed before this plan, just no longer expressed through separate public statics on this class. Every branch is reported back through the callback that names it, soCloudLoginCommand.login(CommandSender)prints the exact same messages it always has; only where the decision is made moved.- Parameters:
onAlreadyLoggedIn- invoked, with nothing else attempted, if a valid token already existsonRateLimited- invoked with the remaining cooldown in seconds if the login attempt is currently rate-limitedonRequesting- invoked right before the magic-link HTTP request is madeonSuccess- invoked with the magic-link URL once the request succeedsonError- invoked with an error message if the magic-link request fails
-
logout
Tears down cloud features unconditionally, then clears the persisted credential if one existed. Mirrors/ulticloud logout's pre-16-09 sequence exactly: teardown must run even when there is nothing to clear (an expired-but-still-connected session is the case that most needs logout to take effect), and the credential is read only after teardown so an in-flight login that commits mid-teardown is still caught and cleared -- seeCloudLoginCommand.logout(CommandSender)'s own comments for why that order matters.CR-01 (16-REVIEW-cloud.md): this method captures
CloudSession.current()exactly once -- in the statement below, immediately before teardown begins -- and passes that captured reference toPluginInitiationUtils.disableCloud(CloudSession), which acts on and returns that exact instance. The disk-clear decision then acts on the returned reference, never on a second, independent call toCloudSession.current()taken after teardown returns. Readingcurrent()a second time, afterward, used to be exactly the bug: a concurrentlogin()(unsynchronized, and reachable from a different thread via@RunAsync) can install a brand-new session withCloudSession.startNew()in the gap while teardown is running, and that second read would then see the NEW session's (alwaysnull) token instead of the one actually being logged out of -- concluding "nothing to clear" and leaving the real credential on disk. Acting on the one reference this method already holds makes that race structurally impossible: there is no second read left to disagree with the first. This method deliberately does not callCloudSession.startNew()itself any more either; clearing the torn-down session in place is sufficient, and the next real login installs a fresh session via its ownCloudSession.startNew()call, exactly as it always has.Round-1 external review finding, corrected in the same plan: the paragraph above establishes WHICH session's disk-clear decision this method acts on, but not what that clear is safe to remove.
CloudSession.clearPersisted()does NOT wipe disk unconditionally -- it delegates toTokenStore.clearIfMatches(TokenEntity), a compare-and-delete keyed on the torn-down session's own last-known token. This matters because a concurrentlogin()can still WRITE a fresh credential to the same shared document while this session's teardown is in flight, even though its own session-invalidation race is already closed by the paragraph above -- seeCloudSession.clearPersisted()'s own javadoc for the full account of why an unconditional clear would remove that fresh write.Documented semantics for logout racing a concurrent login (CR-01's second half): this method's teardown always acts on whichever session was current at the single instant this method's own
CloudSession.current()call below reads it -- a single, fixed point in time this method controls directly, rather than one buried inside a callee. Alogin()that installs its new session before that read wins outright: thislogout()call never sees or touches it. Alogin()that installs its new session after that read has already lost the SESSION-invalidation race regardless of what this method does --CloudSession.startNew()itself unconditionally invalidates whatever session it replaces, so the fresh login's session object is torn down by that call alone, independent of this command. WhatCloudSession.clearPersisted()'s compare-and-delete adds on top is the disk-level half of that same guarantee: even if the fresh login's own commit reaches the shared document before this method's clear does, the clear cannot remove a value the torn-down session never itself wrote.- Returns:
trueif a credential existed and was cleared, or a pending authentication attempt was cancelled (real-machine UAT fix, plan 16-08 -- seeCloudSession.hasAnythingToClear());falseif there was nothing to clear and nothing pending (teardown still ran regardless)- Throws:
IOException- if clearing the persisted credential fails
-
status
- Returns:
- an immutable snapshot of the current session's connection state, for
/ulticloud statusto render -- never the token itself (D-18)
-