Skip to content
Closed
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
28 changes: 28 additions & 0 deletions data/src/main/java/org/cryptomator/data/db/DatabaseUpgrades.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ class DatabaseUpgrades {

private final Map<Integer, List<DatabaseUpgrade>> availableUpgrades;

/**
* Creates the DatabaseUpgrades registry and registers the provided upgrade steps
* into the internal mapping keyed by each step's source version.
*
* Each constructor parameter is the implementation for the corresponding
* from->to version and will be added to the availableUpgrades map.
*
* @param upgrade0To1 upgrade step from version 0 to 1
* @param upgrade1To2 upgrade step from version 1 to 2
* @param upgrade2To3 upgrade step from version 2 to 3
* @param upgrade3To4 upgrade step from version 3 to 4
* @param upgrade4To5 upgrade step from version 4 to 5
* @param upgrade5To6 upgrade step from version 5 to 6
* @param upgrade6To7 upgrade step from version 6 to 7
* @param upgrade7To8 upgrade step from version 7 to 8
* @param upgrade8To9 upgrade step from version 8 to 9
* @param upgrade9To10 upgrade step from version 9 to 10
* @param upgrade10To11 upgrade step from version 10 to 11
* @param upgrade11To12 upgrade step from version 11 to 12
* @param upgrade12To13 upgrade step from version 12 to 13
* @param upgrade13To14 upgrade step from version 13 to 14
*/
@Inject
public DatabaseUpgrades( //
Upgrade0To1 upgrade0To1, //
Expand Down Expand Up @@ -52,6 +74,12 @@ public DatabaseUpgrades( //
upgrade13To14);
}

/**
* Builds a registry that groups provided upgrades by their source version.
*
* @param upgrades varargs of available DatabaseUpgrade instances to register
* @return a map from source version to the list of upgrades that start at that version; each list is sorted in descending (reverse natural) order
*/
private Map<Integer, List<DatabaseUpgrade>> defineUpgrades(DatabaseUpgrade... upgrades) {
Map<Integer, List<DatabaseUpgrade>> result = new HashMap<>();
for (DatabaseUpgrade upgrade : upgrades) {
Expand Down
35 changes: 35 additions & 0 deletions data/src/main/java/org/cryptomator/data/db/Upgrade13To14.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@ import timber.log.Timber
@Singleton
internal class Upgrade13To14 @Inject constructor(private val sharedPreferencesHandler: SharedPreferencesHandler) : DatabaseUpgrade(13, 14) {

/**
* Applies the schema migration steps from version 13 to 14 on the provided database.
*
* If `origin > 0` (an existing installation), marks the welcome flow completed. For builds that
* require migrating a license token (non-premium flavor), reads an existing `LICENSE_TOKEN` from
* the database and saves it to shared preferences. In all cases, removes the `LICENSE_TOKEN`
* column/data from the database.
*
* @param db The database to migrate.
* @param origin The originating schema version; values greater than 0 indicate an existing install.
*/
override fun internalApplyTo(db: Database, origin: Int) {
if (origin > 0) {
// Any user going through a schema migration is an existing user — skip welcome
Expand All @@ -24,10 +35,23 @@ internal class Upgrade13To14 @Inject constructor(private val sharedPreferencesHa
removeLicenseFromDb(db)
}

/**
* Indicates whether the current build uses the premium flavor (i.e., does not store a license key in the database).
*
* @return `true` if the current build is the premium flavor, `false` otherwise.
*/
private fun nonLicenseKeyVariant(): Boolean {
return FlavorConfig.isPremiumFlavor
}

/**
* Removes the `LICENSE_TOKEN` column from the `UPDATE_CHECK_ENTITY` table by recreating the table without that column.
*
* The existing rows for `_id`, `RELEASE_NOTE`, `VERSION`, `URL_TO_APK`, `APK_SHA256`, and `URL_TO_RELEASE_NOTE` are preserved
* by copying them into the new table. The operation is executed within a database transaction.
*
* @param db The database to modify.
*/
private fun removeLicenseFromDb(db: Database) {
db.beginTransaction()
try {
Expand Down Expand Up @@ -55,6 +79,12 @@ internal class Upgrade13To14 @Inject constructor(private val sharedPreferencesHa
}
}

/**
* Retrieves the existing license token from the UPDATE_CHECK_ENTITY table.
*
* @param db The database to query.
* @return The `LICENSE_TOKEN` value from the first row if present, `null` otherwise.
*/
private fun getExistingLicenseToken(db: Database): String? {
Sql.query("UPDATE_CHECK_ENTITY")
.columns(listOf("LICENSE_TOKEN"))
Expand All @@ -66,6 +96,11 @@ internal class Upgrade13To14 @Inject constructor(private val sharedPreferencesHa
return null
}

/**
* Marks the onboarding welcome flow as completed in shared preferences.
*
* Sets the flag that causes the welcome screen to be skipped on subsequent launches.
*/
private fun setWelcomeFlowCompleted() {
sharedPreferencesHandler.setWelcomeFlowCompleted()
Timber.tag("Upgrade13To14").i("Skip welcome screen")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,24 @@ public class UpdateCheckEntity extends DatabaseEntity {

private String urlToReleaseNote;

/**
* Constructs a new UpdateCheckEntity with all fields unset (null).
*
* <p>Required by the persistence framework for entity instantiation.</p>
*/
public UpdateCheckEntity() {
}

/**
* Creates a new UpdateCheckEntity with the given metadata.
*
* @param id primary key (may be null before persistence)
* @param releaseNote release note text or reference
* @param version version string of the release
* @param urlToApk download URL for the APK
* @param apkSha256 SHA-256 checksum of the APK
* @param urlToReleaseNote URL referencing the release notes
*/
@Generated(hash = 867488251)
public UpdateCheckEntity(Long id, String releaseNote, String version, String urlToApk, String apkSha256, String urlToReleaseNote) {
this.id = id;
Expand All @@ -38,10 +53,20 @@ public Long getId() {
return id;
}

/**
* Sets the entity's primary key.
*
* @param id the primary key value, or null if not yet assigned
*/
public void setId(Long id) {
this.id = id;
}

/**
* Gets the version string of the update.
*
* @return the version string, or {@code null} if not set
*/
public String getVersion() {
return this.version;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,29 @@ public HubRepositoryImpl(Context context) {
this.context = context;
}

/**
* Creates an OkHttp logging interceptor that logs HTTP messages to Timber using the "OkHttp" tag.
*
* @param context Android Context used to initialize the interceptor
* @return an Interceptor that forwards OkHttp log messages to Timber with tag "OkHttp"
*/
private Interceptor httpLoggingInterceptor(Context context) {
HttpLoggingInterceptor.Logger logger = message -> Timber.tag("OkHttp").d(message);
return new HttpLoggingInterceptor(logger, context);
}

/**
* Retrieves the vault's JWE access token and subscription state from the Hub.
*
* @param unverifiedHubVaultConfig configuration that provides the Hub API base URL and vault identifier
* @param accessToken Bearer access token for authorization
* @return a {@link HubRepository.VaultAccess} containing the JWE payload and the vault subscription state
* @throws HubLicenseUpgradeRequiredException if the Hub responds with HTTP 402 (payment required)
* @throws HubVaultAccessForbiddenException if the Hub responds with HTTP 403 (forbidden)
* @throws HubVaultIsArchivedException if the Hub responds with HTTP 410 (gone / archived)
* @throws HubUserSetupRequiredException if the Hub responds with status 449 (user setup required)
* @throws FatalBackendException for other HTTP failures, missing response body on HTTP 200, or I/O errors
*/
@Override
public HubRepository.VaultAccess getVaultAccess(UnverifiedHubVaultConfig unverifiedHubVaultConfig, String accessToken) throws BackendException {
var request = new Request.Builder().get() //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,18 @@ private OkHttpClient httpClient() {
.build();
}

/**
* Checks whether a newer application version is available for the provided current version.
*
* If a newer version is found, the method fetches update details, updates the cached
* UpdateCheckEntity (id 1) with APK URL, version and SHA-256, persists the cache, and
* returns an UpdateCheck describing the update. If the provided version already matches
* the latest known version, no update is performed and the result is absent.
*
* @param appVersion the current application version to compare against the latest metadata
* @return an Optional containing an UpdateCheck when an update is available; Optional.absent() when the appVersion matches the latest version
* @throws BackendException if fetching, verifying, or processing remote update metadata fails
*/
@Override
public Optional<UpdateCheck> getUpdateCheck(final String appVersion) throws BackendException {
LatestVersion latestVersion = loadLatestVersion();
Expand All @@ -88,6 +100,13 @@ public Optional<UpdateCheck> getUpdateCheck(final String appVersion) throws Back
return Optional.of(updateCheck);
}

/**
* Downloads the APK pointed to by the cached update entity into the provided file and verifies its SHA-256.
*
* @param file the destination file to write the downloaded APK to
* @throws HashMismatchUpdateCheckException if the downloaded file's SHA-256 does not match the expected value
* @throws GeneralUpdateErrorException for network, I/O, or non-success HTTP status code errors encountered while downloading
*/
@Override
public void update(File file) throws GeneralUpdateErrorException {
try {
Expand Down
73 changes: 73 additions & 0 deletions domain/src/main/java/org/cryptomator/domain/Vault.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ public class Vault implements Serializable {
private final boolean hubVault;
private final boolean hubPaidLicense;

/**
* Creates a Vault instance from the given Builder's validated state.
*
* @param builder the Builder containing the validated properties to initialize this Vault
*/
private Vault(Builder builder) {
this.id = builder.id;
this.name = builder.name;
Expand All @@ -37,10 +42,25 @@ private Vault(Builder builder) {
this.hubPaidLicense = builder.hubPaidLicense;
}

/**
* Create a new Builder for configuring and constructing a Vault.
*
* @return a Builder instance initialized with default values for building a Vault
*/
public static Builder aVault() {
return new Builder();
}

/**
* Create a Builder pre-populated from an existing Vault.
*
* The returned Builder is initialized with the vault's id, cloud, cloudType, name, path,
* unlocked state, saved password and its crypto mode, format, shortening threshold, position,
* and hub-related flags.
*
* @param vault the source Vault to copy properties from
* @return a Builder initialized with the source vault's properties
*/
public static Builder aCopyOf(Vault vault) {
return new Builder() //
.withId(vault.getId()) //
Expand All @@ -57,6 +77,11 @@ public static Builder aCopyOf(Vault vault) {
.withHubPaidLicense(vault.hasHubPaidLicense());
}

/**
* Gets the vault's identifier.
*
* @return the vault id, or {@code null} if the vault has no assigned identifier
*/
public Long getId() {
return id;
}
Expand Down Expand Up @@ -101,18 +126,39 @@ public int getPosition() {
return position;
}

/**
* Indicates whether this vault is read-only.
*
* @return `true` if the vault is read-only, `false` otherwise.
*/
public boolean isReadOnly() {
return false; //TODO Implement read-only check
}

/**
* Indicates whether this vault is managed by Hub.
*
* @return true if the vault is a Hub vault, false otherwise.
*/
public boolean isHubVault() {
return hubVault;
}

/**
* Indicates whether the vault has an associated Hub paid license.
*
* @return `true` if the vault has a Hub paid license, `false` otherwise.
*/
public boolean hasHubPaidLicense() {
return hubPaidLicense;
}

/**
* Determines whether the given object represents the same Vault, using instance identity or matching non-null vault id.
*
* @param obj the object to compare with this Vault
* @return `true` if {@code obj} is the same instance or a {@code Vault} whose non-null id equals this vault's id, `false` otherwise
*/
@Override
public boolean equals(Object obj) {
if (obj == null || getClass() != obj.getClass()) {
Expand Down Expand Up @@ -149,6 +195,9 @@ public static class Builder {
private boolean hubVault;
private boolean hubPaidLicense;

/**
* Creates a new Builder initialized with default field values.
*/
private Builder() {
}

Expand Down Expand Up @@ -219,21 +268,45 @@ public Builder withFormat(int version) {
return this;
}

/**
* Sets the filename shortening threshold for the vault being built.
*
* @param shorteningThreshold the threshold value at which shortening is applied (use -1 to leave unset)
* @return this builder instance
*/
public Builder withShorteningThreshold(int shorteningThreshold) {
this.shorteningThreshold = shorteningThreshold;
return this;
}

/**
* Sets whether the vault has an associated Hub paid license.
*
* @param hubPaidLicense true if the vault has a Hub paid license, false otherwise
* @return this Builder instance for chaining
*/
public Builder withHubPaidLicense(boolean hubPaidLicense) {
this.hubPaidLicense = hubPaidLicense;
return this;
}

/**
* Marks the vault under construction as a Hub vault.
*
* @param hubVault `true` to mark the vault as a Hub-managed vault, `false` otherwise
* @return this builder
*/
public Builder withHubVault(boolean hubVault) {
this.hubVault = hubVault;
return this;
}

/**
* Sets the vault's position used for ordering.
*
* @param position numeric position used for ordering; must be set to a value other than -1 before calling {@code build()}
* @return this Builder instance
*/
public Builder withPosition(int position) {
this.position = position;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,25 @@ import org.cryptomator.domain.exception.BackendException

interface HubRepository {

/**
* Retrieves vault access data for the specified unverified hub vault configuration using the provided access token.
*
* @param unverifiedHubVaultConfig The unverified hub vault configuration that identifies the vault.
* @param accessToken The access token to authenticate the request against the hub.
* @return A [VaultAccess] containing the vault key in JWE form and the vault's subscription state.
* @throws BackendException If the backend request fails or access is denied.
*/
@Throws(BackendException::class)
fun getVaultAccess(unverifiedHubVaultConfig: UnverifiedHubVaultConfig, accessToken: String): VaultAccess

/**
* Fetches the hub user associated with the given unverified hub vault configuration and access token.
*
* @param unverifiedHubVaultConfig The unverified hub vault configuration identifying the vault context.
* @param accessToken The access token used to authenticate the request against the hub.
* @return A UserDto containing the user's id, name, public key, private key, and setup code.
* @throws BackendException If the backend request fails or returns an error.
*/
@Throws(BackendException::class)
fun getUser(unverifiedHubVaultConfig: UnverifiedHubVaultConfig, accessToken: String): UserDto

Expand Down
Loading
Loading