Class TokenStore
Package-private, with a package-private constructor -- a CloudSession is the only thing
in this codebase that constructs one (D-18). This is deliberately a thin wrapper, not a second
implementation of file I/O: every operation below delegates to CredentialStore, this
package's actual single owner of credentials.json on disk (its own structural test,
CredentialStoreTest#onlyCredentialStoreTouchesTheCredentialFileDirectly, asserts that no
class other than CredentialStore opens a reader or writer on that file -- delegating
rather than reimplementing keeps this class outside that scan, and correctly so: this class owns
who may call the write, not how the write happens).
- Since:
- 6.3.0
-
Field Summary
Fields -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescription(package private) voidclearIfMatches(TokenEntity expected) Removes the persistedcloud_tokenentry, but ONLY if it still matchesexpected-- i.e.private static booleanmatchesAccessToken(Object savedRaw, TokenEntity expected) (package private) TokenEntityparseToken(Map<String, Object> data) Parses thecloud_tokenentry out of an already-read document, ornullif absent, unparseable, or missing an access token.(package private) CredentialStore.ReadResultreadRaw()Reads the raw credential document.(package private) voidsave(TokenEntity token) Persiststoken, replacing whatevercloud_tokenentry currently exists in the credential document.
-
Field Details
-
GSON
private static final com.google.gson.Gson GSON
-
-
Constructor Details
-
TokenStore
TokenStore()
-
-
Method Details
-
save
Persiststoken, replacing whatevercloud_tokenentry currently exists in the credential document. Every other field in the document (server UUID, etc.) is left untouched --CredentialStore.update(java.util.function.UnaryOperator<java.util.Map<java.lang.String, java.lang.Object>>)reads the existing document first.- Parameters:
token- the token to persist- Throws:
IOException- if the underlying write fails
-
clearIfMatches
Removes the persistedcloud_tokenentry, but ONLY if it still matchesexpected-- i.e. nothing else has overwritten it sinceexpectedwas this session's own last-known token.Round-1 external review finding (16-10, PR #464): a plain, unconditional clear here is a compare-and-delete's absence, not its presence.
credentials.jsonis one shared document across every session that has ever existed (D-16 gives each session its own lifecycle, not its own file). If a stale session's logout races a fresh login whose commit lands on the shared document first, an unconditional clear removes the FRESH credential -- the exact defect this method exists to prevent. Comparing againstexpected(the calling session's own token, captured before teardown) means a clear only ever removes what that specific session itself is the one that wrote — anything written afterward, by anyone else, survives.- Parameters:
expected- the token this session itself believes is (or was) persisted; if the disk's currentcloud_tokendoes not match it (by access token value), nothing is removed- Throws:
IOException- if the underlying write fails
-
matchesAccessToken
- Parameters:
savedRaw- the rawcloud_tokenvalue read from the credential document (aMaponce Gson has deserialized it, ornull/anything else if absent or malformed)expected- the token to compare against- Returns:
trueifsavedRawis a map whoseaccess_tokenentry equalsexpected's access token
-
readRaw
CredentialStore.ReadResult readRaw()Reads the raw credential document. Exposed rather than a parsedTokenEntitybecause the caller (CloudSession.loadFromDisk()) needs to distinguish "absent" from "torn" and log accordingly -- collapsing that here would lose the distinctionCredentialStore.ReadResultexists to preserve.- Returns:
- the raw read result
-
parseToken
Parses thecloud_tokenentry out of an already-read document, ornullif absent, unparseable, or missing an access token.- Parameters:
data- a parsed credential document, e.g. fromreadRaw()- Returns:
- the parsed token, or
null
-