Skip to content
This repository has been archived by the owner on Nov 1, 2022. It is now read-only.

Commit

Permalink
pass
Browse files Browse the repository at this point in the history
  • Loading branch information
Grisha Kruglov committed Jul 29, 2020
1 parent 4fec235 commit 6d6d7d4
Show file tree
Hide file tree
Showing 38 changed files with 3,139 additions and 3,351 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ typealias OuterDeviceCommandIncoming = DeviceCommandIncoming
*/
sealed class AccountEvent {
/** An incoming command from another device */
class DeviceCommandIncoming(val command: OuterDeviceCommandIncoming) : AccountEvent()
data class DeviceCommandIncoming(val command: OuterDeviceCommandIncoming) : AccountEvent()
/** The account's profile was updated */
class ProfileUpdated : AccountEvent()
object ProfileUpdated : AccountEvent()
/** The authentication state of the account changed - eg, the password changed */
class AccountAuthStateChanged : AccountEvent()
object AccountAuthStateChanged : AccountEvent()
/** The account itself was destroyed */
class AccountDestroyed : AccountEvent()
object AccountDestroyed : AccountEvent()
/** Another device connected to the account */
class DeviceConnected(val deviceName: String) : AccountEvent()
data class DeviceConnected(val deviceName: String) : AccountEvent()
/** A device (possibly this one) disconnected from the account */
class DeviceDisconnected(val deviceId: String, val isLocalDevice: Boolean) : AccountEvent()
data class DeviceDisconnected(val deviceId: String, val isLocalDevice: Boolean) : AccountEvent()
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,12 @@ import mozilla.components.support.base.observer.Observable
*/
interface DeviceConstellation : Observable<AccountEventsObserver> {
/**
* Register current device in the associated [DeviceConstellation].
*
* @param name An initial name for the current device. This may be changed via [setDeviceNameAsync].
* @param type Type of the current device. This can't be changed.
* @param capabilities A list of capabilities that the current device claims to have.
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun initDeviceAsync(
name: String,
type: DeviceType = DeviceType.MOBILE,
capabilities: Set<DeviceCapability>
): Deferred<Boolean>

/**
* Ensure that all passed in [capabilities] are configured.
* This may involve backend service registration, or other work involving network/disc access.
* @param capabilities A list of capabilities to configure. This is expected to be the same or
* longer list than what was passed into [initDeviceAsync]. Removing capabilities is currently
* not supported.
* Perform actions necessary to finalize device initialization based on [authType].
* @param authType Type of an authentication event we're experiencing.
* @param config A [DeviceConfig] that describes current device.
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun ensureCapabilitiesAsync(capabilities: Set<DeviceCapability>): Deferred<Boolean>
suspend fun finalizeDevice(authType: AuthType, config: DeviceConfig): Boolean

/**
* Current state of the constellation. May be missing if state was never queried.
Expand All @@ -55,44 +39,44 @@ interface DeviceConstellation : Observable<AccountEventsObserver> {
* @param context An application context, used for updating internal caches.
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun setDeviceNameAsync(name: String, context: Context): Deferred<Boolean>
suspend fun setDeviceName(name: String, context: Context): Boolean

/**
* Set a [DevicePushSubscription] for the current device.
* @param subscription A new [DevicePushSubscription].
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun setDevicePushSubscriptionAsync(subscription: DevicePushSubscription): Deferred<Boolean>
suspend fun setDevicePushSubscription(subscription: DevicePushSubscription): Boolean

/**
* Send a command to a specified device.
* @param targetDeviceId A device ID of the recipient.
* @param outgoingCommand An event to send.
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun sendCommandToDeviceAsync(targetDeviceId: String, outgoingCommand: DeviceCommandOutgoing): Deferred<Boolean>
suspend fun sendCommandToDevice(targetDeviceId: String, outgoingCommand: DeviceCommandOutgoing): Boolean

/**
* Process a raw event, obtained via a push message or some other out-of-band mechanism.
* @param payload A raw, plaintext payload to be processed.
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun processRawEventAsync(payload: String): Deferred<Boolean>
suspend fun processRawEvent(payload: String): Boolean

/**
* Refreshes [ConstellationState]. Registered [DeviceConstellationObserver] observers will be notified.
*
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun refreshDevicesAsync(): Deferred<Boolean>
suspend fun refreshDevices(): Boolean

/**
* Polls for any pending [DeviceCommandIncoming] commands.
* In case of new commands, registered [AccountEventsObserver] observers will be notified.
*
* @return A [Deferred] that will be resolved with a success flag once operation is complete.
*/
fun pollForCommandsAsync(): Deferred<Boolean>
suspend fun pollForCommands(): Boolean
}

/**
Expand Down Expand Up @@ -128,6 +112,24 @@ data class DevicePushSubscription(
val authKey: String
)

/**
* Configuration for the current device.
*
* @property name An initial name to use for the device record which will be created during authentication.
* This can be changed later via [DeviceConstellation.setDeviceName].
* @property type Type of a device - mobile, desktop - used for displaying identifying icons on other devices.
* This cannot be changed once device record is created.
* @property capabilities A set of device capabilities, such as SEND_TAB.
* @property secureStateAtRest A flag indicating whether or not to use encrypted storage for the persisted account
* state.
*/
data class DeviceConfig(
val name: String,
val type: DeviceType,
val capabilities: Set<DeviceCapability>,
val secureStateAtRest: Boolean = false
)

/**
* Capabilities that a [Device] may have.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,6 @@ package mozilla.components.concept.sync
import kotlinx.coroutines.Deferred
import org.json.JSONObject

/**
* An auth-related exception type, for use with [AuthException].
*
* @property msg string value of the auth exception type
*/
enum class AuthExceptionType(val msg: String) {
KEY_INFO("Missing key info"),
NO_TOKEN("Missing access token"),
UNAUTHORIZED("Unauthorized")
}

/**
* The access-type determines whether the code can be exchanged for a refresh token for
* offline use or not.
Expand All @@ -29,22 +18,17 @@ enum class AccessType(val msg: String) {
OFFLINE("offline")
}

/**
* An exception which may happen while obtaining auth information using [OAuthAccount].
*/
class AuthException(type: AuthExceptionType, cause: Exception? = null) : Throwable(type.msg, cause)

/**
* An object that represents a login flow initiated by [OAuthAccount].
* @property state OAuth state parameter, identifying a specific authentication flow.
* This string is randomly generated during [OAuthAccount.beginOAuthFlowAsync] and [OAuthAccount.beginPairingFlowAsync].
* This string is randomly generated during [OAuthAccount.beginOAuthFlow] and [OAuthAccount.beginPairingFlow].
* @property url Url which needs to be loaded to go through the authentication flow identified by [state].
*/
data class AuthFlowUrl(val state: String, val url: String)

/**
* Represents a specific type of an "in-flight" migration state that could result from intermittent
* issues during [OAuthAccount.migrateFromSessionTokenAsync] or [OAuthAccount.copyFromSessionTokenAsync].
* issues during [OAuthAccount.migrateFromAccount].
*/
enum class InFlightMigrationState {
/**
Expand All @@ -53,16 +37,25 @@ enum class InFlightMigrationState {
NONE,

/**
* "Copy" in-flight migration present. Can retry migration via [OAuthAccount.retryMigrateFromSessionTokenAsync].
* "Copy" in-flight migration present. Can retry migration via [OAuthAccount.retryMigrateFromSessionToken].
*/
COPY_SESSION_TOKEN,

/**
* "Reuse" in-flight migration present. Can retry migration via [OAuthAccount.retryMigrateFromSessionTokenAsync].
* "Reuse" in-flight migration present. Can retry migration via [OAuthAccount.retryMigrateFromSessionToken].
*/
REUSE_SESSION_TOKEN
}

/**
* Data structure describing FxA and Sync credentials necessary to sign-in into an FxA account.
*/
data class MigratingAccountInfo(
val sessionToken: String,
val kSync: String,
val kXCS: String
)

/**
* Facilitates testing consumers of FirefoxAccount.
*/
Expand All @@ -75,7 +68,7 @@ interface OAuthAccount : AutoCloseable {
* @param scopes List of OAuth scopes for which the client wants access
* @return Deferred AuthFlowUrl that resolves to the flow URL when complete
*/
fun beginOAuthFlowAsync(scopes: Set<String>): Deferred<AuthFlowUrl?>
suspend fun beginOAuthFlow(scopes: Set<String>): AuthFlowUrl?

/**
* Constructs a URL used to begin the pairing flow for the requested scopes and pairingUrl.
Expand All @@ -84,7 +77,7 @@ interface OAuthAccount : AutoCloseable {
* @param scopes List of OAuth scopes for which the client wants access
* @return Deferred AuthFlowUrl Optional that resolves to the flow URL when complete
*/
fun beginPairingFlowAsync(pairingUrl: String, scopes: Set<String>): Deferred<AuthFlowUrl?>
suspend fun beginPairingFlow(pairingUrl: String, scopes: Set<String>): AuthFlowUrl?

/**
* Returns current FxA Device ID for an authenticated account.
Expand All @@ -110,12 +103,12 @@ interface OAuthAccount : AutoCloseable {
* the code can be exchanged for a refresh token to be used offline or not
* @return Deferred authorized auth code string, or `null` in case of failure.
*/
fun authorizeOAuthCodeAsync(
suspend fun authorizeOAuthCode(
clientId: String,
scopes: Array<String>,
state: String,
accessType: AccessType = AccessType.ONLINE
): Deferred<String?>
): String?

/**
* Fetches the profile object for the current client either from the existing cached state
Expand All @@ -124,18 +117,18 @@ interface OAuthAccount : AutoCloseable {
* @param ignoreCache Fetch the profile information directly from the server
* @return Profile (optional, if successfully retrieved) representing the user's basic profile info
*/
fun getProfileAsync(ignoreCache: Boolean = false): Deferred<Profile?>
suspend fun getProfile(ignoreCache: Boolean = false): Profile?

/**
* Authenticates the current account using the [code] and [state] parameters obtained via the
* OAuth flow initiated by [beginOAuthFlowAsync].
* OAuth flow initiated by [beginOAuthFlow].
*
* Modifies the FirefoxAccount state.
* @param code OAuth code string
* @param state state token string
* @return Deferred boolean representing success or failure
*/
fun completeOAuthFlowAsync(code: String, state: String): Deferred<Boolean>
suspend fun completeOAuthFlow(code: String, state: String): Boolean

/**
* Tries to fetch an access token for the given scope.
Expand All @@ -144,11 +137,11 @@ interface OAuthAccount : AutoCloseable {
* @return [AccessTokenInfo] that stores the token, along with its scope, key and
* expiration timestamp (in seconds) since epoch when complete
*/
fun getAccessTokenAsync(singleScope: String): Deferred<AccessTokenInfo?>
suspend fun getAccessToken(singleScope: String): AccessTokenInfo?

/**
* Call this whenever an authentication error was encountered while using an access token
* issued by [getAccessTokenAsync].
* issued by [getAccessToken].
*/
fun authErrorDetected()

Expand All @@ -163,7 +156,7 @@ interface OAuthAccount : AutoCloseable {
* @return An optional [Boolean] flag indicating if we're connected, or need to go through
* re-authentication. A null result means we were not able to determine state at this time.
*/
fun checkAuthorizationStatusAsync(singleScope: String): Deferred<Boolean?>
suspend fun checkAuthorizationStatus(singleScope: String): Boolean?

/**
* Fetches the token server endpoint, for authentication using the SAML bearer flow.
Expand All @@ -190,33 +183,18 @@ interface OAuthAccount : AutoCloseable {
* Attempts to migrate from an existing session token without user input.
* Passed-in session token will be reused.
*
* @param sessionToken token string to use for login
* @param kSync sync string for login
* @param kXCS XCS string for login
* @return JSON object with the result of the migration or 'null' if it failed.
* For up-to-date schema, see underlying implementation in https://github.com/mozilla/application-services/blob/v0.49.0/components/fxa-client/src/migrator.rs#L10
* At the moment, it's just "{total_duration: long}".
*/
fun migrateFromSessionTokenAsync(sessionToken: String, kSync: String, kXCS: String): Deferred<JSONObject?>

/**
* Attempts to migrate from an existing session token without user input.
* New session token will be created.
*
* @param sessionToken token string to use for login
* @param kSync sync string for login
* @param kXCS XCS string for login
* @param authInfo Auth info necessary for signing in
* @param reuseSessionToken Whether or not session token should be reused; reusing session token means that FxA device record will be inherited
* @return JSON object with the result of the migration or 'null' if it failed.
* For up-to-date schema, see underlying implementation in https://github.com/mozilla/application-services/blob/v0.49.0/components/fxa-client/src/migrator.rs#L10
* At the moment, it's just "{total_duration: long}".
*/
fun copyFromSessionTokenAsync(sessionToken: String, kSync: String, kXCS: String): Deferred<JSONObject?>
suspend fun migrateFromAccount(authInfo: MigratingAccountInfo, reuseSessionToken: Boolean): JSONObject?

/**
* Checks if there's a migration in-flight. An in-flight migration means that we've tried to migrate
* via either [migrateFromSessionTokenAsync] or [copyFromSessionTokenAsync], and failed for intermittent
* (e.g. network)
* reasons. When an in-flight migration is present, we can retry using [retryMigrateFromSessionTokenAsync].
* via [migrateFromAccount], and failed for intermittent (e.g. network) reasons. When an in-flight
* migration is present, we can retry using [retryMigrateFromSessionToken].
* @return InFlightMigrationState indicating specific migration state.
*/
fun isInMigrationState(): InFlightMigrationState
Expand All @@ -227,7 +205,7 @@ interface OAuthAccount : AutoCloseable {
* For up-to-date schema, see underlying implementation in https://github.com/mozilla/application-services/blob/v0.49.0/components/fxa-client/src/migrator.rs#L10
* At the moment, it's just "{total_duration: long}".
*/
fun retryMigrateFromSessionTokenAsync(): Deferred<JSONObject?>
suspend fun retryMigrateFromSessionToken(): JSONObject?

/**
* Returns the device constellation for the current account
Expand All @@ -245,7 +223,7 @@ interface OAuthAccount : AutoCloseable {
* Failure indicates that we may have failed to destroy current device record. Nothing to do for
* the consumer; device record will be cleaned up eventually via TTL.
*/
fun disconnectAsync(): Deferred<Boolean>
suspend fun disconnect(): Boolean

/**
* Serializes the current account's authentication state as a JSON string, for persistence in
Expand Down Expand Up @@ -294,16 +272,42 @@ sealed class AuthType {
data class OtherExternal(val action: String?) : AuthType()

/**
* Account created via a shared account state from another app.
* Account created via a shared account state from another app via the copy token flow.
*/
object Shared : AuthType()
object MigratedCopy : AuthType()

/**
* Account created via a shared account state from another app via the reuse token flow.
*/
object MigratedReuse : AuthType()

/**
* Existing account was recovered from an authentication problem.
*/
object Recovered : AuthType()
}

/**
* Different types of errors that may be encountered during authorization and migration flows.
* Intermittent network problems are the most common reason for these errors.
*/
enum class AuthFlowError {
/**
* Couldn't begin authorization, i.e. failed to obtain an authorization URL.
*/
FailedToBeginAuth,

/**
* Couldn't complete authorization after user entered valid credentials/paired correctly.
*/
FailedToCompleteAuth,

/**
* Unrecoverable error during account migration.
*/
FailedToMigrate
}

/**
* Observer interface which lets its users monitor account state changes and major events.
* (XXX - there's some tension between this and the
Expand Down Expand Up @@ -333,6 +337,12 @@ interface AccountObserver {
* Account needs to be re-authenticated (e.g. due to a password change).
*/
fun onAuthenticationProblems() = Unit

/**
* Encountered an error during an authentication or migration flow.
* @param error Exact error encountered.
*/
fun onFlowError(error: AuthFlowError) = Unit
}

data class Avatar(
Expand Down
Loading

0 comments on commit 6d6d7d4

Please sign in to comment.