diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index f05b10c72..7ce412d8e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,7 +7,7 @@ on:
env:
NODE_VERSION: 22
- JAVA_VERSION: 21
+ JAVA_VERSION: 25
defaults:
run:
diff --git a/.github/workflows/dependabot-frontend-build.yml b/.github/workflows/dependabot-frontend-build.yml
index da72b6579..7b95847f0 100644
--- a/.github/workflows/dependabot-frontend-build.yml
+++ b/.github/workflows/dependabot-frontend-build.yml
@@ -18,7 +18,7 @@ defaults:
env:
NODE_VERSION: 22
- JAVA_VERSION: 21
+ JAVA_VERSION: 25
jobs:
test:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a4908fd7..08d6247d7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -20,7 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Updated Keycloak to 26.6.3
-- Update Quarkus to 3.27.3.1 LTS
+- Updated to Java 25
+- Update Quarkus to 3.33.2.1 LTS
- Improved browser locale detection (#371)
- Improved efficiency of keycloak-to-hub data sync (#377)
- Improved efficiency of group-based access permission checks (#372)
diff --git a/backend/.idea/misc.xml b/backend/.idea/misc.xml
index e8e9dd894..9bcac8d42 100644
--- a/backend/.idea/misc.xml
+++ b/backend/.idea/misc.xml
@@ -8,7 +8,26 @@
-
+
+
+
+
+
+
+
+
diff --git a/backend/pom.xml b/backend/pom.xml
index 8b8ba1eb1..e2a560bb6 100644
--- a/backend/pom.xml
+++ b/backend/pom.xml
@@ -8,10 +8,11 @@
UTF-8
- 21
+ 25
UTF-8
- 3.27.3.1
+ 3.33.2.1
4.5.1
+ 1.0.0
3.15.0
3.10.0
3.5.5
@@ -22,8 +23,8 @@
- io.quarkus
- quarkus-universe-bom
+ io.quarkus.platform
+ quarkus-bom
${quarkus.platform.version}
pom
import
@@ -98,12 +99,12 @@
io.quarkus
- quarkus-junit5
+ quarkus-junit
test
io.quarkus
- quarkus-junit5-mockito
+ quarkus-junit-mockito
test
@@ -134,6 +135,11 @@
io.quarkus
quarkus-opentelemetry
+
+ org.jspecify
+ jspecify
+ ${jspecify.version}
+
@@ -183,7 +189,7 @@
- -javaagent:${org.mockito:mockito-core:jar} --add-opens java.base/java.lang=ALL-UNNAMED
+ @{argLine} -javaagent:${org.mockito:mockito-core:jar} --add-opens java.base/java.lang=ALL-UNNAMED
org.jboss.logmanager.LogManager
${maven.home}
@@ -215,7 +221,7 @@
- -javaagent:${org.mockito:mockito-core:jar} --add-opens java.base/java.lang=ALL-UNNAMED
+ @{argLine} -javaagent:${org.mockito:mockito-core:jar} --add-opens java.base/java.lang=ALL-UNNAMED
plain
false
diff --git a/backend/src/main/docker/Dockerfile.jvm b/backend/src/main/docker/Dockerfile.jvm
index a422607ee..290f47c8e 100644
--- a/backend/src/main/docker/Dockerfile.jvm
+++ b/backend/src/main/docker/Dockerfile.jvm
@@ -1,5 +1,5 @@
## Stage 1 : build with maven builder image
-FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 AS builder
+FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-25 AS builder
COPY --chown=quarkus:quarkus --chmod=0755 mvnw /code/mvnw
COPY --chown=quarkus:quarkus .mvn /code/.mvn
COPY --chown=quarkus:quarkus pom.xml /code/
@@ -10,7 +10,7 @@ COPY src /code/src
RUN ./mvnw -B package -DskipTests --no-transfer-progress --strict-checksums
## Stage 2 : create the docker final image
-FROM eclipse-temurin:21
+FROM eclipse-temurin:25
COPY --from=builder /code/target/quarkus-app/ /work/
WORKDIR /work/
diff --git a/backend/src/main/docker/Dockerfile.native b/backend/src/main/docker/Dockerfile.native
index 576b825e4..9adcd4912 100644
--- a/backend/src/main/docker/Dockerfile.native
+++ b/backend/src/main/docker/Dockerfile.native
@@ -1,5 +1,5 @@
## Stage 1 : build with maven builder image with native capabilities
-FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-21 AS builder
+FROM quay.io/quarkus/ubi-quarkus-mandrel-builder-image:jdk-25 AS builder
COPY --chown=quarkus:quarkus --chmod=0755 mvnw /code/mvnw
COPY --chown=quarkus:quarkus .mvn /code/.mvn
COPY --chown=quarkus:quarkus pom.xml /code/
diff --git a/backend/src/main/java/org/cryptomator/hub/Main.java b/backend/src/main/java/org/cryptomator/hub/Main.java
index f1cbcca98..1f58da97f 100644
--- a/backend/src/main/java/org/cryptomator/hub/Main.java
+++ b/backend/src/main/java/org/cryptomator/hub/Main.java
@@ -12,8 +12,12 @@ public class Main implements QuarkusApplication {
private static final Logger LOG = Logger.getLogger(Main.class);
+ private final LicenseHolder license;
+
@Inject
- LicenseHolder license;
+ Main(LicenseHolder license) {
+ this.license = license;
+ }
@Override
public int run(String... args) throws Exception {
diff --git a/backend/src/main/java/org/cryptomator/hub/api/AuditLogResource.java b/backend/src/main/java/org/cryptomator/hub/api/AuditLogResource.java
index 95571cbb8..de2a30888 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/AuditLogResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/AuditLogResource.java
@@ -56,10 +56,14 @@ public class AuditLogResource {
VaultKeyRetrievedEvent.TYPE, VaultMemberAddedEvent.TYPE, VaultMemberRemovedEvent.TYPE, VaultMemberUpdatedEvent.TYPE, VaultOwnershipClaimedEvent.TYPE,
EmergencyAccessSetupEvent.TYPE, EmergencyAccessSettingsUpdatedEvent.TYPE, EmergencyAccessRecoveryStartedEvent.TYPE, EmergencyAccessRecoveryApprovedEvent.TYPE, EmergencyAccessRecoveryCompletedEvent.TYPE, EmergencyAccessRecoveryAbortedEvent.TYPE);
+ private final AuditEvent.Repository auditEventRepo;
+ private final LicenseHolder license;
+
@Inject
- AuditEvent.Repository auditEventRepo;
- @Inject
- LicenseHolder license;
+ AuditLogResource(AuditEvent.Repository auditEventRepo, LicenseHolder license) {
+ this.auditEventRepo = auditEventRepo;
+ this.license = license;
+ }
@GET
@RolesAllowed("admin")
diff --git a/backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java b/backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java
index 1ac651e78..242b57b4b 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java
@@ -5,6 +5,7 @@
import org.cryptomator.hub.entities.Authority;
import org.cryptomator.hub.entities.Group;
import org.cryptomator.hub.entities.User;
+import org.jspecify.annotations.Nullable;
@JsonInclude(JsonInclude.Include.NON_NULL)
abstract sealed class AuthorityDto permits UserDto, GroupDto, MemberDto {
@@ -23,9 +24,9 @@ public enum Type {
public final String name;
@JsonProperty("pictureUrl")
- public final String pictureUrl;
+ public final @Nullable String pictureUrl;
- protected AuthorityDto(String id, Type type, String name, String pictureUrl) {
+ protected AuthorityDto(String id, Type type, String name, @Nullable String pictureUrl) {
this.id = id;
this.type = type;
this.name = name;
diff --git a/backend/src/main/java/org/cryptomator/hub/api/AuthorityResource.java b/backend/src/main/java/org/cryptomator/hub/api/AuthorityResource.java
index e5c8564a1..7feb67bd0 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/AuthorityResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/AuthorityResource.java
@@ -21,8 +21,12 @@
@Produces(MediaType.TEXT_PLAIN)
public class AuthorityResource {
+ private final Authority.Repository authorityRepo;
+
@Inject
- Authority.Repository authorityRepo;
+ AuthorityResource(Authority.Repository authorityRepo) {
+ this.authorityRepo = authorityRepo;
+ }
@GET
@Path("/search")
diff --git a/backend/src/main/java/org/cryptomator/hub/api/BillingResource.java b/backend/src/main/java/org/cryptomator/hub/api/BillingResource.java
index 5f7bbe5f7..810be3c9b 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/BillingResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/BillingResource.java
@@ -15,7 +15,6 @@
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.cryptomator.hub.entities.EffectiveVaultAccess;
-import org.cryptomator.hub.entities.Settings;
import org.cryptomator.hub.license.LicenseHolder;
import org.cryptomator.hub.validation.ValidJWS;
import org.eclipse.microprofile.openapi.annotations.Operation;
@@ -27,12 +26,14 @@
@Path("/billing")
public class BillingResource {
+ private final LicenseHolder licenseHolder;
+ private final EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
+
@Inject
- LicenseHolder licenseHolder;
- @Inject
- EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
- @Inject
- Settings.Repository settingsRepo;
+ BillingResource(LicenseHolder licenseHolder, EffectiveVaultAccess.Repository effectiveVaultAccessRepo) {
+ this.licenseHolder = licenseHolder;
+ this.effectiveVaultAccessRepo = effectiveVaultAccessRepo;
+ }
@GET
@Path("/")
@@ -61,7 +62,7 @@ public Response setToken(@NotNull @ValidJWS String token) {
try {
licenseHolder.set(token);
return Response.status(Response.Status.NO_CONTENT).build();
- } catch (JWTVerificationException e) {
+ } catch (JWTVerificationException _) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/api/ConfigResource.java b/backend/src/main/java/org/cryptomator/hub/api/ConfigResource.java
index 3b804895e..a60a23178 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/ConfigResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/ConfigResource.java
@@ -18,35 +18,33 @@
@Path("/config")
public class ConfigResource {
- @Inject
- @ConfigProperty(name = "hub.keycloak.public-url", defaultValue = "")
- String keycloakPublicUrl;
-
- @Inject
- @ConfigProperty(name = "hub.keycloak.realm", defaultValue = "")
- String keycloakRealm;
-
- @Inject
- @ConfigProperty(name = "quarkus.oidc.client-id", defaultValue = "")
- String keycloakClientIdHub;
-
- @Inject
- @ConfigProperty(name = "hub.keycloak.oidc.cryptomator-client-id", defaultValue = "")
- String keycloakClientIdCryptomator;
+ private final String keycloakPublicUrl;
+ private final String keycloakRealm;
+ private final String keycloakClientIdHub;
+ private final String keycloakClientIdCryptomator;
+ private final String internalRealmUrl;
+ private final String billingUrl;
+ private final OidcConfigurationMetadata oidcConfData;
+ private final LicenseHolder license;
@Inject
- @ConfigProperty(name = "quarkus.oidc.auth-server-url")
- String internalRealmUrl;
-
- @Inject
- @ConfigProperty(name = "hub.billing-url", defaultValue = "")
- String billingUrl;
-
- @Inject
- OidcConfigurationMetadata oidcConfData;
-
- @Inject
- LicenseHolder license;
+ ConfigResource(@ConfigProperty(name = "hub.keycloak.public-url", defaultValue = "") String keycloakPublicUrl,
+ @ConfigProperty(name = "hub.keycloak.realm", defaultValue = "") String keycloakRealm,
+ @ConfigProperty(name = "quarkus.oidc.client-id", defaultValue = "") String keycloakClientIdHub,
+ @ConfigProperty(name = "hub.keycloak.oidc.cryptomator-client-id", defaultValue = "") String keycloakClientIdCryptomator,
+ @ConfigProperty(name = "quarkus.oidc.auth-server-url") String internalRealmUrl,
+ @ConfigProperty(name = "hub.billing-url", defaultValue = "") String billingUrl,
+ OidcConfigurationMetadata oidcConfData,
+ LicenseHolder license) {
+ this.keycloakPublicUrl = keycloakPublicUrl;
+ this.keycloakRealm = keycloakRealm;
+ this.keycloakClientIdHub = keycloakClientIdHub;
+ this.keycloakClientIdCryptomator = keycloakClientIdCryptomator;
+ this.internalRealmUrl = internalRealmUrl;
+ this.billingUrl = billingUrl;
+ this.oidcConfData = oidcConfData;
+ this.license = license;
+ }
@PermitAll
@GET
@@ -61,7 +59,7 @@ public ConfigDto getConfig() {
}
//visible for testing
- String replacePrefix(String str, String prefix, String replacement) {
+ static String replacePrefix(String str, String prefix, String replacement) {
int index = str.indexOf(prefix);
if (index == 0) {
return replacement + str.substring(prefix.length());
@@ -71,7 +69,7 @@ String replacePrefix(String str, String prefix, String replacement) {
}
//visible for testing
- String trimTrailingSlash(String str) {
+ static String trimTrailingSlash(String str) {
if (str.endsWith("/")) {
return str.substring(0, str.length() - 1);
} else {
diff --git a/backend/src/main/java/org/cryptomator/hub/api/DeviceResource.java b/backend/src/main/java/org/cryptomator/hub/api/DeviceResource.java
index 116fd188e..dac972ea0 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/DeviceResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/DeviceResource.java
@@ -1,7 +1,7 @@
package org.cryptomator.hub.api;
import com.fasterxml.jackson.annotation.JsonProperty;
-import jakarta.annotation.Nullable;
+import org.jspecify.annotations.Nullable;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.persistence.NoResultException;
@@ -54,27 +54,31 @@ public class DeviceResource {
private static final Logger LOG = Logger.getLogger(DeviceResource.class);
- @Inject
- EventLogger eventLogger;
- @Inject
- User.Repository userRepo;
- @Inject
- Device.Repository deviceRepo;
+ private final EventLogger eventLogger;
+ private final User.Repository userRepo;
+ private final Device.Repository deviceRepo;
/**
* @deprecated to be removed in #333
*/
@Deprecated(since = "1.3.0", forRemoval = true)
- @Inject
- LegacyAccessToken.Repository legacyAccessTokenRepo;
+ private final LegacyAccessToken.Repository legacyAccessTokenRepo;
/**
* @deprecated to be removed in #333
*/
@Deprecated(since = "1.3.0", forRemoval = true)
- @Inject
- LegacyDevice.Repository legacyDeviceRepo;
+ private final LegacyDevice.Repository legacyDeviceRepo;
+ private final JsonWebToken jwt;
@Inject
- JsonWebToken jwt;
+ @SuppressWarnings("deprecation")
+ DeviceResource(EventLogger eventLogger, User.Repository userRepo, Device.Repository deviceRepo, LegacyAccessToken.Repository legacyAccessTokenRepo, LegacyDevice.Repository legacyDeviceRepo, JsonWebToken jwt) {
+ this.eventLogger = eventLogger;
+ this.userRepo = userRepo;
+ this.deviceRepo = deviceRepo;
+ this.legacyAccessTokenRepo = legacyAccessTokenRepo;
+ this.legacyDeviceRepo = legacyDeviceRepo;
+ this.jwt = jwt;
+ }
@GET
@Path("/")
@@ -130,7 +134,7 @@ public Response createOrUpdate(@Valid @NotNull DeviceDto dto, @PathParam("device
Device device;
try {
device = deviceRepo.findByIdAndUser(deviceId, jwt.getSubject());
- } catch (NoResultException e) {
+ } catch (NoResultException _) {
device = new Device();
device.setId(deviceId);
device.setOwner(userRepo.findById(jwt.getSubject()));
@@ -250,8 +254,8 @@ public record DeviceDto(@JsonProperty("id") @ValidId String id,
@JsonProperty("userPrivateKey") @NotNull @ValidJWE String userPrivateKeys, // singular name for history reasons (don't break client compatibility)
@JsonProperty("owner") @ValidId String ownerId,
@JsonProperty("creationTime") Instant creationTime,
- @JsonProperty("lastIpAddress") String lastIpAddress,
- @JsonProperty("lastAccessTime") Instant lastAccessTime,
+ @JsonProperty("lastIpAddress") @Nullable String lastIpAddress,
+ @JsonProperty("lastAccessTime") @Nullable Instant lastAccessTime,
@JsonProperty("legacyDevice") boolean legacyDevice) {
public static DeviceDto fromEntity(Device entity) {
@@ -263,7 +267,7 @@ public static DeviceDto fromEntity(Device entity) {
*/
@Deprecated(since = "1.3.0", forRemoval = true)
public static DeviceDto fromEntity(LegacyDevice entity) {
- return new DeviceDto(entity.getId(), entity.getName(), entity.getType(), entity.getPublickey(), null, entity.getOwner().getId(), entity.getCreationTime().truncatedTo(ChronoUnit.MILLIS), null, null, true);
+ return new DeviceDto(entity.getId(), entity.getName(), entity.getType(), entity.getPublickey(), null /* userPrivateKeys: intentionally null — legacy devices have none; the @NotNull only guards deserialization */, entity.getOwner().getId(), entity.getCreationTime().truncatedTo(ChronoUnit.MILLIS), null, null, true);
}
/**
@@ -273,7 +277,7 @@ public static DeviceDto fromEntity(LegacyDevice entity) {
public static DeviceDto fromEntity(LegacyDevice d, @Nullable VaultKeyRetrievedEvent event) {
var lastIpAddress = (event != null) ? event.getIpAddress() : null;
var lastAccessTime = (event != null) ? event.getTimestamp() : null;
- return new DeviceResource.DeviceDto(d.getId(), d.getName(), d.getType(), d.getPublickey(), null, d.getOwner().getId(), d.getCreationTime().truncatedTo(ChronoUnit.MILLIS), lastIpAddress, lastAccessTime, true);
+ return new DeviceResource.DeviceDto(d.getId(), d.getName(), d.getType(), d.getPublickey(), null /* userPrivateKeys: intentionally null — legacy devices have none; the @NotNull only guards deserialization */, d.getOwner().getId(), d.getCreationTime().truncatedTo(ChronoUnit.MILLIS), lastIpAddress, lastAccessTime, true);
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/api/EmergencyAccessResource.java b/backend/src/main/java/org/cryptomator/hub/api/EmergencyAccessResource.java
index 2c31e49e0..31b75bffa 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/EmergencyAccessResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/EmergencyAccessResource.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.vertx.core.http.HttpServerRequest;
+import org.jspecify.annotations.Nullable;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
@@ -25,7 +26,6 @@
import jakarta.ws.rs.core.Response;
import org.cryptomator.hub.entities.EmergencyRecoveryProcess;
import org.cryptomator.hub.entities.RecoveredEmergencyKeyShares;
-import org.cryptomator.hub.entities.Vault;
import org.cryptomator.hub.entities.events.EventLogger;
import org.cryptomator.hub.util.RawJson;
import org.cryptomator.hub.validation.ValidJWE;
@@ -42,23 +42,21 @@
@Path("/emergency-access")
public class EmergencyAccessResource {
- @Inject
- EmergencyRecoveryProcess.Repository recoverProcessRepo;
-
- @Inject
- RecoveredEmergencyKeyShares.Repository recoveredKeySharesRepo;
-
- @Inject
- Vault.Repository vaultRepo;
-
- @Inject
- JsonWebToken jwt;
+ private final EmergencyRecoveryProcess.Repository recoverProcessRepo;
+ private final RecoveredEmergencyKeyShares.Repository recoveredKeySharesRepo;
+ private final JsonWebToken jwt;
+ private final EventLogger eventLogger;
@Context
HttpServerRequest request;
@Inject
- EventLogger eventLogger;
+ EmergencyAccessResource(EmergencyRecoveryProcess.Repository recoverProcessRepo, RecoveredEmergencyKeyShares.Repository recoveredKeySharesRepo, JsonWebToken jwt, EventLogger eventLogger) {
+ this.recoverProcessRepo = recoverProcessRepo;
+ this.recoveredKeySharesRepo = recoveredKeySharesRepo;
+ this.jwt = jwt;
+ this.eventLogger = eventLogger;
+ }
@PUT
@Path("/{processId}")
@@ -195,7 +193,7 @@ public record RecoveryProcessDto(
@JsonProperty("id") @NotNull UUID id,
@JsonProperty("vaultId") @NotNull UUID vaultId,
@JsonProperty("type") @NotNull EmergencyRecoveryProcess.Type type,
- @JsonProperty("details") @RawJson String details,
+ @JsonProperty("details") @RawJson @Nullable String details,
@JsonProperty("requiredKeyShares") @Min(2) int requiredKeyShares,
@JsonProperty("processPublicKey") @NotNull String processPublicKey,
@JsonProperty("recoveredKeyShares") @NotEmpty Map recoveredKeyShares) {
@@ -216,7 +214,7 @@ public static RecoveryProcessDto fromEntity(EmergencyRecoveryProcess entity) {
@JsonInclude(JsonInclude.Include.NON_NULL)
public record RecoveredKeyShareDto(@JsonProperty("processPrivateKey") @ValidJWE String processPrivateKey, @JsonProperty("unrecoveredKeyShare") @ValidJWE String unrecoveredKeyShare,
- @JsonProperty("recoveredKeyShare") @ValidJWE String recoveredKeyShare, @JsonProperty("signedProcessInfo") @ValidJWS String signedProcessInfo) {
+ @JsonProperty("recoveredKeyShare") @ValidJWE @Nullable String recoveredKeyShare, @JsonProperty("signedProcessInfo") @ValidJWS @Nullable String signedProcessInfo) {
public static RecoveredKeyShareDto fromEntity(RecoveredEmergencyKeyShares entity) {
return new RecoveredKeyShareDto(entity.getProcessPrivateKey(), entity.getUnrecoveredKeyShare(), entity.getRecoveredKeyShare(), entity.getSignedProcessInfo());
diff --git a/backend/src/main/java/org/cryptomator/hub/api/GroupDto.java b/backend/src/main/java/org/cryptomator/hub/api/GroupDto.java
index 9b5d6a686..5b48ec594 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/GroupDto.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/GroupDto.java
@@ -3,18 +3,19 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import org.cryptomator.hub.entities.Group;
+import org.jspecify.annotations.Nullable;
import java.util.List;
public final class GroupDto extends AuthorityDto {
@JsonProperty("memberSize")
- public final Integer memberSize;
+ public final @Nullable Integer memberSize;
@JsonProperty("vaultCount")
- public final Integer vaultCount;
+ public final @Nullable Integer vaultCount;
- GroupDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("memberSize") Integer memberSize, @JsonProperty("vaultCount") Integer vaultCount) {
+ GroupDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") @Nullable String pictureUrl, @JsonProperty("memberSize") @Nullable Integer memberSize, @JsonProperty("vaultCount") @Nullable Integer vaultCount) {
super(id, Type.GROUP, name, pictureUrl);
this.memberSize = memberSize;
this.vaultCount = vaultCount;
diff --git a/backend/src/main/java/org/cryptomator/hub/api/GroupsResource.java b/backend/src/main/java/org/cryptomator/hub/api/GroupsResource.java
index b8968c0aa..aaff7204c 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/GroupsResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/GroupsResource.java
@@ -1,6 +1,7 @@
package org.cryptomator.hub.api;
import com.fasterxml.jackson.annotation.JsonProperty;
+import org.jspecify.annotations.Nullable;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
@@ -32,14 +33,18 @@
@Path("/groups")
public class GroupsResource {
+ private final User.Repository userRepo;
+ private final Group.Repository groupRepo;
+ private final VaultAccess.Repository vaultAccessRepo;
+ private final KeycloakAuthorityPuller keycloakAuthorityPuller;
+
@Inject
- User.Repository userRepo;
- @Inject
- Group.Repository groupRepo;
- @Inject
- VaultAccess.Repository vaultAccessRepo;
- @Inject
- KeycloakAuthorityPuller keycloakAuthorityPuller;
+ GroupsResource(User.Repository userRepo, Group.Repository groupRepo, VaultAccess.Repository vaultAccessRepo, KeycloakAuthorityPuller keycloakAuthorityPuller) {
+ this.userRepo = userRepo;
+ this.groupRepo = groupRepo;
+ this.vaultAccessRepo = vaultAccessRepo;
+ this.keycloakAuthorityPuller = keycloakAuthorityPuller;
+ }
@GET
@Path("/")
@@ -172,14 +177,14 @@ public Response deleteGroup(@PathParam("groupId") @ValidId String groupId) {
public record CreateGroupDto(
@JsonProperty("name") @NotNull String name,
- @JsonProperty("pictureUrl") @Size(max = 255) String pictureUrl
+ @JsonProperty("pictureUrl") @Size(max = 255) @Nullable String pictureUrl
) {
}
public record UpdateGroupDto(
@JsonProperty("name") @NotNull String name,
- @JsonProperty("pictureUrl") @Size(max = 255) String pictureUrl
+ @JsonProperty("pictureUrl") @Size(max = 255) @Nullable String pictureUrl
) {
}
}
\ No newline at end of file
diff --git a/backend/src/main/java/org/cryptomator/hub/api/LicenseResource.java b/backend/src/main/java/org/cryptomator/hub/api/LicenseResource.java
index d4d08b202..f594fd059 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/LicenseResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/LicenseResource.java
@@ -21,11 +21,14 @@
@Path("/license")
public class LicenseResource {
- @Inject
- LicenseHolder licenseHolder;
+ private final LicenseHolder licenseHolder;
+ private final EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
@Inject
- EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
+ LicenseResource(LicenseHolder licenseHolder, EffectiveVaultAccess.Repository effectiveVaultAccessRepo) {
+ this.licenseHolder = licenseHolder;
+ this.effectiveVaultAccessRepo = effectiveVaultAccessRepo;
+ }
@GET
@Path("/user-info")
diff --git a/backend/src/main/java/org/cryptomator/hub/api/MemberDto.java b/backend/src/main/java/org/cryptomator/hub/api/MemberDto.java
index 04a8eb420..c74f9d3a5 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/MemberDto.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/MemberDto.java
@@ -4,19 +4,20 @@
import org.cryptomator.hub.entities.Group;
import org.cryptomator.hub.entities.User;
import org.cryptomator.hub.entities.VaultAccess;
+import org.jspecify.annotations.Nullable;
public final class MemberDto extends AuthorityDto {
@JsonProperty("ecdhPublicKey")
- public final String ecdhPublicKey;
+ public final @Nullable String ecdhPublicKey;
@JsonProperty("ecdsaPublicKey")
- public final String ecdsaPublicKey;
+ public final @Nullable String ecdsaPublicKey;
@JsonProperty("vaultRole")
public final VaultAccess.Role role;
@JsonProperty("memberSize")
- public final Integer memberSize;
+ public final @Nullable Integer memberSize;
- MemberDto(@JsonProperty("id") String id, @JsonProperty("type") Type type, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("ecdhPublicKey") String ecdhPublicKey, @JsonProperty("ecdsaPublicKey") String ecdsaPublicKey, @JsonProperty("vaultRole") VaultAccess.Role role, @JsonProperty("memberSize") Integer memberSize) {
+ MemberDto(@JsonProperty("id") String id, @JsonProperty("type") Type type, @JsonProperty("name") String name, @JsonProperty("pictureUrl") @Nullable String pictureUrl, @JsonProperty("ecdhPublicKey") @Nullable String ecdhPublicKey, @JsonProperty("ecdsaPublicKey") @Nullable String ecdsaPublicKey, @JsonProperty("vaultRole") VaultAccess.Role role, @JsonProperty("memberSize") @Nullable Integer memberSize) {
super(id, type, name, pictureUrl);
this.ecdhPublicKey = ecdhPublicKey;
this.ecdsaPublicKey = ecdsaPublicKey;
diff --git a/backend/src/main/java/org/cryptomator/hub/api/SettingsResource.java b/backend/src/main/java/org/cryptomator/hub/api/SettingsResource.java
index 32a6af153..5256311f3 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/SettingsResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/SettingsResource.java
@@ -27,14 +27,16 @@
@Path("/settings")
public class SettingsResource {
- @Inject
- EventLogger eventLogger;
-
- @Inject
- Settings.Repository settingsRepo;
+ private final EventLogger eventLogger;
+ private final Settings.Repository settingsRepo;
+ private final JsonWebToken jwt;
@Inject
- JsonWebToken jwt;
+ SettingsResource(EventLogger eventLogger, Settings.Repository settingsRepo, JsonWebToken jwt) {
+ this.eventLogger = eventLogger;
+ this.settingsRepo = settingsRepo;
+ this.jwt = jwt;
+ }
@GET
@RolesAllowed("user")
diff --git a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java
index 6da9f8465..15c93463d 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/UserDto.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/UserDto.java
@@ -4,11 +4,11 @@
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
-import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;
import org.cryptomator.hub.entities.User;
import org.cryptomator.hub.validation.OnlyBase64Chars;
import org.cryptomator.hub.validation.ValidJWE;
+import org.jspecify.annotations.Nullable;
import java.util.List;
import java.util.Set;
@@ -17,25 +17,25 @@
public final class UserDto extends AuthorityDto {
private final String email;
- private final String firstName;
- private final String lastName;
- private final String language;
+ private final @Nullable String firstName;
+ private final @Nullable String lastName;
+ private final @Nullable String language;
private final boolean enabled;
private final Set devices;
- private final String ecdhPublicKey;
- private final String ecdsaPublicKey;
- private final String privateKeys;
- private final String setupCode;
+ private final @Nullable String ecdhPublicKey;
+ private final @Nullable String ecdsaPublicKey;
+ private final @Nullable String privateKeys;
+ private final @Nullable String setupCode;
@JsonCreator
public UserDto(
@JsonProperty("id") @NotNull String id,
@JsonProperty("name") @NotNull String name,
- @JsonProperty("pictureUrl") String pictureUrl,
+ @JsonProperty("pictureUrl") @Nullable String pictureUrl,
@JsonProperty("email") @NotNull String email,
- @JsonProperty("firstName") String firstName,
- @JsonProperty("lastName") String lastName,
- @JsonProperty("language") String language,
+ @JsonProperty("firstName") @Nullable String firstName,
+ @JsonProperty("lastName") @Nullable String lastName,
+ @JsonProperty("language") @Nullable String language,
@JsonProperty("enabled") boolean enabled,
@JsonProperty("devices") Set devices,
// Accept either "ecdhPublicKey" or the legacy "publicKey" on input
@@ -62,17 +62,17 @@ public UserDto(
public UserDto(
String id,
String name,
- String pictureUrl,
+ @Nullable String pictureUrl,
String email,
- String firstName,
- String lastName,
- String language,
+ @Nullable String firstName,
+ @Nullable String lastName,
+ @Nullable String language,
boolean enabled,
Set devices,
- String ecdhPublicKey,
- String ecdsaPublicKey,
- String privateKeys,
- String setupCode) {
+ @Nullable String ecdhPublicKey,
+ @Nullable String ecdsaPublicKey,
+ @Nullable String privateKeys,
+ @Nullable String setupCode) {
this(id, name, pictureUrl, email, firstName, lastName, language, enabled, devices, ecdhPublicKey, ecdhPublicKey, ecdsaPublicKey, privateKeys, privateKeys, setupCode);
}
@@ -82,17 +82,17 @@ public String getEmail() {
}
@JsonProperty("firstName")
- public String getFirstName() {
+ public @Nullable String getFirstName() {
return firstName;
}
@JsonProperty("lastName")
- public String getLastName() {
+ public @Nullable String getLastName() {
return lastName;
}
@JsonProperty("language")
- public String getLanguage() {
+ public @Nullable String getLanguage() {
return language;
}
@@ -107,7 +107,7 @@ public Set getDevices() {
}
@JsonProperty("ecdhPublicKey")
- public String getEcdhPublicKey() {
+ public @Nullable String getEcdhPublicKey() {
return ecdhPublicKey;
}
@@ -118,17 +118,17 @@ public String getEcdhPublicKey() {
*/
@Deprecated(forRemoval = true)
@JsonProperty("publicKey")
- public String getPublicKey() {
+ public @Nullable String getPublicKey() {
return ecdhPublicKey;
}
@JsonProperty("ecdsaPublicKey")
- public String getEcdsaPublicKey() {
+ public @Nullable String getEcdsaPublicKey() {
return ecdsaPublicKey;
}
@JsonProperty("privateKeys")
- public String getPrivateKeys() {
+ public @Nullable String getPrivateKeys() {
return privateKeys;
}
@@ -139,12 +139,12 @@ public String getPrivateKeys() {
*/
@Deprecated(forRemoval = true)
@JsonProperty("privateKey")
- public String getPrivateKey() {
+ public @Nullable String getPrivateKey() {
return privateKeys;
}
@JsonProperty("setupCode")
- public String getSetupCode() {
+ public @Nullable String getSetupCode() {
return setupCode;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
index 49d503b1a..78ff297cb 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
@@ -1,7 +1,7 @@
package org.cryptomator.hub.api;
import com.fasterxml.jackson.annotation.JsonProperty;
-import jakarta.annotation.Nullable;
+import org.jspecify.annotations.Nullable;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
@@ -53,30 +53,32 @@
@Produces(MediaType.TEXT_PLAIN)
public class UsersResource {
- @Inject
- AccessToken.Repository accessTokenRepo;
- @Inject
- EventLogger eventLogger;
- @Inject
- User.Repository userRepo;
- @Inject
- Device.Repository deviceRepo;
- @Inject
- Vault.Repository vaultRepo;
- @Inject
- EmergencyRecoveryProcess.Repository emergencyRecovery;
- @Inject
- WotEntry.Repository wotRepo;
- @Inject
- EffectiveWot.Repository effectiveWotRepo;
- @Inject
- AuditEvent.Repository auditEventRepo;
+ private final AccessToken.Repository accessTokenRepo;
+ private final EventLogger eventLogger;
+ private final User.Repository userRepo;
+ private final Device.Repository deviceRepo;
+ private final Vault.Repository vaultRepo;
+ private final EmergencyRecoveryProcess.Repository emergencyRecovery;
+ private final WotEntry.Repository wotRepo;
+ private final EffectiveWot.Repository effectiveWotRepo;
+ private final AuditEvent.Repository auditEventRepo;
+ private final JsonWebToken jwt;
+ private final KeycloakAuthorityPuller keycloakAuthorityPuller;
@Inject
- JsonWebToken jwt;
-
- @Inject
- KeycloakAuthorityPuller keycloakAuthorityPuller;
+ UsersResource(AccessToken.Repository accessTokenRepo, EventLogger eventLogger, User.Repository userRepo, Device.Repository deviceRepo, Vault.Repository vaultRepo, EmergencyRecoveryProcess.Repository emergencyRecovery, WotEntry.Repository wotRepo, EffectiveWot.Repository effectiveWotRepo, AuditEvent.Repository auditEventRepo, JsonWebToken jwt, KeycloakAuthorityPuller keycloakAuthorityPuller) {
+ this.accessTokenRepo = accessTokenRepo;
+ this.eventLogger = eventLogger;
+ this.userRepo = userRepo;
+ this.deviceRepo = deviceRepo;
+ this.vaultRepo = vaultRepo;
+ this.emergencyRecovery = emergencyRecovery;
+ this.wotRepo = wotRepo;
+ this.effectiveWotRepo = effectiveWotRepo;
+ this.auditEventRepo = auditEventRepo;
+ this.jwt = jwt;
+ this.keycloakAuthorityPuller = keycloakAuthorityPuller;
+ }
@PUT
@Path("/me")
@@ -448,18 +450,18 @@ public record CreateUserDto(
@JsonProperty("firstName") @NotNull String firstName,
@JsonProperty("lastName") @NotNull String lastName,
@JsonProperty("password") @NotNull String password,
- @JsonProperty("pictureUrl") @Size(max = 255) String pictureUrl,
- @JsonProperty("groupIds") Set groupIds,
+ @JsonProperty("pictureUrl") @Size(max = 255) @Nullable String pictureUrl,
+ @JsonProperty("groupIds") @Nullable Set groupIds,
@JsonProperty("realmRoles") @NotNull Set realmRoles
) {
}
public record UpdateUserDto(
- @JsonProperty("email") String email,
- @JsonProperty("firstName") String firstName,
- @JsonProperty("lastName") String lastName,
- @JsonProperty("password") String password,
- @JsonProperty("pictureUrl") @Size(max = 255) String pictureUrl,
+ @JsonProperty("email") @Nullable String email,
+ @JsonProperty("firstName") @Nullable String firstName,
+ @JsonProperty("lastName") @Nullable String lastName,
+ @JsonProperty("password") @Nullable String password,
+ @JsonProperty("pictureUrl") @Size(max = 255) @Nullable String pictureUrl,
@JsonProperty("realmRoles") @NotNull Set realmRoles
) {
}
diff --git a/backend/src/main/java/org/cryptomator/hub/api/VaultResource.java b/backend/src/main/java/org/cryptomator/hub/api/VaultResource.java
index af47c7278..044267026 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/VaultResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/VaultResource.java
@@ -5,9 +5,8 @@
import com.auth0.jwt.exceptions.JWTVerificationException;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
-import io.quarkus.security.identity.SecurityIdentity;
import io.vertx.core.http.HttpServerRequest;
-import jakarta.annotation.Nullable;
+import org.jspecify.annotations.Nullable;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.persistence.NoResultException;
@@ -44,6 +43,7 @@
import org.cryptomator.hub.entities.User;
import org.cryptomator.hub.entities.Vault;
import org.cryptomator.hub.entities.VaultAccess;
+import org.cryptomator.hub.entities.VaultAccess.Role;
import org.cryptomator.hub.entities.events.EventLogger;
import org.cryptomator.hub.entities.events.VaultKeyRetrievedEvent;
import org.cryptomator.hub.filters.ActiveLicense;
@@ -79,62 +79,52 @@
@Path("/vaults")
public class VaultResource {
- @Inject
- EventLogger eventLogger;
-
- @Inject
- AccessToken.Repository accessTokenRepo;
-
- @Inject
- Device.Repository deviceRepo;
-
- @Inject
- Group.Repository groupRepo;
-
- @Inject
- User.Repository userRepo;
-
- @Inject
- Authority.Repository authorityRepo;
-
- @Inject
- EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
-
+ private final EventLogger eventLogger;
+ private final AccessToken.Repository accessTokenRepo;
+ private final Device.Repository deviceRepo;
+ private final Group.Repository groupRepo;
+ private final User.Repository userRepo;
+ private final Authority.Repository authorityRepo;
+ private final EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
/**
* @deprecated to be removed in #333
*/
- @Inject
@Deprecated(since = "1.3.0", forRemoval = true)
- LegacyAccessToken.Repository legacyAccessTokenRepo;
-
- @Inject
- Vault.Repository vaultRepo;
-
- @Inject
- VaultAccess.Repository vaultAccessRepo;
-
- @Inject
- JsonWebToken jwt;
-
- @Inject
- SecurityIdentity identity;
-
- @Inject
- LicenseHolder license;
-
- @Inject
- VaultUnlockMetrics vaultUnlockMetrics;
+ private final LegacyAccessToken.Repository legacyAccessTokenRepo;
+ private final Vault.Repository vaultRepo;
+ private final VaultAccess.Repository vaultAccessRepo;
+ private final JsonWebToken jwt;
+ private final LicenseHolder license;
+ private final VaultUnlockMetrics vaultUnlockMetrics;
@Context
HttpServerRequest request;
+ @Inject
+ @SuppressWarnings("deprecation")
+ VaultResource(EventLogger eventLogger, AccessToken.Repository accessTokenRepo, Device.Repository deviceRepo, Group.Repository groupRepo, User.Repository userRepo, Authority.Repository authorityRepo, EffectiveVaultAccess.Repository effectiveVaultAccessRepo, LegacyAccessToken.Repository legacyAccessTokenRepo, Vault.Repository vaultRepo, VaultAccess.Repository vaultAccessRepo, JsonWebToken jwt, LicenseHolder license, VaultUnlockMetrics vaultUnlockMetrics) {
+ this.eventLogger = eventLogger;
+ this.accessTokenRepo = accessTokenRepo;
+ this.deviceRepo = deviceRepo;
+ this.groupRepo = groupRepo;
+ this.userRepo = userRepo;
+ this.authorityRepo = authorityRepo;
+ this.effectiveVaultAccessRepo = effectiveVaultAccessRepo;
+ this.legacyAccessTokenRepo = legacyAccessTokenRepo;
+ this.vaultRepo = vaultRepo;
+ this.vaultAccessRepo = vaultAccessRepo;
+ this.jwt = jwt;
+ this.license = license;
+ this.vaultUnlockMetrics = vaultUnlockMetrics;
+ }
+
@GET
@Path("/accessible")
@RolesAllowed("user")
@Produces(MediaType.APPLICATION_JSON)
@Transactional
@Operation(summary = "list all accessible vaults", description = "list all vaults that have been shared with the currently logged in user or a group in wich this user is")
- public List getAccessible(@Nullable @QueryParam("role") VaultAccess.Role role) {
+ public List getAccessible(@Nullable @QueryParam("role") Role role) {
var currentUserId = jwt.getSubject();
final Stream resultStream;
if (role == null) {
@@ -239,7 +229,7 @@ public Response setDirectMembers(@PathParam("vaultId") UUID vaultId, @NotEmpty M
// resolve group members and simulate new seat count:
var effectiveUsers = new HashSet();
effectiveUsers.addAll(userRepo.getEffectiveGroupUsers(memberRoles.keySet()));
- effectiveUsers.addAll(userRepo.findByIds(memberRoles.keySet()).toList());
+ effectiveUsers.addAll(userRepo.streamByIds(memberRoles.keySet()).toList());
var newSeatOccupyingUsers = new HashSet<>(effectiveVaultAccessRepo.usersSeatedOnOtherVaults(vaultId).toList()); // initialize with users already having access to other vaults
newSeatOccupyingUsers.addAll(effectiveUsers.stream().map(User::getId).toList()); // add all users that will have access to this vault after the operation (avoid double counting by using a set)
if (newSeatOccupyingUsers.size() > license.getEntitlements().seats()) {
@@ -403,7 +393,7 @@ public Response legacyUnlock(@PathParam("vaultId") UUID vaultId, @PathParam("dev
response = response.header("Hub-Android-License", androidLicense);
}
return response.build();
- } catch (NoResultException e) {
+ } catch (NoResultException _) {
eventLogger.logVaultKeyRetrieved(Instant.now(), jwt.getSubject(), vaultId, VaultKeyRetrievedEvent.Result.UNAUTHORIZED, ipAddress, deviceId);
throw new ForbiddenException("Access to this device not granted.");
}
@@ -551,7 +541,7 @@ public VaultDto setArchived(@PathParam("vaultId") UUID vaultId, @NotNull Boolean
.collect(Collectors.toSet());
var effectiveUsers = new HashSet();
effectiveUsers.addAll(userRepo.getEffectiveGroupUsers(authorityIds));
- effectiveUsers.addAll(userRepo.findByIds(authorityIds).toList());
+ effectiveUsers.addAll(userRepo.streamByIds(authorityIds).toList());
var projectedSeatUsers = new HashSet<>(effectiveVaultAccessRepo.usersSeatedOnOtherVaults(vaultId).toList());
projectedSeatUsers.addAll(effectiveUsers.stream().map(User::getId).toList());
if (projectedSeatUsers.size() > license.getEntitlements().seats()) {
@@ -687,14 +677,14 @@ public Response claimOwnership(@PathParam("vaultId") UUID vaultId, @FormParam("p
@JsonInclude(JsonInclude.Include.NON_NULL)
public record VaultDto(@JsonProperty("id") @NotNull UUID id,
@JsonProperty("name") @NoHtmlOrScriptChars @NotBlank String name,
- @JsonProperty("creationTime") Instant creationTime, @JsonProperty("description") @NoHtmlOrScriptChars String description,
+ @JsonProperty("creationTime") Instant creationTime, @JsonProperty("description") @NoHtmlOrScriptChars @Nullable String description,
@JsonProperty("archived") boolean archived,
@JsonProperty("requiredEmergencyKeyShares") @Min(0) int requiredEmergencyKeyShares,
@JsonProperty("emergencyKeyShares") Map emergencyKeyShares,
// Legacy properties ("Vault Admin Password"):
- @JsonProperty("masterkey") @OnlyBase64Chars String masterkey, @JsonProperty("iterations") Integer iterations,
- @JsonProperty("salt") @OnlyBase64Chars String salt,
- @JsonProperty("authPublicKey") @OnlyBase64Chars String authPublicKey, @JsonProperty("authPrivateKey") @OnlyBase64Chars String authPrivateKey
+ @JsonProperty("masterkey") @OnlyBase64Chars @Nullable String masterkey, @JsonProperty("iterations") @Nullable Integer iterations,
+ @JsonProperty("salt") @OnlyBase64Chars @Nullable String salt,
+ @JsonProperty("authPublicKey") @OnlyBase64Chars @Nullable String authPublicKey, @JsonProperty("authPrivateKey") @OnlyBase64Chars @Nullable String authPrivateKey
) {
public static VaultDto fromEntity(Vault entity) {
@@ -706,7 +696,7 @@ public static VaultDto fromEntity(Vault entity) {
public record VaultDtoWithRole(
@JsonProperty("id") UUID id,
@JsonProperty("name") String name,
- @JsonProperty("description") String description,
+ @JsonProperty("description") @Nullable String description,
@JsonProperty("archived") boolean archived,
@JsonProperty("creationTime") Instant creationTime,
@JsonProperty("role") VaultAccess.Role role
diff --git a/backend/src/main/java/org/cryptomator/hub/api/VersionResource.java b/backend/src/main/java/org/cryptomator/hub/api/VersionResource.java
index a9b724cb0..c032478db 100644
--- a/backend/src/main/java/org/cryptomator/hub/api/VersionResource.java
+++ b/backend/src/main/java/org/cryptomator/hub/api/VersionResource.java
@@ -1,6 +1,7 @@
package org.cryptomator.hub.api;
import com.fasterxml.jackson.annotation.JsonProperty;
+import org.jspecify.annotations.Nullable;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@@ -14,11 +15,14 @@
@Path("/version")
public class VersionResource {
- @ConfigProperty(name = "quarkus.application.version")
- String hubVersion;
+ private final String hubVersion;
+ private final Keycloak keycloak;
@Inject
- Keycloak keycloak;
+ VersionResource(@ConfigProperty(name = "quarkus.application.version") String hubVersion, Keycloak keycloak) {
+ this.hubVersion = hubVersion;
+ this.keycloak = keycloak;
+ }
@GET
@Produces(MediaType.APPLICATION_JSON)
@@ -33,7 +37,7 @@ public VersionDto getVersion() {
return new VersionDto(hubVersion, keycloakVersion);
}
- public record VersionDto(@JsonProperty("hubVersion") String hubVersion, @JsonProperty("keycloakVersion") String keycloakVersion) {
+ public record VersionDto(@JsonProperty("hubVersion") String hubVersion, @JsonProperty("keycloakVersion") @Nullable String keycloakVersion) {
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/api/package-info.java b/backend/src/main/java/org/cryptomator/hub/api/package-info.java
new file mode 100644
index 000000000..dabced1af
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/api/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.api;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/AccessToken.java b/backend/src/main/java/org/cryptomator/hub/entities/AccessToken.java
index 96720bc79..5b414f2ad 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/AccessToken.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/AccessToken.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
@@ -15,6 +14,7 @@
import jakarta.persistence.NoResultException;
import jakarta.persistence.Table;
+import java.util.Map;
import java.util.Objects;
import java.util.UUID;
@@ -116,14 +116,14 @@ public static class Repository implements PanacheRepositoryBase {
public Stream byName(String name) {
- return find("#Authority.byName", Parameters.with("name", '%' + name.toLowerCase() + '%')).stream();
+ return find("#Authority.byName", Map.of("name", '%' + name.toLowerCase() + '%')).stream();
}
public Stream findAllInList(Collection ids) {
- return Batch.of(200).run(ids, Stream.empty(), (batch, result) -> {
- var partial = find("WHERE id IN :ids", Parameters.with("ids", batch));
- return Stream.concat(result, partial.stream());
- });
+ return ids.stream()
+ .gather(Gatherers.windowFixed(200))
+ .flatMap(batch -> find("WHERE id IN :ids", Map.of("ids", batch)).stream());
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/Batch.java b/backend/src/main/java/org/cryptomator/hub/entities/Batch.java
index f3661e89f..c2c718ea0 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/Batch.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/Batch.java
@@ -25,7 +25,7 @@ public static Batch of(int size) {
// TODO: add jspecify annotations
public void run(Collection collection, Consumer> job) {
- run(collection, null, (batch, ignored) -> {
+ run(collection, null, (batch, _) -> {
job.accept(batch);
return null;
});
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/Device.java b/backend/src/main/java/org/cryptomator/hub/entities/Device.java
index c7800ff4b..d01178700 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/Device.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/Device.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -17,7 +16,9 @@
import java.time.Instant;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
+import java.util.stream.Gatherers;
import java.util.stream.Stream;
@Entity
@@ -180,14 +181,13 @@ public int hashCode() {
public static class Repository implements PanacheRepositoryBase {
public Device findByIdAndUser(String deviceId, String userId) throws NoResultException {
- return find("#Device.findByIdAndOwner", Parameters.with("deviceId", deviceId).and("userId", userId)).singleResult();
+ return find("#Device.findByIdAndOwner", Map.of("deviceId", deviceId, "userId", userId)).singleResult();
}
public Stream findAllInList(List ids) {
- return Batch.of(200).run(ids, Stream.empty(), (batch, result) -> {
- var partial = find("#Device.allInList", Parameters.with("ids", batch));
- return Stream.concat(result, partial.stream());
- });
+ return ids.stream()
+ .gather(Gatherers.windowFixed(200))
+ .flatMap(batch -> find("#Device.allInList", Map.of("ids", batch)).stream());
}
public void updateLastAccess(String deviceId, Instant timestamp, String ipAddress) {
@@ -195,7 +195,7 @@ public void updateLastAccess(String deviceId, Instant timestamp, String ipAddres
}
public void deleteByOwner(String userId) {
- delete("#Device.deleteByOwner", Parameters.with("userId", userId));
+ delete("#Device.deleteByOwner", Map.of("userId", userId));
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/EffectiveGroupMembership.java b/backend/src/main/java/org/cryptomator/hub/entities/EffectiveGroupMembership.java
index 91a95da15..fccf6db90 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/EffectiveGroupMembership.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/EffectiveGroupMembership.java
@@ -2,7 +2,6 @@
import io.opentelemetry.instrumentation.annotations.WithSpan;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@@ -14,6 +13,7 @@
import org.hibernate.annotations.Immutable;
import java.util.Collection;
+import java.util.Map;
@NamedNativeQuery(name = "EffectiveGroupMembership.fullUpdate", query = """
INSERT INTO "effective_group_membership" ("group_id", "intermediate_group_ids", "member_id")
@@ -105,7 +105,7 @@ public void updateGroups(Collection groupIds) {
@WithSpan("EffectiveGroupMembership.Repository.updateUsers")
public void updateUsers(Collection userIds) {
Batch.of(200).run(userIds, batch -> {
- delete("#EffectiveGroupMembership.deleteUsers", Parameters.with("userIds", batch));
+ delete("#EffectiveGroupMembership.deleteUsers", Map.of("userIds", batch));
getEntityManager()
.createNamedQuery("EffectiveGroupMembership.updateUsers")
.setParameter("userIds", batch)
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/EffectiveVaultAccess.java b/backend/src/main/java/org/cryptomator/hub/entities/EffectiveVaultAccess.java
index 42b8c1713..be9761a31 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/EffectiveVaultAccess.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/EffectiveVaultAccess.java
@@ -2,7 +2,6 @@
import io.opentelemetry.instrumentation.annotations.WithSpan;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@@ -19,6 +18,7 @@
import org.hibernate.annotations.Immutable;
import java.util.Collection;
+import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -118,12 +118,12 @@ public record Id(
public static class Repository implements PanacheRepositoryBase {
public boolean isUserOccupyingSeat(String userId) {
- return find("#EffectiveVaultAccess.isUserOccupyingSeat", Parameters.with("userId", userId)).page(0, 1).firstResult() != null;
+ return find("#EffectiveVaultAccess.isUserOccupyingSeat", Map.of("userId", userId)).page(0, 1).firstResult() != null;
}
public long countSeatsOccupiedByUsers(Collection userIds) {
return Batch.of(200).run(Set.copyOf(userIds), 0L, (batch, result) -> {
- long partialCount = count("#EffectiveVaultAccess.countSeatsOccupiedByUsers", Parameters.with("userIds", batch));
+ long partialCount = count("#EffectiveVaultAccess.countSeatsOccupiedByUsers", Map.of("userIds", batch));
return result + partialCount;
});
}
@@ -138,17 +138,17 @@ public long countSeatOccupyingUsersWithAccessToken() {
}
public long countSeatOccupyingUsersOfGroup(String groupId) {
- return count("#EffectiveVaultAccess.countSeatOccupyingUsersOfGroup", Parameters.with("groupId", groupId));
+ return count("#EffectiveVaultAccess.countSeatOccupyingUsersOfGroup", Map.of("groupId", groupId));
}
public Collection listRoles(UUID vaultId, String authorityId) {
- return find("#EffectiveVaultAccess.findByAuthorityAndVault", Parameters.with("vaultId", vaultId).and("authorityId", authorityId)).stream()
+ return find("#EffectiveVaultAccess.findByAuthorityAndVault", Map.of("vaultId", vaultId, "authorityId", authorityId)).stream()
.map(eva -> eva.getId().role())
.collect(Collectors.toUnmodifiableSet());
}
public Stream usersSeatedOnOtherVaults(UUID vaultId) {
- return find("#EffectiveVaultAccess.usersSeatedOnOtherVaults", Parameters.with("vaultId", vaultId)).project(String.class).stream();
+ return find("#EffectiveVaultAccess.usersSeatedOnOtherVaults", Map.of("vaultId", vaultId)).project(String.class).stream();
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/EffectiveWot.java b/backend/src/main/java/org/cryptomator/hub/entities/EffectiveWot.java
index 7a70632bd..2a12c81f9 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/EffectiveWot.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/EffectiveWot.java
@@ -2,7 +2,6 @@
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@@ -12,6 +11,7 @@
import jakarta.persistence.Table;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Type;
+import java.util.Map;
@Entity
@Immutable
@@ -31,7 +31,7 @@ public class EffectiveWot {
@EmbeddedId
private Id id;
- @Column(name = "signature_chain")
+ @Column(name = "signature_chain", nullable = false)
@Type(StringArrayType.class)
private String[] signatureChain;
@@ -60,11 +60,11 @@ public record Id(
@ApplicationScoped
public static class Repository implements PanacheRepositoryBase {
public PanacheQuery findTrusted(String trustingUserId) {
- return find("#EffectiveWot.findTrustedUsers", Parameters.with("trustingUserId", trustingUserId));
+ return find("#EffectiveWot.findTrustedUsers", Map.of("trustingUserId", trustingUserId));
}
public PanacheQuery findTrusted(String trustingUserId, String trustedUserId) {
- return find("#EffectiveWot.findTrustedUser", Parameters.with("trustingUserId", trustingUserId).and("trustedUserId", trustedUserId));
+ return find("#EffectiveWot.findTrustedUser", Map.of("trustingUserId", trustingUserId, "trustedUserId", trustedUserId));
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/EmergencyRecoveryProcess.java b/backend/src/main/java/org/cryptomator/hub/entities/EmergencyRecoveryProcess.java
index cb820d175..f3062a9e7 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/EmergencyRecoveryProcess.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/EmergencyRecoveryProcess.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
@@ -14,6 +13,7 @@
import jakarta.persistence.NamedQuery;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
@@ -68,7 +68,7 @@ public enum Type {
private Type type;
@Column(name = "details")
- private String details;
+ private @Nullable String details;
@Column(name = "required_key_shares", nullable = false)
private int requiredKeyShares;
@@ -104,11 +104,11 @@ public void setType(Type type) {
this.type = type;
}
- public String getDetails() {
+ public @Nullable String getDetails() {
return details;
}
- public void setDetails(String details) {
+ public void setDetails(@Nullable String details) {
this.details = details;
}
@@ -153,15 +153,15 @@ public int hashCode() {
public static class Repository implements PanacheRepositoryBase {
public Stream findByVaultId(UUID vaultId) {
- return find("#EmergencyRecoveryProcess.findByVaultId", Parameters.with("vaultId", vaultId)).stream();
+ return find("#EmergencyRecoveryProcess.findByVaultId", Map.of("vaultId", vaultId)).stream();
}
public Stream findByCouncilMember(String councilMemberId) {
- return find("#EmergencyRecoveryProcess.byCouncilMember", Parameters.with("councilMemberId", councilMemberId)).stream();
+ return find("#EmergencyRecoveryProcess.byCouncilMember", Map.of("councilMemberId", councilMemberId)).stream();
}
public Stream findByCouncilMemberOrProcessMember(String councilMemberId) {
- return find("#EmergencyRecoveryProcess.byCouncilMemberOrProcessMember", Parameters.with("councilMemberId", councilMemberId)).stream();
+ return find("#EmergencyRecoveryProcess.byCouncilMemberOrProcessMember", Map.of("councilMemberId", councilMemberId)).stream();
}
public void deleteKeySharesForCouncilMember(String councilMemberId) {
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/Group.java b/backend/src/main/java/org/cryptomator/hub/entities/Group.java
index 398ae4877..ded3eb61e 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/Group.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/Group.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
@@ -18,6 +17,7 @@
import java.util.Collection;
import java.util.HashSet;
+import java.util.Map;
import java.util.Set;
@NamedNativeQuery(name = "Group.addMember", query = """
@@ -63,7 +63,7 @@ public Group findByIdWithEagerDetails(String id) {
FROM Group g
LEFT JOIN FETCH g.members
WHERE g.id = :id
- """, Parameters.with("id", id)).singleResultOptional().orElse(null);
+ """, Map.of("id", id)).singleResultOptional().orElse(null);
if (group == null) {
return null;
}
@@ -73,7 +73,7 @@ public Group findByIdWithEagerDetails(String id) {
}
public long deleteByIds(Collection ids) {
- return Batch.of(200).run(ids, 0L, (batch, result) -> result + delete("id IN :ids", Parameters.with("ids", batch)));
+ return Batch.of(200).run(ids, 0L, (batch, result) -> result + delete("id IN :ids", Map.of("ids", batch)));
}
/**
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/LegacyDevice.java b/backend/src/main/java/org/cryptomator/hub/entities/LegacyDevice.java
index 7f8c00f8f..0815298d1 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/LegacyDevice.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/LegacyDevice.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -16,6 +15,7 @@
import java.time.Instant;
import java.util.List;
+import java.util.Map;
import java.util.stream.Stream;
/**
@@ -95,7 +95,7 @@ public String getPublickey() {
public static class Repository implements PanacheRepositoryBase {
public Stream findAllInList(List ids) {
- return find("#LegacyDevice.allInList", Parameters.with("ids", ids)).stream();
+ return find("#LegacyDevice.allInList", Map.of("ids", ids)).stream();
}
public boolean existsAny() {
@@ -103,7 +103,7 @@ public boolean existsAny() {
}
public void deleteByOwner(String userId) {
- delete("#LegacyDevice.deleteByOwner", Parameters.with("userId", userId));
+ delete("#LegacyDevice.deleteByOwner", Map.of("userId", userId));
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/RecoveredEmergencyKeyShares.java b/backend/src/main/java/org/cryptomator/hub/entities/RecoveredEmergencyKeyShares.java
index a7866a884..24d5a05e6 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/RecoveredEmergencyKeyShares.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/RecoveredEmergencyKeyShares.java
@@ -7,6 +7,7 @@
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -25,10 +26,10 @@ public class RecoveredEmergencyKeyShares {
private String unrecoveredKeyShare;
@Column(name = "recovered_key_share")
- private String recoveredKeyShare;
+ private @Nullable String recoveredKeyShare;
@Column(name = "signed_process_info")
- private String signedProcessInfo;
+ private @Nullable String signedProcessInfo;
public Id getId() {
return id;
@@ -54,19 +55,19 @@ public void setUnrecoveredKeyShare(String unrecoveredKeyShare) {
this.unrecoveredKeyShare = unrecoveredKeyShare;
}
- public String getRecoveredKeyShare() {
+ public @Nullable String getRecoveredKeyShare() {
return recoveredKeyShare;
}
- public void setRecoveredKeyShare(String recoveredKeyShare) {
+ public void setRecoveredKeyShare(@Nullable String recoveredKeyShare) {
this.recoveredKeyShare = recoveredKeyShare;
}
- public String getSignedProcessInfo() {
+ public @Nullable String getSignedProcessInfo() {
return signedProcessInfo;
}
- public void setSignedProcessInfo(String signedProcessInfo) {
+ public void setSignedProcessInfo(@Nullable String signedProcessInfo) {
this.signedProcessInfo = signedProcessInfo;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/Settings.java b/backend/src/main/java/org/cryptomator/hub/entities/Settings.java
index 056868e70..0bf7d7ea1 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/Settings.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/Settings.java
@@ -9,6 +9,7 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.HashSet;
@@ -29,7 +30,7 @@ public class Settings {
private String hubId;
@Column(name = "license_key")
- private String licenseKey;
+ private @Nullable String licenseKey;
@Column(name = "wot_max_depth", nullable = false)
private int wotMaxDepth;
@@ -73,11 +74,11 @@ public void setHubId(String hubId) {
this.hubId = hubId;
}
- public String getLicenseKey() {
+ public @Nullable String getLicenseKey() {
return licenseKey;
}
- public void setLicenseKey(String licenseKey) {
+ public void setLicenseKey(@Nullable String licenseKey) {
this.licenseKey = licenseKey;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/User.java b/backend/src/main/java/org/cryptomator/hub/entities/User.java
index af1abd6e6..861b5f8a9 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/User.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/User.java
@@ -2,7 +2,6 @@
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorValue;
@@ -14,14 +13,17 @@
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import org.hibernate.annotations.Immutable;
+import org.jspecify.annotations.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
+import java.util.stream.Gatherers;
import java.util.stream.Stream;
@Entity
@@ -50,31 +52,31 @@ SELECT count( DISTINCT u)
public class User extends Authority {
@Column(name = "email")
- private String email;
+ private @Nullable String email;
@Column(name = "firstname")
- private String firstName;
+ private @Nullable String firstName;
@Column(name = "lastname")
- private String lastName;
+ private @Nullable String lastName;
@Column(name = "language")
- private String language;
+ private @Nullable String language;
@Column(name = "enabled", nullable = false)
private boolean enabled = true;
@Column(name = "ecdh_publickey")
- private String ecdhPublicKey;
+ private @Nullable String ecdhPublicKey;
@Column(name = "ecdsa_publickey")
- private String ecdsaPublicKey;
+ private @Nullable String ecdsaPublicKey;
@Column(name = "privatekeys")
- private String privateKeys;
+ private @Nullable String privateKeys;
@Column(name = "setupcode")
- private String setupCode;
+ private @Nullable String setupCode;
@OneToOne(mappedBy = "user", fetch = FetchType.LAZY)
public UserMetrics metrics;
@@ -99,35 +101,35 @@ public class User extends Authority {
@OneToMany(mappedBy = "owner", orphanRemoval = true, fetch = FetchType.LAZY)
private Set legacyDevices = new HashSet<>();
- public String getEmail() {
+ public @Nullable String getEmail() {
return email;
}
- public void setEmail(String email) {
+ public void setEmail(@Nullable String email) {
this.email = email;
}
- public String getFirstName() {
+ public @Nullable String getFirstName() {
return firstName;
}
- public void setFirstName(String firstName) {
+ public void setFirstName(@Nullable String firstName) {
this.firstName = firstName;
}
- public String getLastName() {
+ public @Nullable String getLastName() {
return lastName;
}
- public void setLastName(String lastName) {
+ public void setLastName(@Nullable String lastName) {
this.lastName = lastName;
}
- public String getLanguage() {
+ public @Nullable String getLanguage() {
return language;
}
- public void setLanguage(String language) {
+ public void setLanguage(@Nullable String language) {
this.language = language;
}
@@ -139,35 +141,35 @@ public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
- public String getEcdhPublicKey() {
+ public @Nullable String getEcdhPublicKey() {
return ecdhPublicKey;
}
- public void setEcdhPublicKey(String ecdhPublicKey) {
+ public void setEcdhPublicKey(@Nullable String ecdhPublicKey) {
this.ecdhPublicKey = ecdhPublicKey;
}
- public String getEcdsaPublicKey() {
+ public @Nullable String getEcdsaPublicKey() {
return ecdsaPublicKey;
}
- public void setEcdsaPublicKey(String ecdsaPublicKey) {
+ public void setEcdsaPublicKey(@Nullable String ecdsaPublicKey) {
this.ecdsaPublicKey = ecdsaPublicKey;
}
- public String getPrivateKeys() {
+ public @Nullable String getPrivateKeys() {
return privateKeys;
}
- public void setPrivateKeys(String privateKeys) {
+ public void setPrivateKeys(@Nullable String privateKeys) {
this.privateKeys = privateKeys;
}
- public String getSetupCode() {
+ public @Nullable String getSetupCode() {
return setupCode;
}
- public void setSetupCode(String setupCode) {
+ public void setSetupCode(@Nullable String setupCode) {
this.setupCode = setupCode;
}
@@ -241,7 +243,7 @@ public User findByIdWithEagerDetails(String id) {
LEFT JOIN FETCH u.devices d
LEFT JOIN FETCH u.legacyDevices ld
WHERE u.id = :id
- """, Parameters.with("id", id)).singleResultOptional().orElse(null);
+ """, Map.of("id", id)).singleResultOptional().orElse(null);
if (user == null) {
return null;
}
@@ -258,23 +260,22 @@ public User findByIdWithEagerDetails(String id) {
return user;
}
- public Stream findByIds(Collection ids) {
- return Batch.of(200).run(ids, Stream.empty(), (batch, result) -> {
- var partial = find("id IN :ids", Parameters.with("ids", batch));
- return Stream.concat(result, partial.stream());
- });
+ public Stream streamByIds(Collection ids) {
+ return ids.stream()
+ .gather(Gatherers.windowFixed(200))
+ .flatMap(batch -> find("id IN :ids", Map.of("ids", batch)).stream());
}
public long deleteByIds(Collection ids) {
- return Batch.of(200).run(ids, 0L, (batch, result) -> result + delete("id IN :ids", Parameters.with("ids", batch)));
+ return Batch.of(200).run(ids, 0L, (batch, result) -> result + delete("id IN :ids", Map.of("ids", batch)));
}
public Stream findRequiringAccessGrant(UUID vaultId) {
- return find("#User.requiringAccessGrant", Parameters.with("vaultId", vaultId)).stream();
+ return find("#User.requiringAccessGrant", Map.of("vaultId", vaultId)).stream();
}
public long countEffectiveGroupUsers(String groupdId) {
- return count("#User.countEffectiveGroupUsers", Parameters.with("groupId", groupdId));
+ return count("#User.countEffectiveGroupUsers", Map.of("groupId", groupdId));
}
public Stream getEffectiveGroupUsers(String groupdId) {
@@ -283,7 +284,7 @@ public Stream getEffectiveGroupUsers(String groupdId) {
public Set getEffectiveGroupUsers(Collection groupIds) {
return Batch.of(200).run(groupIds, new HashSet<>(), (batch, result) -> {
- var partial = find("#User.getEffectiveGroupUsers", Parameters.with("groupIds", batch)).project(User.class);
+ var partial = find("#User.getEffectiveGroupUsers", Map.of("groupIds", batch)).project(User.class);
result.addAll(partial.list());
return result;
});
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/Vault.java b/backend/src/main/java/org/cryptomator/hub/entities/Vault.java
index e34f82af7..5ddd802aa 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/Vault.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/Vault.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
@@ -18,6 +17,7 @@
import jakarta.persistence.Table;
import jakarta.transaction.Transactional;
import org.hibernate.annotations.Immutable;
+import org.jspecify.annotations.Nullable;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
@@ -35,6 +35,7 @@
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
+import java.util.stream.Gatherers;
import java.util.stream.Stream;
@Entity
@@ -91,25 +92,25 @@ public class Vault {
private String name;
@Column(name = "salt")
- private String salt;
+ private @Nullable String salt;
@Column(name = "iterations")
- private Integer iterations;
+ private @Nullable Integer iterations;
@Column(name = "masterkey")
- private String masterkey;
+ private @Nullable String masterkey;
@Column(name = "auth_pubkey")
- private String authenticationPublicKey;
+ private @Nullable String authenticationPublicKey;
@Column(name = "auth_prvkey")
- private String authenticationPrivateKey;
+ private @Nullable String authenticationPrivateKey;
@Column(name = "creation_time", nullable = false)
private Instant creationTime;
@Column(name = "description")
- private String description;
+ private @Nullable String description;
@Column(name = "archived", nullable = false)
private boolean archived;
@@ -140,7 +141,7 @@ public Optional getAuthenticationPublicKeyOptional() {
} else {
return Optional.empty();
}
- } catch (InvalidKeySpecException e) {
+ } catch (InvalidKeySpecException _) {
return Optional.empty();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException(e);
@@ -175,43 +176,43 @@ public void setName(String name) {
this.name = name;
}
- public String getSalt() {
+ public @Nullable String getSalt() {
return salt;
}
- public void setSalt(String salt) {
+ public void setSalt(@Nullable String salt) {
this.salt = salt;
}
- public Integer getIterations() {
+ public @Nullable Integer getIterations() {
return iterations;
}
- public void setIterations(Integer iterations) {
+ public void setIterations(@Nullable Integer iterations) {
this.iterations = iterations;
}
- public String getMasterkey() {
+ public @Nullable String getMasterkey() {
return masterkey;
}
- public void setMasterkey(String masterkey) {
+ public void setMasterkey(@Nullable String masterkey) {
this.masterkey = masterkey;
}
- public void setAuthenticationPublicKey(String authenticationPublicKey) {
+ public void setAuthenticationPublicKey(@Nullable String authenticationPublicKey) {
this.authenticationPublicKey = authenticationPublicKey;
}
- public String getAuthenticationPrivateKey() {
+ public @Nullable String getAuthenticationPrivateKey() {
return authenticationPrivateKey;
}
- public String getAuthenticationPublicKey() {
+ public @Nullable String getAuthenticationPublicKey() {
return authenticationPublicKey;
}
- public void setAuthenticationPrivateKey(String authenticationPrivateKey) {
+ public void setAuthenticationPrivateKey(@Nullable String authenticationPrivateKey) {
this.authenticationPrivateKey = authenticationPrivateKey;
}
@@ -223,11 +224,11 @@ public void setCreationTime(Instant creationTime) {
this.creationTime = creationTime;
}
- public String getDescription() {
+ public @Nullable String getDescription() {
return description;
}
- public void setDescription(String description) {
+ public void setDescription(@Nullable String description) {
this.description = description;
}
@@ -298,22 +299,21 @@ public String toString() {
public static class Repository implements PanacheRepositoryBase {
public Stream findAccessibleByUser(String userId) {
- return find("#Vault.accessibleByUser", Parameters.with("userId", userId)).stream();
+ return find("#Vault.accessibleByUser", Map.of("userId", userId)).stream();
}
public Stream findRecoverable(String userId) {
- return find("#Vault.recoverableByUser", Parameters.with("councilMemberId", userId)).stream();
+ return find("#Vault.recoverableByUser", Map.of("councilMemberId", userId)).stream();
}
public Stream findAccessibleByUser(String userId, VaultAccess.Role role) {
- return find("#Vault.accessibleByUserAndRole", Parameters.with("userId", userId).and("role", role)).stream();
+ return find("#Vault.accessibleByUserAndRole", Map.of("userId", userId, "role", role)).stream();
}
public Stream findAllInList(List ids) {
- return Batch.of(200).run(ids, Stream.of(), (batch, result) -> {
- Stream partialResult = find("#Vault.allInList", Parameters.with("ids", batch)).stream();
- return Stream.concat(result, partialResult);
- });
+ return ids.stream()
+ .gather(Gatherers.windowFixed(200))
+ .flatMap(batch -> find("#Vault.allInList", Map.of("ids", batch)).stream());
}
@Transactional(Transactional.TxType.REQUIRED)
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/VaultAccess.java b/backend/src/main/java/org/cryptomator/hub/entities/VaultAccess.java
index 64ce7e6e9..ac14b6ab9 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/VaultAccess.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/VaultAccess.java
@@ -1,7 +1,6 @@
package org.cryptomator.hub.entities;
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@@ -15,6 +14,7 @@
import jakarta.persistence.NamedQuery;
import jakarta.persistence.Table;
+import java.util.Map;
import java.util.UUID;
import java.util.stream.Stream;
@@ -127,19 +127,19 @@ public record Id(@Column(name = "vault_id") UUID vaultId, @Column(name = "author
public static class Repository implements PanacheRepositoryBase {
public Stream forVault(UUID vaultId) {
- return find("#VaultAccess.forVault", Parameters.with("vaultId", vaultId)).stream();
+ return find("#VaultAccess.forVault", Map.of("vaultId", vaultId)).stream();
}
public long countByAuthority(String authorityId) {
- return count("#VaultAccess.countByAuthority", Parameters.with("authorityId", authorityId));
+ return count("#VaultAccess.countByAuthority", Map.of("authorityId", authorityId));
}
public Stream findByAuthority(String authorityId) {
- return find("#VaultAccess.findByAuthority", Parameters.with("authorityId", authorityId)).stream();
+ return find("#VaultAccess.findByAuthority", Map.of("authorityId", authorityId)).stream();
}
public long delete(UUID vaultId, Iterable authorityIds) {
- return delete("#VaultAccess.deleteSpecific", Parameters.with("vaultId", vaultId).and("authorityIds", authorityIds));
+ return delete("#VaultAccess.deleteSpecific", Map.of("vaultId", vaultId, "authorityIds", authorityIds));
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/AuditEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/AuditEvent.java
index 8df4baeb7..da637f0b3 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/AuditEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/AuditEvent.java
@@ -2,7 +2,6 @@
import io.quarkus.hibernate.orm.panache.PanacheQuery;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
-import io.quarkus.panache.common.Parameters;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorColumn;
@@ -18,6 +17,7 @@
import java.time.Instant;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Stream;
@@ -124,11 +124,7 @@ public static class Repository implements PanacheRepository {
public Stream findAllInPeriod(Instant startDate, Instant endDate, List type, long paginationId, boolean ascending, int pageSize) {
var allTypes = type.isEmpty();
- var parameters = Parameters.with("startDate", startDate)
- .and("endDate", endDate)
- .and("paginationId", paginationId)
- .and("types", type)
- .and("allTypes", allTypes);
+ var parameters = Map.of("startDate", startDate, "endDate", endDate, "paginationId", paginationId, "types", type, "allTypes", allTypes);
final PanacheQuery query;
if (ascending) {
@@ -141,7 +137,7 @@ public Stream findAllInPeriod(Instant startDate, Instant endDate, Li
}
public Stream findLastVaultKeyRetrieve(Set deviceIds) {
- return find("#AuditEvent.lastVaultKeyRetrieve", Parameters.with("deviceIds", deviceIds)).stream();
+ return find("#AuditEvent.lastVaultKeyRetrieve", Map.of("deviceIds", deviceIds)).stream();
}
}
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRegisteredEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRegisteredEvent.java
index 6117336a9..21144520d 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRegisteredEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRegisteredEvent.java
@@ -6,7 +6,8 @@
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
-import org.cryptomator.hub.entities.Device;
+import org.cryptomator.hub.entities.Device.Type;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -18,47 +19,47 @@ public class DeviceRegisteredEvent extends AuditEvent {
public static final String TYPE = "DEVICE_REGISTER";
@Column(name = "registered_by")
- private String registeredBy;
+ private @Nullable String registeredBy;
@Column(name = "device_id")
- private String deviceId;
+ private @Nullable String deviceId;
@Column(name = "device_name")
- private String deviceName;
+ private @Nullable String deviceName;
@Column(name = "device_type")
@Enumerated(EnumType.STRING)
- private Device.Type deviceType;
+ private @Nullable Type deviceType;
- public String getRegisteredBy() {
+ public @Nullable String getRegisteredBy() {
return registeredBy;
}
- public void setRegisteredBy(String registeredBy) {
+ public void setRegisteredBy(@Nullable String registeredBy) {
this.registeredBy = registeredBy;
}
- public String getDeviceId() {
+ public @Nullable String getDeviceId() {
return deviceId;
}
- public void setDeviceId(String deviceId) {
+ public void setDeviceId(@Nullable String deviceId) {
this.deviceId = deviceId;
}
- public String getDeviceName() {
+ public @Nullable String getDeviceName() {
return deviceName;
}
- public void setDeviceName(String deviceName) {
+ public void setDeviceName(@Nullable String deviceName) {
this.deviceName = deviceName;
}
- public Device.Type getDeviceType() {
+ public @Nullable Type getDeviceType() {
return deviceType;
}
- public void setDeviceType(Device.Type deviceType) {
+ public void setDeviceType(@Nullable Type deviceType) {
this.deviceType = deviceType;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRemovedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRemovedEvent.java
index 71ef0bc0c..7358ef7fd 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRemovedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/DeviceRemovedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -15,24 +16,24 @@ public class DeviceRemovedEvent extends AuditEvent {
public static final String TYPE = "DEVICE_REMOVE";
@Column(name = "removed_by")
- private String removedBy;
+ private @Nullable String removedBy;
@Column(name = "device_id")
- private String deviceId;
+ private @Nullable String deviceId;
- public String getRemovedBy() {
+ public @Nullable String getRemovedBy() {
return removedBy;
}
- public void setRemovedBy(String removedBy) {
+ public void setRemovedBy(@Nullable String removedBy) {
this.removedBy = removedBy;
}
- public String getDeviceId() {
+ public @Nullable String getDeviceId() {
return deviceId;
}
- public void setDeviceId(String deviceId) {
+ public void setDeviceId(@Nullable String deviceId) {
this.deviceId = deviceId;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryAbortedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryAbortedEvent.java
index 87be806fb..8231d5db5 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryAbortedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryAbortedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -25,7 +26,7 @@ public class EmergencyAccessRecoveryAbortedEvent extends AuditEvent {
private String councilMemberId;
@Column(name = "ip_address")
- private String ipAddress;
+ private @Nullable String ipAddress;
public UUID getVaultId() {
return vaultId;
@@ -51,11 +52,11 @@ public void setCouncilMemberId(String councilMemberId) {
this.councilMemberId = councilMemberId;
}
- public String getIpAddress() {
+ public @Nullable String getIpAddress() {
return ipAddress;
}
- public void setIpAddress(String ipAddress) {
+ public void setIpAddress(@Nullable String ipAddress) {
this.ipAddress = ipAddress;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryCompletedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryCompletedEvent.java
index 0c5ec4c7c..7b042099f 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryCompletedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/EmergencyAccessRecoveryCompletedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -22,7 +23,7 @@ public class EmergencyAccessRecoveryCompletedEvent extends AuditEvent {
private String councilMemberId;
@Column(name = "ip_address")
- private String ipAddress;
+ private @Nullable String ipAddress;
public UUID getProcessId() {
return processId;
@@ -40,11 +41,11 @@ public void setCouncilMemberId(String councilMemberId) {
this.councilMemberId = councilMemberId;
}
- public String getIpAddress() {
+ public @Nullable String getIpAddress() {
return ipAddress;
}
- public void setIpAddress(String ipAddress) {
+ public void setIpAddress(@Nullable String ipAddress) {
this.ipAddress = ipAddress;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/EventLogger.java b/backend/src/main/java/org/cryptomator/hub/entities/events/EventLogger.java
index 15ea124e3..8e94b75c1 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/EventLogger.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/EventLogger.java
@@ -11,8 +11,12 @@
@ApplicationScoped
public class EventLogger {
+ private final AuditEvent.Repository auditEventRepository;
+
@Inject
- AuditEvent.Repository auditEventRepository;
+ EventLogger(AuditEvent.Repository auditEventRepository) {
+ this.auditEventRepository = auditEventRepository;
+ }
public void logVaultCreated(String createdBy, UUID vaultId, String vaultName, String vaultDescription) {
var event = new VaultCreatedEvent();
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/SettingWotUpdateEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/SettingWotUpdateEvent.java
index dd336c190..a30e6967a 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/SettingWotUpdateEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/SettingWotUpdateEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -15,7 +16,7 @@ public class SettingWotUpdateEvent extends AuditEvent {
public static final String TYPE = "SETTING_WOT_UPDATE";
@Column(name = "updated_by")
- private String updatedBy;
+ private @Nullable String updatedBy;
@Column(name = "wot_max_depth")
private int wotMaxDepth;
@@ -23,11 +24,11 @@ public class SettingWotUpdateEvent extends AuditEvent {
@Column(name = "wot_id_verify_len")
private int wotIdVerifyLen;
- public String getUpdatedBy() {
+ public @Nullable String getUpdatedBy() {
return updatedBy;
}
- public void setUpdatedBy(String updatedBy) {
+ public void setUpdatedBy(@Nullable String updatedBy) {
this.updatedBy = updatedBy;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/SignedWotIdEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/SignedWotIdEvent.java
index 4566619f9..e2ed7e2c4 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/SignedWotIdEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/SignedWotIdEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -15,46 +16,46 @@ public class SignedWotIdEvent extends AuditEvent {
public static final String TYPE = "SIGN_WOT_ID";
@Column(name = "user_id")
- private String userId;
+ private @Nullable String userId;
@Column(name = "signer_id")
- private String signerId;
+ private @Nullable String signerId;
@Column(name = "signer_key")
- private String signerKey;
+ private @Nullable String signerKey;
@Column(name = "signature")
- private String signature;
+ private @Nullable String signature;
- public String getUserId() {
+ public @Nullable String getUserId() {
return userId;
}
- public void setUserId(String userId) {
+ public void setUserId(@Nullable String userId) {
this.userId = userId;
}
- public String getSignerId() {
+ public @Nullable String getSignerId() {
return signerId;
}
- public void setSignerId(String signerId) {
+ public void setSignerId(@Nullable String signerId) {
this.signerId = signerId;
}
- public String getSignerKey() {
+ public @Nullable String getSignerKey() {
return signerKey;
}
- public void setSignerKey(String signerKey) {
+ public void setSignerKey(@Nullable String signerKey) {
this.signerKey = signerKey;
}
- public String getSignature() {
+ public @Nullable String getSignature() {
return signature;
}
- public void setSignature(String signature) {
+ public void setSignature(@Nullable String signature) {
this.signature = signature;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/UserAccountResetEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/UserAccountResetEvent.java
index 47e733f0d..f9f740580 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/UserAccountResetEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/UserAccountResetEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -15,13 +16,13 @@ public class UserAccountResetEvent extends AuditEvent {
public static final String TYPE = "USER_ACCOUNT_RESET";
@Column(name = "reset_by")
- private String resetBy;
+ private @Nullable String resetBy;
- public String getResetBy() {
+ public @Nullable String getResetBy() {
return resetBy;
}
- public void setResetBy(String resetBy) {
+ public void setResetBy(@Nullable String resetBy) {
this.resetBy = resetBy;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/UserKeysChangeEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/UserKeysChangeEvent.java
index 24ad18f3f..09014ec90 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/UserKeysChangeEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/UserKeysChangeEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -15,24 +16,24 @@ public class UserKeysChangeEvent extends AuditEvent {
public static final String TYPE = "USER_KEYS_CHANGE";
@Column(name = "changed_by")
- private String changedBy;
+ private @Nullable String changedBy;
@Column(name = "user_name")
- private String userName;
+ private @Nullable String userName;
- public String getChangedBy() {
+ public @Nullable String getChangedBy() {
return changedBy;
}
- public void setChangedBy(String changedBy) {
+ public void setChangedBy(@Nullable String changedBy) {
this.changedBy = changedBy;
}
- public String getUserName() {
+ public @Nullable String getUserName() {
return userName;
}
- public void setUserName(String userName) {
+ public void setUserName(@Nullable String userName) {
this.userName = userName;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/UserSetupCodeChangeEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/UserSetupCodeChangeEvent.java
index b846bb5ff..a1059fa5e 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/UserSetupCodeChangeEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/UserSetupCodeChangeEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
@@ -15,13 +16,13 @@ public class UserSetupCodeChangeEvent extends AuditEvent {
public static final String TYPE = "USER_SETUP_CODE_CHANGE";
@Column(name = "changed_by")
- private String changedBy;
+ private @Nullable String changedBy;
- public String getChangedBy() {
+ public @Nullable String getChangedBy() {
return changedBy;
}
- public void setChangedBy(String changedBy) {
+ public void setChangedBy(@Nullable String changedBy) {
this.changedBy = changedBy;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultAccessGrantedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultAccessGrantedEvent.java
index 4314e0025..4e4521548 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultAccessGrantedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultAccessGrantedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -16,35 +17,35 @@ public class VaultAccessGrantedEvent extends AuditEvent {
public static final String TYPE = "VAULT_ACCESS_GRANT";
@Column(name = "granted_by")
- private String grantedBy;
+ private @Nullable String grantedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "authority_id")
- private String authorityId;
+ private @Nullable String authorityId;
- public String getGrantedBy() {
+ public @Nullable String getGrantedBy() {
return grantedBy;
}
- public void setGrantedBy(String grantedBy) {
+ public void setGrantedBy(@Nullable String grantedBy) {
this.grantedBy = grantedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public String getAuthorityId() {
+ public @Nullable String getAuthorityId() {
return authorityId;
}
- public void setAuthorityId(String authorityId) {
+ public void setAuthorityId(@Nullable String authorityId) {
this.authorityId = authorityId;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultCreatedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultCreatedEvent.java
index 8a1e9863b..06924109f 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultCreatedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultCreatedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -16,46 +17,46 @@ public class VaultCreatedEvent extends AuditEvent {
public static final String TYPE = "VAULT_CREATE";
@Column(name = "created_by")
- private String createdBy;
+ private @Nullable String createdBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "vault_name")
- private String vaultName;
+ private @Nullable String vaultName;
@Column(name = "vault_description")
- private String vaultDescription;
+ private @Nullable String vaultDescription;
- public String getCreatedBy() {
+ public @Nullable String getCreatedBy() {
return createdBy;
}
- public void setCreatedBy(String createdBy) {
+ public void setCreatedBy(@Nullable String createdBy) {
this.createdBy = createdBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public String getVaultName() {
+ public @Nullable String getVaultName() {
return vaultName;
}
- public void setVaultName(String vaultName) {
+ public void setVaultName(@Nullable String vaultName) {
this.vaultName = vaultName;
}
- public String getVaultDescription() {
+ public @Nullable String getVaultDescription() {
return vaultDescription;
}
- public void setVaultDescription(String vaultDescription) {
+ public void setVaultDescription(@Nullable String vaultDescription) {
this.vaultDescription = vaultDescription;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultKeyRetrievedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultKeyRetrievedEvent.java
index 81f845a3b..ade3ce37c 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultKeyRetrievedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultKeyRetrievedEvent.java
@@ -6,6 +6,7 @@
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -18,58 +19,58 @@ public class VaultKeyRetrievedEvent extends AuditEvent {
public static final String TYPE = "VAULT_KEY_RETRIEVE";
@Column(name = "retrieved_by")
- private String retrievedBy;
+ private @Nullable String retrievedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "result")
@Enumerated(EnumType.STRING)
- private Result result;
+ private @Nullable Result result;
@Column(name = "ip_address")
- private String ipAddress;
+ private @Nullable String ipAddress;
@Column(name = "device_id")
- private String deviceId;
+ private @Nullable String deviceId;
- public String getRetrievedBy() {
+ public @Nullable String getRetrievedBy() {
return retrievedBy;
}
- public void setRetrievedBy(String retrievedBy) {
+ public void setRetrievedBy(@Nullable String retrievedBy) {
this.retrievedBy = retrievedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public Result getResult() {
+ public @Nullable Result getResult() {
return result;
}
- public void setResult(Result result) {
+ public void setResult(@Nullable Result result) {
this.result = result;
}
- public String getIpAddress() {
+ public @Nullable String getIpAddress() {
return ipAddress;
}
- public void setIpAddress(String ipAddress) {
+ public void setIpAddress(@Nullable String ipAddress) {
this.ipAddress = ipAddress;
}
- public String getDeviceId() {
+ public @Nullable String getDeviceId() {
return deviceId;
}
- public void setDeviceId(String device) {
+ public void setDeviceId(@Nullable String device) {
this.deviceId = device;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberAddedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberAddedEvent.java
index 0472f5593..43e411919 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberAddedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberAddedEvent.java
@@ -7,6 +7,7 @@
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import org.cryptomator.hub.entities.VaultAccess;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -19,39 +20,39 @@ public class VaultMemberAddedEvent extends AuditEvent {
public static final String TYPE = "VAULT_MEMBER_ADD";
@Column(name = "added_by")
- private String addedBy;
+ private @Nullable String addedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "authority_id")
- private String authorityId;
+ private @Nullable String authorityId;
@Column(name = "role", nullable = false)
@Enumerated(EnumType.STRING)
private VaultAccess.Role role;
- public String getAddedBy() {
+ public @Nullable String getAddedBy() {
return addedBy;
}
- public void setAddedBy(String addedBy) {
+ public void setAddedBy(@Nullable String addedBy) {
this.addedBy = addedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public String getAuthorityId() {
+ public @Nullable String getAuthorityId() {
return authorityId;
}
- public void setAuthorityId(String authorityId) {
+ public void setAuthorityId(@Nullable String authorityId) {
this.authorityId = authorityId;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberRemovedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberRemovedEvent.java
index de236c9c5..e7a823204 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberRemovedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberRemovedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -16,35 +17,35 @@ public class VaultMemberRemovedEvent extends AuditEvent {
public static final String TYPE = "VAULT_MEMBER_REMOVE";
@Column(name = "removed_by")
- private String removedBy;
+ private @Nullable String removedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "authority_id")
- private String authorityId;
+ private @Nullable String authorityId;
- public String getRemovedBy() {
+ public @Nullable String getRemovedBy() {
return removedBy;
}
- public void setRemovedBy(String removedBy) {
+ public void setRemovedBy(@Nullable String removedBy) {
this.removedBy = removedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public String getAuthorityId() {
+ public @Nullable String getAuthorityId() {
return authorityId;
}
- public void setAuthorityId(String authorityId) {
+ public void setAuthorityId(@Nullable String authorityId) {
this.authorityId = authorityId;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberUpdatedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberUpdatedEvent.java
index c5cf8c479..0337a4af6 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberUpdatedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultMemberUpdatedEvent.java
@@ -7,6 +7,7 @@
import jakarta.persistence.Enumerated;
import jakarta.persistence.Table;
import org.cryptomator.hub.entities.VaultAccess;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -19,39 +20,39 @@ public class VaultMemberUpdatedEvent extends AuditEvent {
public static final String TYPE = "VAULT_MEMBER_UPDATE";
@Column(name = "updated_by")
- private String updatedBy;
+ private @Nullable String updatedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "authority_id")
- private String authorityId;
+ private @Nullable String authorityId;
@Column(name = "role", nullable = false)
@Enumerated(EnumType.STRING)
private VaultAccess.Role role;
- public String getUpdatedBy() {
+ public @Nullable String getUpdatedBy() {
return updatedBy;
}
- public void setUpdatedBy(String updatedBy) {
+ public void setUpdatedBy(@Nullable String updatedBy) {
this.updatedBy = updatedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public String getAuthorityId() {
+ public @Nullable String getAuthorityId() {
return authorityId;
}
- public void setAuthorityId(String authorityId) {
+ public void setAuthorityId(@Nullable String authorityId) {
this.authorityId = authorityId;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultOwnershipClaimedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultOwnershipClaimedEvent.java
index a1344b312..b62953604 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultOwnershipClaimedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultOwnershipClaimedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -16,24 +17,24 @@ public class VaultOwnershipClaimedEvent extends AuditEvent {
public static final String TYPE = "VAULT_OWNERSHIP_CLAIM";
@Column(name = "claimed_by")
- private String claimedBy;
+ private @Nullable String claimedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
- public String getClaimedBy() {
+ public @Nullable String getClaimedBy() {
return claimedBy;
}
- public void setClaimedBy(String claimedBy) {
+ public void setClaimedBy(@Nullable String claimedBy) {
this.claimedBy = claimedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultUpdatedEvent.java b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultUpdatedEvent.java
index d6ea6c382..081ead968 100644
--- a/backend/src/main/java/org/cryptomator/hub/entities/events/VaultUpdatedEvent.java
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/VaultUpdatedEvent.java
@@ -4,6 +4,7 @@
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.Table;
+import org.jspecify.annotations.Nullable;
import java.util.Objects;
import java.util.UUID;
@@ -16,49 +17,49 @@ public class VaultUpdatedEvent extends AuditEvent {
public static final String TYPE = "VAULT_UPDATE";
@Column(name = "updated_by")
- private String updatedBy;
+ private @Nullable String updatedBy;
@Column(name = "vault_id")
- private UUID vaultId;
+ private @Nullable UUID vaultId;
@Column(name = "vault_name")
- private String vaultName;
+ private @Nullable String vaultName;
@Column(name = "vault_description")
- private String vaultDescription;
+ private @Nullable String vaultDescription;
@Column(name = "vault_archived")
private boolean vaultArchived;
- public String getUpdatedBy() {
+ public @Nullable String getUpdatedBy() {
return updatedBy;
}
- public void setUpdatedBy(String updatedBy) {
+ public void setUpdatedBy(@Nullable String updatedBy) {
this.updatedBy = updatedBy;
}
- public UUID getVaultId() {
+ public @Nullable UUID getVaultId() {
return vaultId;
}
- public void setVaultId(UUID vaultId) {
+ public void setVaultId(@Nullable UUID vaultId) {
this.vaultId = vaultId;
}
- public String getVaultName() {
+ public @Nullable String getVaultName() {
return vaultName;
}
- public void setVaultName(String vaultName) {
+ public void setVaultName(@Nullable String vaultName) {
this.vaultName = vaultName;
}
- public String getVaultDescription() {
+ public @Nullable String getVaultDescription() {
return vaultDescription;
}
- public void setVaultDescription(String vaultDescription) {
+ public void setVaultDescription(@Nullable String vaultDescription) {
this.vaultDescription = vaultDescription;
}
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/events/package-info.java b/backend/src/main/java/org/cryptomator/hub/entities/events/package-info.java
new file mode 100644
index 000000000..1f9717d17
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/entities/events/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.entities.events;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/entities/package-info.java b/backend/src/main/java/org/cryptomator/hub/entities/package-info.java
new file mode 100644
index 000000000..107d134c5
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/entities/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.entities;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/filters/ActiveLicenseFilter.java b/backend/src/main/java/org/cryptomator/hub/filters/ActiveLicenseFilter.java
index e695899bc..e3a491660 100644
--- a/backend/src/main/java/org/cryptomator/hub/filters/ActiveLicenseFilter.java
+++ b/backend/src/main/java/org/cryptomator/hub/filters/ActiveLicenseFilter.java
@@ -16,8 +16,12 @@
@ActiveLicense
public class ActiveLicenseFilter implements ContainerRequestFilter {
+ private final LicenseHolder license;
+
@Inject
- LicenseHolder license;
+ ActiveLicenseFilter(LicenseHolder license) {
+ this.license = license;
+ }
@Override
public void filter(ContainerRequestContext requestContext) {
diff --git a/backend/src/main/java/org/cryptomator/hub/filters/FrontendRootPathFilter.java b/backend/src/main/java/org/cryptomator/hub/filters/FrontendRootPathFilter.java
index bd126bd5b..ca70f5fd9 100644
--- a/backend/src/main/java/org/cryptomator/hub/filters/FrontendRootPathFilter.java
+++ b/backend/src/main/java/org/cryptomator/hub/filters/FrontendRootPathFilter.java
@@ -22,9 +22,12 @@
*/
public class FrontendRootPathFilter extends HttpFilter {
+ private final Provider publicRootPath;
+
@Inject
- @ConfigProperty(name = "hub.public-root-path", defaultValue = "")
- Provider publicRootPath;
+ FrontendRootPathFilter(@ConfigProperty(name = "hub.public-root-path", defaultValue = "") Provider publicRootPath) {
+ this.publicRootPath = publicRootPath;
+ }
@Override
protected void doFilter(HttpServletRequest req, HttpServletResponse res, FilterChain chain) throws IOException, ServletException {
diff --git a/backend/src/main/java/org/cryptomator/hub/filters/VaultRoleFilter.java b/backend/src/main/java/org/cryptomator/hub/filters/VaultRoleFilter.java
index b542f63b6..498e4fff5 100644
--- a/backend/src/main/java/org/cryptomator/hub/filters/VaultRoleFilter.java
+++ b/backend/src/main/java/org/cryptomator/hub/filters/VaultRoleFilter.java
@@ -30,21 +30,22 @@
@VaultRole
public class VaultRoleFilter implements ContainerRequestFilter {
- @Inject
- JsonWebToken jwt;
-
- @Inject
- EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
-
- @Inject
- EmergencyRecoveryProcess.Repository recoveryRepo;
-
- @Inject
- Vault.Repository vaultRepo;
+ private final JsonWebToken jwt;
+ private final EffectiveVaultAccess.Repository effectiveVaultAccessRepo;
+ private final EmergencyRecoveryProcess.Repository recoveryRepo;
+ private final Vault.Repository vaultRepo;
@Context
ResourceInfo resourceInfo;
+ @Inject
+ VaultRoleFilter(JsonWebToken jwt, EffectiveVaultAccess.Repository effectiveVaultAccessRepo, EmergencyRecoveryProcess.Repository recoveryRepo, Vault.Repository vaultRepo) {
+ this.jwt = jwt;
+ this.effectiveVaultAccessRepo = effectiveVaultAccessRepo;
+ this.recoveryRepo = recoveryRepo;
+ this.vaultRepo = vaultRepo;
+ }
+
@Override
public void filter(ContainerRequestContext requestContext) throws NotFoundException, ForbiddenException, NotAuthorizedException {
var annotation = resourceInfo.getResourceMethod().getAnnotation(VaultRole.class);
diff --git a/backend/src/main/java/org/cryptomator/hub/filters/package-info.java b/backend/src/main/java/org/cryptomator/hub/filters/package-info.java
new file mode 100644
index 000000000..f50dbb854
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/filters/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.filters;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/flyway/package-info.java b/backend/src/main/java/org/cryptomator/hub/flyway/package-info.java
new file mode 100644
index 000000000..68c646958
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/flyway/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.flyway;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProvider.java b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProvider.java
index b22f55dd3..226cf3cef 100644
--- a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProvider.java
+++ b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProvider.java
@@ -26,11 +26,14 @@ public class KeycloakAuthorityProvider {
//visible for testing
static final int MAX_COUNT_PER_REQUEST = 5_000;
- @Inject
- Keycloak keycloak;
+ private final Keycloak keycloak;
+ private final String keycloakRealm;
- @ConfigProperty(name = "hub.keycloak.realm")
- String keycloakRealm;
+ @Inject
+ KeycloakAuthorityProvider(Keycloak keycloak, @ConfigProperty(name = "hub.keycloak.realm") String keycloakRealm) {
+ this.keycloak = keycloak;
+ this.keycloakRealm = keycloakRealm;
+ }
public List users() {
return users(keycloak.realm(keycloakRealm));
diff --git a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPuller.java b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPuller.java
index 95d0e902d..bf84a21a3 100644
--- a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPuller.java
+++ b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPuller.java
@@ -46,26 +46,25 @@ public class KeycloakAuthorityPuller {
private static final Logger LOG = Logger.getLogger(KeycloakAuthorityPuller.class);
- @Inject
- Keycloak keycloak;
- @Inject
- User.Repository userRepo;
- @Inject
- Group.Repository groupRepo;
- @Inject
- KeycloakAuthorityProvider remoteUserProvider;
- @Inject
- EffectiveGroupMembership.Repository effectiveGroupMembershipRepo;
- @Inject
- KeycloakRealmRoles realmRoles;
-
- @ConfigProperty(name = "hub.keycloak.realm")
- String keycloakRealm;
-
+ private final Keycloak keycloak;
+ private final User.Repository userRepo;
+ private final Group.Repository groupRepo;
+ private final KeycloakAuthorityProvider remoteUserProvider;
+ private final EffectiveGroupMembership.Repository effectiveGroupMembershipRepo;
+ private final KeycloakRealmRoles realmRoles;
+
+ // visible for testing
RealmResource realm;
- @PostConstruct
- public void setup() {
+ @Inject
+ KeycloakAuthorityPuller(Keycloak keycloak, User.Repository userRepo, Group.Repository groupRepo, KeycloakAuthorityProvider remoteUserProvider, EffectiveGroupMembership.Repository effectiveGroupMembershipRepo, KeycloakRealmRoles realmRoles, @ConfigProperty(name = "hub.keycloak.realm") String keycloakRealm) {
+ this.keycloak = keycloak;
+ this.userRepo = userRepo;
+ this.groupRepo = groupRepo;
+ this.remoteUserProvider = remoteUserProvider;
+ this.effectiveGroupMembershipRepo = effectiveGroupMembershipRepo;
+ this.realmRoles = realmRoles;
+
this.realm = keycloak.realm(keycloakRealm);
}
@@ -397,7 +396,7 @@ public Group syncGroup(GroupResource groupResource, GroupRepresentation keycloak
}
var pictureUrl = KeycloakAuthorityProvider.parsePictureUrl(keycloakGroup.getAttributes());
- var dbMembers = userRepo.findByIds(memberIds).collect(Collectors.toMap(User::getId, Function.identity()));
+ var dbMembers = userRepo.streamByIds(memberIds).collect(Collectors.toMap(User::getId, Function.identity()));
applyGroup(dbGroup, keycloakGroup.getName(), pictureUrl, new HashSet<>(memberIds), dbMembers::get);
groupRepo.persist(dbGroup);
diff --git a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakGroupDto.java b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakGroupDto.java
index 3a849a890..25214cc9f 100644
--- a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakGroupDto.java
+++ b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakGroupDto.java
@@ -1,6 +1,8 @@
package org.cryptomator.hub.keycloak;
+import org.jspecify.annotations.Nullable;
+
import java.util.Set;
-public record KeycloakGroupDto(String id, String name, String pictureUrl, Set members) {
+public record KeycloakGroupDto(String id, String name, @Nullable String pictureUrl, Set members) {
}
diff --git a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakRealmRoles.java b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakRealmRoles.java
index 259a3fc06..51f3545ac 100644
--- a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakRealmRoles.java
+++ b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakRealmRoles.java
@@ -11,14 +11,16 @@
@ApplicationScoped
public class KeycloakRealmRoles {
- @Inject
- Keycloak keycloak;
-
- @ConfigProperty(name = "hub.keycloak.realm")
- String keycloakRealm;
-
+ private final Keycloak keycloak;
+ private final String keycloakRealm;
private final EnumMap cachedRoles = new EnumMap<>(RealmRole.class);
+ @Inject
+ KeycloakRealmRoles(Keycloak keycloak, @ConfigProperty(name = "hub.keycloak.realm") String keycloakRealm) {
+ this.keycloak = keycloak;
+ this.keycloakRealm = keycloakRealm;
+ }
+
public RoleRepresentation getRealmRole(RealmRole realmRole) {
return cachedRoles.computeIfAbsent(realmRole, this::fetchRealmRole);
}
diff --git a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakUserDto.java b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakUserDto.java
index d6417d96c..e79319ec1 100644
--- a/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakUserDto.java
+++ b/backend/src/main/java/org/cryptomator/hub/keycloak/KeycloakUserDto.java
@@ -1,4 +1,6 @@
package org.cryptomator.hub.keycloak;
-public record KeycloakUserDto(String id, String name, String email, String firstName, String lastName, String pictureUrl, boolean enabled) {
+import org.jspecify.annotations.Nullable;
+
+public record KeycloakUserDto(String id, String name, @Nullable String email, @Nullable String firstName, @Nullable String lastName, @Nullable String pictureUrl, boolean enabled) {
}
diff --git a/backend/src/main/java/org/cryptomator/hub/keycloak/package-info.java b/backend/src/main/java/org/cryptomator/hub/keycloak/package-info.java
new file mode 100644
index 000000000..bd876b0a6
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/keycloak/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.keycloak;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/license/HubLicenseEntitlements.java b/backend/src/main/java/org/cryptomator/hub/license/HubLicenseEntitlements.java
index 5a4f88b64..c1ae72147 100644
--- a/backend/src/main/java/org/cryptomator/hub/license/HubLicenseEntitlements.java
+++ b/backend/src/main/java/org/cryptomator/hub/license/HubLicenseEntitlements.java
@@ -3,6 +3,7 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.quarkus.runtime.annotations.RegisterForReflection;
+import org.jspecify.annotations.Nullable;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
@@ -14,9 +15,9 @@ public record HubLicenseEntitlements(@JsonProperty("seats") long seats,
@JsonProperty("auditLogRetentionDays") long auditLogRetentionDays,
@JsonProperty("emergencyAccessEnabled") boolean emergencyAccessEnabled,
@JsonProperty("keycloakAccessEnabled") boolean keycloakAccessEnabled,
- @JsonProperty("iosLicense") String iosLicense,
- @JsonProperty("androidLicense") String androidLicense,
- @JsonProperty("desktopLicense") String desktopLicense) {
+ @JsonProperty("iosLicense") @Nullable String iosLicense,
+ @JsonProperty("androidLicense") @Nullable String androidLicense,
+ @JsonProperty("desktopLicense") @Nullable String desktopLicense) {
/**
* Calculates the earliest point of time for audit log entries to still be retained.
@@ -26,7 +27,7 @@ public record HubLicenseEntitlements(@JsonProperty("seats") long seats,
public Instant auditLogRetentionThreshold() {
try {
return Instant.now().minus(auditLogRetentionDays(), ChronoUnit.DAYS).truncatedTo(ChronoUnit.DAYS);
- } catch (ArithmeticException e) {
+ } catch (ArithmeticException _) {
return Instant.MIN;
}
}
@@ -57,15 +58,15 @@ public HubLicenseEntitlements withKeycloakAccessEnabled(boolean keycloakAccessEn
return new HubLicenseEntitlements(this.seats, this.showTrialHint, this.auditLogRetentionDays, this.emergencyAccessEnabled, keycloakAccessEnabled, this.iosLicense, this.androidLicense, this.desktopLicense);
}
- public HubLicenseEntitlements withIosLicense(String iosLicense) {
+ public HubLicenseEntitlements withIosLicense(@Nullable String iosLicense) {
return new HubLicenseEntitlements(this.seats, this.showTrialHint, this.auditLogRetentionDays, this.emergencyAccessEnabled, this.keycloakAccessEnabled, iosLicense, this.androidLicense, this.desktopLicense);
}
- public HubLicenseEntitlements withAndroidLicense(String androidLicense) {
+ public HubLicenseEntitlements withAndroidLicense(@Nullable String androidLicense) {
return new HubLicenseEntitlements(this.seats, this.showTrialHint, this.auditLogRetentionDays, this.emergencyAccessEnabled, this.keycloakAccessEnabled, this.iosLicense, androidLicense, this.desktopLicense);
}
- public HubLicenseEntitlements withDesktopLicense(String desktopLicense) {
+ public HubLicenseEntitlements withDesktopLicense(@Nullable String desktopLicense) {
return new HubLicenseEntitlements(this.seats, this.showTrialHint, this.auditLogRetentionDays, this.emergencyAccessEnabled, this.keycloakAccessEnabled, this.iosLicense, this.androidLicense, desktopLicense);
}
diff --git a/backend/src/main/java/org/cryptomator/hub/license/LicenseHolder.java b/backend/src/main/java/org/cryptomator/hub/license/LicenseHolder.java
index 3b1142c3e..e585ad43c 100644
--- a/backend/src/main/java/org/cryptomator/hub/license/LicenseHolder.java
+++ b/backend/src/main/java/org/cryptomator/hub/license/LicenseHolder.java
@@ -39,39 +39,38 @@ public class LicenseHolder {
private static final Logger LOG = Logger.getLogger(LicenseHolder.class);
- @Inject
- @ConfigProperty(name = "hub.managed-instance", defaultValue = "false")
- Boolean managedInstance;
-
- @Inject
- @ConfigProperty(name = "hub.initial-id")
- Optional initialId;
-
- @Inject
- @ConfigProperty(name = "hub.initial-license")
- Optional initialLicenseToken;
-
- @Inject
- @ConfigProperty(name = "hub.managed-api-username")
- Optional managedApiUsername;
-
- @Inject
- @ConfigProperty(name = "hub.managed-api-password")
- Optional managedApiPassword;
+ private final Boolean managedInstance;
+ private final Optional initialId;
+ private final Optional initialLicenseToken;
+ private final Optional managedApiUsername;
+ private final Optional managedApiPassword;
+ private final LicenseValidator licenseValidator;
+ private final RandomSleeper randomSleeper;
+ private final Settings.Repository settingsRepo;
+ private final LicenseApi licenseApi;
- @Inject
- LicenseValidator licenseValidator;
-
- @Inject
- RandomSleeper randomSleeper;
+ private DecodedJWT license;
@Inject
- Settings.Repository settingsRepo;
-
- @RestClient
- LicenseApi licenseApi;
-
- private DecodedJWT license;
+ LicenseHolder(@ConfigProperty(name = "hub.managed-instance", defaultValue = "false") Boolean managedInstance,
+ @ConfigProperty(name = "hub.initial-id") Optional initialId,
+ @ConfigProperty(name = "hub.initial-license") Optional initialLicenseToken,
+ @ConfigProperty(name = "hub.managed-api-username") Optional managedApiUsername,
+ @ConfigProperty(name = "hub.managed-api-password") Optional managedApiPassword,
+ LicenseValidator licenseValidator,
+ RandomSleeper randomSleeper,
+ Settings.Repository settingsRepo,
+ @RestClient LicenseApi licenseApi) {
+ this.managedInstance = managedInstance;
+ this.initialId = initialId;
+ this.initialLicenseToken = initialLicenseToken;
+ this.managedApiUsername = managedApiUsername;
+ this.managedApiPassword = managedApiPassword;
+ this.licenseValidator = licenseValidator;
+ this.randomSleeper = randomSleeper;
+ this.settingsRepo = settingsRepo;
+ this.licenseApi = licenseApi;
+ }
@PostConstruct
void init() {
@@ -227,7 +226,7 @@ public void refreshLicense() throws IOException {
} catch (LicenseRefreshFailedException e) {
LOG.errorv("Failed to refresh license token. Request to {0} was answered with response code {1,number,integer}", refreshUrl, e.statusCode);
throw new IOException("Failed to refresh license token.", e);
- } catch (InterruptedException e) {
+ } catch (InterruptedException _) {
Thread.currentThread().interrupt();
throw new InterruptedIOException("License refresh was interrupted");
}
diff --git a/backend/src/main/java/org/cryptomator/hub/license/LicenseValidator.java b/backend/src/main/java/org/cryptomator/hub/license/LicenseValidator.java
index f78e862e9..e4a5e1c03 100644
--- a/backend/src/main/java/org/cryptomator/hub/license/LicenseValidator.java
+++ b/backend/src/main/java/org/cryptomator/hub/license/LicenseValidator.java
@@ -13,9 +13,12 @@ public class LicenseValidator {
private static final String[] REQUIRED_CLAIMS = {"jti", "sub", "iat", "exp", "seats"}; // TODO: eventually phase out "seats" claim in favor of "org.cryptomator.hub.entitlements"."seats", see https://github.com/cryptomator/hub/issues/391
+ private final JWTVerifier verifier;
+
@Inject
- @Named("licenseVerifier")
- JWTVerifier verifier;
+ LicenseValidator(@Named("licenseVerifier") JWTVerifier verifier) {
+ this.verifier = verifier;
+ }
/**
* Validates the token signature and whether it matches the Hub ID. It does NOT check the expiration date, though.
diff --git a/backend/src/main/java/org/cryptomator/hub/license/LicenseVerifierProducer.java b/backend/src/main/java/org/cryptomator/hub/license/LicenseVerifierProducer.java
index cb8a22720..3d3073334 100644
--- a/backend/src/main/java/org/cryptomator/hub/license/LicenseVerifierProducer.java
+++ b/backend/src/main/java/org/cryptomator/hub/license/LicenseVerifierProducer.java
@@ -6,6 +6,7 @@
import com.auth0.jwt.interfaces.JWTVerifier;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Produces;
+import jakarta.inject.Inject;
import jakarta.inject.Named;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@@ -38,8 +39,12 @@ public class LicenseVerifierProducer {
-----END CERTIFICATE-----
""";
- @ConfigProperty(name = "hub.license.chain.required-cn")
- String licenseChainRequiredCn;
+ private final String licenseChainRequiredCn;
+
+ @Inject
+ LicenseVerifierProducer(@ConfigProperty(name = "hub.license.chain.required-cn") String licenseChainRequiredCn) {
+ this.licenseChainRequiredCn = licenseChainRequiredCn;
+ }
@Produces
@ApplicationScoped
diff --git a/backend/src/main/java/org/cryptomator/hub/license/package-info.java b/backend/src/main/java/org/cryptomator/hub/license/package-info.java
new file mode 100644
index 000000000..6de6a13b4
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/license/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.license;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/metrics/package-info.java b/backend/src/main/java/org/cryptomator/hub/metrics/package-info.java
new file mode 100644
index 000000000..98dc49fff
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/metrics/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.metrics;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/package-info.java b/backend/src/main/java/org/cryptomator/hub/package-info.java
new file mode 100644
index 000000000..f177acfb7
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/util/package-info.java b/backend/src/main/java/org/cryptomator/hub/util/package-info.java
new file mode 100644
index 000000000..8baa1fc30
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/util/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.util;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/main/java/org/cryptomator/hub/validation/package-info.java b/backend/src/main/java/org/cryptomator/hub/validation/package-info.java
new file mode 100644
index 000000000..6ea25d33b
--- /dev/null
+++ b/backend/src/main/java/org/cryptomator/hub/validation/package-info.java
@@ -0,0 +1,4 @@
+@NullMarked
+package org.cryptomator.hub.validation;
+
+import org.jspecify.annotations.NullMarked;
diff --git a/backend/src/test/java/org/cryptomator/hub/api/ConfigResourceTest.java b/backend/src/test/java/org/cryptomator/hub/api/ConfigResourceTest.java
index 700a0e264..ce10fad7a 100644
--- a/backend/src/test/java/org/cryptomator/hub/api/ConfigResourceTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/api/ConfigResourceTest.java
@@ -1,19 +1,11 @@
package org.cryptomator.hub.api;
import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
public class ConfigResourceTest {
- ConfigResource configResource;
-
- @BeforeEach
- void init() {
- this.configResource = new ConfigResource();
- }
-
@ParameterizedTest
@CsvSource({"foobar,foo,baz,bazbar",
"foobar,baz,bar,foobar",
@@ -22,7 +14,7 @@ void init() {
"'',baz,bar,''"
})
void testReplacePrefix(String str, String prefix, String replacement, String expected) {
- String out = configResource.replacePrefix(str, prefix, replacement);
+ String out = ConfigResource.replacePrefix(str, prefix, replacement);
Assertions.assertEquals(expected, out);
}
@@ -35,7 +27,7 @@ void testReplacePrefix(String str, String prefix, String replacement, String exp
"/,''"
})
void testTrimTrailingSlash(String in, String expected) {
- String out = configResource.trimTrailingSlash(in);
+ String out = ConfigResource.trimTrailingSlash(in);
Assertions.assertEquals(expected, out);
}
diff --git a/backend/src/test/java/org/cryptomator/hub/filters/ActiveLicenseFilterTest.java b/backend/src/test/java/org/cryptomator/hub/filters/ActiveLicenseFilterTest.java
index 001a90f84..c85b14091 100644
--- a/backend/src/test/java/org/cryptomator/hub/filters/ActiveLicenseFilterTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/filters/ActiveLicenseFilterTest.java
@@ -3,7 +3,6 @@
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.core.Response;
import org.cryptomator.hub.license.LicenseHolder;
-import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentMatcher;
@@ -12,17 +11,13 @@
public class ActiveLicenseFilterTest {
ContainerRequestContext context = Mockito.mock(ContainerRequestContext.class);
- ActiveLicenseFilter filter = new ActiveLicenseFilter();
-
- @BeforeEach
- void setup() {
- filter.license = Mockito.mock(LicenseHolder.class);
- }
+ LicenseHolder license = Mockito.mock(LicenseHolder.class);
+ ActiveLicenseFilter filter = new ActiveLicenseFilter(license);
@Test
@DisplayName("abort when providing expired license")
void testFilterWithExpiredLicense() {
- Mockito.doReturn(true).when(filter.license).isExpired();
+ Mockito.doReturn(true).when(license).isExpired();
filter.filter(context);
@@ -32,7 +27,7 @@ void testFilterWithExpiredLicense() {
@Test
@DisplayName("continue when seats are still available")
void testDontFilterWhenLicenseIsNotExpired() {
- Mockito.doReturn(false).when(filter.license).isExpired();
+ Mockito.doReturn(false).when(license).isExpired();
filter.filter(context);
diff --git a/backend/src/test/java/org/cryptomator/hub/filters/VaultRoleFilterTest.java b/backend/src/test/java/org/cryptomator/hub/filters/VaultRoleFilterTest.java
index c30c71a69..6bbf40c07 100644
--- a/backend/src/test/java/org/cryptomator/hub/filters/VaultRoleFilterTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/filters/VaultRoleFilterTest.java
@@ -36,15 +36,11 @@ class VaultRoleFilterTest {
private final EffectiveVaultAccess.Repository effectiveVaultAccessRepo = Mockito.mock(EffectiveVaultAccess.Repository.class);
private final EmergencyRecoveryProcess.Repository recoveryRepo = Mockito.mock(EmergencyRecoveryProcess.Repository.class);
private final Vault.Repository vaultRepo = Mockito.mock(Vault.Repository.class);
- private final VaultRoleFilter filter = new VaultRoleFilter();
+ private final VaultRoleFilter filter = new VaultRoleFilter(jwt, effectiveVaultAccessRepo, recoveryRepo, vaultRepo);
@BeforeEach
void setup() {
filter.resourceInfo = resourceInfo;
- filter.jwt = jwt;
- filter.effectiveVaultAccessRepo = effectiveVaultAccessRepo;
- filter.recoveryRepo = recoveryRepo;
- filter.vaultRepo = vaultRepo;
Mockito.doReturn(uriInfo).when(context).getUriInfo();
Mockito.doReturn(securityContext).when(context).getSecurityContext();
diff --git a/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProviderTest.java b/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProviderTest.java
index 218db70a0..86e291011 100644
--- a/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProviderTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityProviderTest.java
@@ -55,7 +55,7 @@ void setUp() {
Mockito.when(user2.getEmail()).thenReturn("email3001");
Mockito.when(user2.isEnabled()).thenReturn(false);
- keycloakRemoteUserProvider = new KeycloakAuthorityProvider();
+ keycloakRemoteUserProvider = new KeycloakAuthorityProvider(null, null);
}
@Test
diff --git a/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPullerTest.java b/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPullerTest.java
index 7b3ad71b5..0568ba6b1 100644
--- a/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPullerTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/keycloak/KeycloakAuthorityPullerTest.java
@@ -22,6 +22,7 @@
import org.junit.jupiter.params.converter.ConvertWith;
import org.junit.jupiter.params.converter.SimpleArgumentConverter;
import org.junit.jupiter.params.provider.CsvSource;
+import org.keycloak.admin.client.Keycloak;
import org.keycloak.admin.client.resource.GroupResource;
import org.keycloak.admin.client.resource.GroupsResource;
import org.keycloak.admin.client.resource.RealmResource;
@@ -53,10 +54,12 @@
class KeycloakAuthorityPullerTest {
+ private final Keycloak keycloak = Mockito.mock(Keycloak.class);
private final KeycloakAuthorityProvider remoteUserProvider = Mockito.mock(KeycloakAuthorityProvider.class);
private final User.Repository userRepo = Mockito.mock(User.Repository.class);
private final Group.Repository groupRepo = Mockito.mock(Group.Repository.class);
private final EffectiveGroupMembership.Repository effectiveGroupMembershipRepo = Mockito.mock(EffectiveGroupMembership.Repository.class);
+ private final KeycloakRealmRoles realmRoles = Mockito.mock(KeycloakRealmRoles.class);
private final List persistedUsers = new ArrayList<>();
private final List persistedGroups = new ArrayList<>();
@@ -65,11 +68,7 @@ class KeycloakAuthorityPullerTest {
@BeforeEach
void setUp() {
- remoteUserPuller = new KeycloakAuthorityPuller();
- remoteUserPuller.remoteUserProvider = remoteUserProvider;
- remoteUserPuller.userRepo = userRepo;
- remoteUserPuller.groupRepo = groupRepo;
- remoteUserPuller.effectiveGroupMembershipRepo = effectiveGroupMembershipRepo;
+ remoteUserPuller = new KeycloakAuthorityPuller(keycloak, userRepo, groupRepo, remoteUserProvider, effectiveGroupMembershipRepo, realmRoles, "cryptomator");
persistedUsers.clear();
Mockito.doAnswer(invocation -> {
Iterable iterable = invocation.getArgument(0);
@@ -372,12 +371,10 @@ class WriteMethods {
private final RealmResource realm = Mockito.mock(RealmResource.class);
private final UsersResource usersResource = Mockito.mock(UsersResource.class);
private final GroupsResource groupsResource = Mockito.mock(GroupsResource.class);
- private final KeycloakRealmRoles realmRoles = Mockito.mock(KeycloakRealmRoles.class);
@BeforeEach
void setUp() {
remoteUserPuller.realm = realm;
- remoteUserPuller.realmRoles = realmRoles;
Mockito.lenient().when(realm.users()).thenReturn(usersResource);
Mockito.lenient().when(realm.groups()).thenReturn(groupsResource);
}
@@ -530,7 +527,7 @@ void testSyncGroupUpsertsAndInvalidates() {
Mockito.when(groupResource.toRepresentation()).thenReturn(representation);
Mockito.when(groupResource.members(0, KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST)).thenReturn(List.of());
Mockito.when(groupRepo.findById("g")).thenReturn(null);
- Mockito.when(userRepo.findByIds(Mockito.anyList())).thenReturn(Stream.of());
+ Mockito.when(userRepo.streamByIds(Mockito.anyList())).thenReturn(Stream.of());
var result = remoteUserPuller.syncGroup("g");
@@ -561,13 +558,13 @@ void testSyncGroupPaginatesMembers() {
Mockito.when(groupResource.members(0, KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST)).thenReturn(firstPage);
Mockito.when(groupResource.members(KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST, KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST)).thenReturn(List.of(lastMember));
Mockito.when(groupRepo.findById("g")).thenReturn(null);
- Mockito.when(userRepo.findByIds(Mockito.anyList())).thenReturn(Stream.of());
+ Mockito.when(userRepo.streamByIds(Mockito.anyList())).thenReturn(Stream.of());
remoteUserPuller.syncGroup("g");
Mockito.verify(groupResource).members(KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST, KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST);
var captor = ArgumentCaptor.forClass(List.class);
- Mockito.verify(userRepo).findByIds(captor.capture());
+ Mockito.verify(userRepo).streamByIds(captor.capture());
Assertions.assertEquals(KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST + 1, captor.getValue().size());
}
@@ -726,7 +723,7 @@ void testCreateGroupSuccess() {
Mockito.when(groupResource.toRepresentation()).thenReturn(representation);
Mockito.when(groupResource.members(0, KeycloakAuthorityProvider.MAX_COUNT_PER_REQUEST)).thenReturn(List.of());
Mockito.when(groupRepo.findById("newGroup")).thenReturn(null);
- Mockito.when(userRepo.findByIds(Mockito.anyList())).thenReturn(Stream.of());
+ Mockito.when(userRepo.streamByIds(Mockito.anyList())).thenReturn(Stream.of());
var result = remoteUserPuller.createGroup("New Group", null);
diff --git a/backend/src/test/java/org/cryptomator/hub/license/LicenseHolderTest.java b/backend/src/test/java/org/cryptomator/hub/license/LicenseHolderTest.java
index d8210de77..75c5487e2 100644
--- a/backend/src/test/java/org/cryptomator/hub/license/LicenseHolderTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/license/LicenseHolderTest.java
@@ -38,13 +38,11 @@ public class LicenseHolderTest {
@BeforeEach
public void resetTestclass() {
- licenseHolder = new LicenseHolder();
- licenseHolder.licenseValidator = validator;
- licenseHolder.settingsRepo = settingsRepo;
- licenseHolder.randomSleeper = randomSleeper;
- licenseHolder.licenseApi = licenseApi;
- licenseHolder.managedApiUsername = Optional.empty();
- licenseHolder.managedApiPassword = Optional.empty();
+ licenseHolder = buildLicenseHolder(Optional.empty(), Optional.empty());
+ }
+
+ private LicenseHolder buildLicenseHolder(Optional initialId, Optional initialLicenseToken) {
+ return new LicenseHolder(false, initialId, initialLicenseToken, Optional.empty(), Optional.empty(), validator, randomSleeper, settingsRepo, licenseApi);
}
@Nested
@@ -65,8 +63,7 @@ void setup() {
@DisplayName("call validateExistingLicense(), if DB contains existing token")
void testValidateExistingLicense() {
//to show check, that db has higher precedence
- licenseHolderSpy.initialId = Optional.of("43");
- licenseHolderSpy.initialLicenseToken = Optional.of("initToken");
+ licenseHolderSpy = Mockito.spy(buildLicenseHolder(Optional.of("43"), Optional.of("initToken")));
when(settings.getLicenseKey()).thenReturn("token");
when(settings.getHubId()).thenReturn("42");
var license = Mockito.mock(DecodedJWT.class);
@@ -87,8 +84,7 @@ void testValidateExistingLicense() {
"null, 42"
}, nullValues = {"null"})
void testApplyInitLicense(String dbToken, String dbHubId) {
- licenseHolderSpy.initialLicenseToken = Optional.of("token");
- licenseHolderSpy.initialId = Optional.of("43");
+ licenseHolderSpy = Mockito.spy(buildLicenseHolder(Optional.of("43"), Optional.of("token")));
when(settings.getLicenseKey()).thenReturn(dbToken);
when(settings.getHubId()).thenReturn(dbHubId);
var license = Mockito.mock(DecodedJWT.class);
@@ -109,8 +105,7 @@ void testApplyInitLicense(String dbToken, String dbHubId) {
"dbToken, null, initToken, null"
}, nullValues = {"null"})
void testRequestTrialLicense(String dbToken, String dbHubId, String initToken, String initId) {
- licenseHolderSpy.initialLicenseToken = Optional.ofNullable(initToken);
- licenseHolderSpy.initialId = Optional.ofNullable(initId);
+ licenseHolderSpy = Mockito.spy(buildLicenseHolder(Optional.ofNullable(initId), Optional.ofNullable(initToken)));
when(settings.getLicenseKey()).thenReturn(dbToken);
when(settings.getHubId()).thenReturn(dbHubId);
var license = Mockito.mock(DecodedJWT.class);
@@ -126,8 +121,7 @@ void testRequestTrialLicense(String dbToken, String dbHubId, String initToken, S
@DisplayName("requestAnonTrialLicense() fails when server doesn't respond as expected")
@Test
void testFailingRequestTrialLicense() {
- licenseHolderSpy.initialLicenseToken = Optional.empty();
- licenseHolderSpy.initialId = Optional.empty();
+ licenseHolderSpy = Mockito.spy(buildLicenseHolder(Optional.empty(), Optional.empty()));
doReturn(null).when(settings).getLicenseKey();
doReturn(null).when(settings).getHubId();
doCallRealMethod().when(licenseHolderSpy).requestAnonTrialLicense(Mockito.any());
diff --git a/backend/src/test/java/org/cryptomator/hub/license/LicenseValidatorTest.java b/backend/src/test/java/org/cryptomator/hub/license/LicenseValidatorTest.java
index aa9ad6350..33f2d7802 100644
--- a/backend/src/test/java/org/cryptomator/hub/license/LicenseValidatorTest.java
+++ b/backend/src/test/java/org/cryptomator/hub/license/LicenseValidatorTest.java
@@ -32,13 +32,12 @@ class LicenseValidatorTest {
private static final String LEAF_CERTIFICATE = "MIICDDCCAb6gAwIBAgIUDMOzxJY381JjdN82cW7qd+B3vh0wBQYDK2VwME4xCzAJBgNVBAYTAkRFMRYwFAYDVQQKDA1Ta3ltYXRpYyBHbWJIMScwJQYDVQQDDB5MaWNlbnNlIEludGVybWVkaWF0ZSBDQSAoVGVzdCkwHhcNMjYwMjI2MTU0MDM2WhcNMzYwMjI0MTU0MDM2WjBIMQswCQYDVQQGEwJERTEWMBQGA1UECgwNU2t5bWF0aWMgR21iSDEhMB8GA1UEAwwYTGljZW5zZSBJc3N1ZXIgQ0EgKFRlc3QpMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBiUVQ0HhZMuAOqiO2lPIT+MMSH4bcl6BOWnFn205bzTcRI9RuRdtrXVNwp/IPtjMVXTj/oW0r12HcrEdLmi9QI6QASTEByWLNTS/d94IoXmRYQTnC+RtH+H/4I1TWYw90aiig2yV0G1s0qCgAiyKswj+ST6r71NM/gepmlW3+qiv9/PWjQjBAMB0GA1UdDgQWBBSNjBwv+/iYQvpOOqz02u7xaASSITAfBgNVHSMEGDAWgBRMASsp1kiawJm8YoJ6+8/sq21X4zAFBgMrZXADQQA4Ok/+y0bdzm2RUmkHd6QFS6WbBKf9O4zz3Uc7iBMpKIq1kBlq+7TbbgMHJu+aYbODcRWT++5sx4i2OspkgOsJ";
private static final String LEAF_PRIVATE_KEY = "MGACAQAwEAYHKoZIzj0CAQYFK4EEACMESTBHAgEBBEIA+tBtqmK6OyXS+0ATPadXIF3mf1uwAY/ujIbhtox+dcqolusy8fR8cIVYNqbRb8wUZvbY++xn24nsDAiw6Za4NTg=";
- LicenseValidator validator = new LicenseValidator();
+ LicenseValidator validator;
@BeforeEach
void setup() {
- var verifierProducer = new LicenseVerifierProducer();
- verifierProducer.licenseChainRequiredCn = "";
- validator.verifier = verifierProducer.produceLicenseVerifier(ROOT_CERTIFICATE, INTERMEDIATE_CN);
+ var verifierProducer = new LicenseVerifierProducer("");
+ validator = new LicenseValidator(verifierProducer.produceLicenseVerifier(ROOT_CERTIFICATE, INTERMEDIATE_CN));
}
@Test
@@ -112,8 +111,8 @@ void testValidateTokenWithX5cCertificateChain() {
@Test
@DisplayName("reject token with invalid x5c certificate chain")
void testRejectTokenWithInvalidX5cCertificateChain() {
- var verifierProducer = new LicenseVerifierProducer();
- validator.verifier = verifierProducer.produceLicenseVerifier(ROOT_CERTIFICATE, INTERMEDIATE_CN);
+ var verifierProducer = new LicenseVerifierProducer("");
+ validator = new LicenseValidator(verifierProducer.produceLicenseVerifier(ROOT_CERTIFICATE, INTERMEDIATE_CN));
var token = JWT.create()
.withHeader(Map.of("x5c", List.of(LEAF_CERTIFICATE)))
.withJWTId("42")
@@ -145,8 +144,8 @@ void testValidateTokenWithMatchingIntermediateCn() {
@Test
@DisplayName("reject token with mismatching intermediate cert cn")
void testRejectTokenWithMismatchingIntermediateCn() {
- var verifierProducer = new LicenseVerifierProducer();
- validator.verifier = verifierProducer.produceLicenseVerifier(ROOT_CERTIFICATE, "some other CN");
+ var verifierProducer = new LicenseVerifierProducer("");
+ validator = new LicenseValidator(verifierProducer.produceLicenseVerifier(ROOT_CERTIFICATE, "some other CN"));
var token = JWT.create()
.withHeader(Map.of("x5c", List.of(LEAF_CERTIFICATE, INTERMEDIATE_CERTIFICATE)))
.withJWTId("42")
diff --git a/backend/src/test/resources/org/cryptomator/hub/flyway/V9999__Test_Data.sql b/backend/src/test/resources/org/cryptomator/hub/flyway/V9999__Test_Data.sql
index 2f3ee4da9..85fab8afd 100644
--- a/backend/src/test/resources/org/cryptomator/hub/flyway/V9999__Test_Data.sql
+++ b/backend/src/test/resources/org/cryptomator/hub/flyway/V9999__Test_Data.sql
@@ -71,7 +71,8 @@ INSERT INTO "access_token" ("user_id", "vault_id", "vault_masterkey")
VALUES
('user1', '7E57C0DE-0000-4000-8000-000100001111', 'jwe.jwe.jwe.vault1.user1'), -- direct access
('user2', '7E57C0DE-0000-4000-8000-000100001111', 'jwe.jwe.jwe.vault1.user2'), -- direct access
- ('user1', '7E57C0DE-0000-4000-8000-000100002222', 'jwe.jwe.jwe.vault2.user1'); -- access via group1
+ ('user1', '7E57C0DE-0000-4000-8000-000100002222', 'jwe.jwe.jwe.vault2.user1'), -- access via group1
+ ('user1', '7E57C0DE-0000-4000-8000-00010000AAAA', 'jwe.jwe.jwe.vaultAAA.user1'); -- direct access to archived vault
-- DEPRECATED:
INSERT INTO "device_legacy" ("id", "owner_id", "name", "type", "publickey", "creation_time")