Skip to content

Commit 91e8970

Browse files
Merge branch 'develop' into feature/non-null-licensekey
# Conflicts: # backend/src/main/java/org/cryptomator/hub/api/AuditLogResource.java # backend/src/main/java/org/cryptomator/hub/license/LicenseHolder.java # backend/src/test/java/org/cryptomator/hub/license/LicenseHolderTest.java
2 parents 9e4ccb0 + dd9a51f commit 91e8970

6 files changed

Lines changed: 64 additions & 51 deletions

File tree

backend/src/main/java/org/cryptomator/hub/api/AuditLogResource.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
@Path("/auditlog")
4545
public class AuditLogResource {
4646

47+
private static final Set<String> EVENT_TYPES = Set.of(DeviceRegisteredEvent.TYPE, DeviceRemovedEvent.TYPE, UserAccountResetEvent.TYPE, UserKeysChangeEvent.TYPE, UserSetupCodeChangeEvent.TYPE,
48+
SettingWotUpdateEvent.TYPE, SignedWotIdEvent.TYPE, VaultCreatedEvent.TYPE, VaultUpdatedEvent.TYPE, VaultAccessGrantedEvent.TYPE,
49+
VaultKeyRetrievedEvent.TYPE, VaultMemberAddedEvent.TYPE, VaultMemberRemovedEvent.TYPE, VaultMemberUpdatedEvent.TYPE, VaultOwnershipClaimedEvent.TYPE);
50+
4751
@Inject
4852
AuditEvent.Repository auditEventRepo;
4953
@Inject
@@ -81,17 +85,16 @@ public List<AuditEventDto> getAllEvents(@QueryParam("startDate") Instant startDa
8185
throw new BadRequestException("pageSize must be between 1 and 100");
8286
} else if (type == null) {
8387
throw new BadRequestException("type must be specified");
84-
} else if (!type.isEmpty()) {
85-
var validTypes = Set.of(DeviceRegisteredEvent.TYPE, DeviceRemovedEvent.TYPE, UserAccountResetEvent.TYPE, UserKeysChangeEvent.TYPE, UserSetupCodeChangeEvent.TYPE,
86-
SettingWotUpdateEvent.TYPE, SignedWotIdEvent.TYPE, VaultCreatedEvent.TYPE, VaultUpdatedEvent.TYPE, VaultAccessGrantedEvent.TYPE,
87-
VaultKeyRetrievedEvent.TYPE, VaultMemberAddedEvent.TYPE, VaultMemberRemovedEvent.TYPE, VaultMemberUpdatedEvent.TYPE, VaultOwnershipClaimedEvent.TYPE);
88-
if (!validTypes.containsAll(type)) {
89-
throw new BadRequestException("Invalid event type provided");
90-
}
9188
} else if (paginationId == null) {
9289
throw new BadRequestException("paginationId must be specified");
9390
}
9491

92+
if (!type.isEmpty()) {
93+
if (!EVENT_TYPES.containsAll(type)) {
94+
throw new BadRequestException("Invalid event type provided");
95+
}
96+
}
97+
9598
// cut off startDate at retention threshold
9699
startDate = startDate.isBefore(retentionThreshold) ? retentionThreshold : startDate;
97100

backend/src/main/java/org/cryptomator/hub/license/LicenseHolder.java

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.auth0.jwt.exceptions.JWTVerificationException;
44
import com.auth0.jwt.interfaces.DecodedJWT;
55
import io.quarkus.scheduler.Scheduled;
6+
import io.smallrye.common.annotation.RunOnVirtualThread;
67
import jakarta.enterprise.context.ApplicationScoped;
78
import jakarta.inject.Inject;
89
import jakarta.transaction.Transactional;
@@ -23,6 +24,7 @@
2324
import java.security.MessageDigest;
2425
import java.security.NoSuchAlgorithmException;
2526
import java.time.Instant;
27+
import java.time.temporal.ChronoUnit;
2628
import java.util.HexFormat;
2729
import java.util.Optional;
2830

@@ -47,7 +49,7 @@ public class LicenseHolder {
4749
LicenseValidator licenseValidator;
4850

4951
@Inject
50-
RandomMinuteSleeper randomMinuteSleeper;
52+
RandomSleeper randomSleeper;
5153

5254
@Inject
5355
Settings.Repository settingsRepo;
@@ -154,23 +156,27 @@ public void set(String token) throws JWTVerificationException {
154156
* Attempts to refresh the Hub licence every day between 01:00:00 and 02:00:00 AM UTC if claim refreshURL is present.
155157
*/
156158
@Scheduled(cron = "0 0 1 * * ?", timeZone = "UTC", concurrentExecution = Scheduled.ConcurrentExecution.SKIP)
157-
void refreshLicense() throws InterruptedException {
158-
if (get() != null) {
159-
randomMinuteSleeper.sleep(); // add random sleep between [0,59]min to reduce infrastructure load
160-
var refreshUrlClaim = get().getClaim("refreshUrl");
161-
if (refreshUrlClaim != null) {
162-
try {
163-
var refreshUrl = URI.create(refreshUrlClaim.asString());
164-
var refreshedLicense = requestLicenseRefresh(refreshUrl, get().getToken());
165-
set(refreshedLicense);
166-
} catch (LicenseRefreshFailedException e) {
167-
LOG.errorv("Failed to refresh license token. Request to {0} was answered with response code {1,number,integer}", refreshUrlClaim, e.statusCode);
168-
} catch (IllegalArgumentException | IOException e) {
169-
LOG.error("Failed to refresh license token", e);
170-
} catch (JWTVerificationException e) {
171-
LOG.error("Failed to refresh license token. Refreshed token is invalid.", e);
172-
}
173-
}
159+
@RunOnVirtualThread
160+
void refreshLicense() {
161+
var refreshUrlClaim = get().getClaim("refreshUrl");
162+
if (refreshUrlClaim.isMissing()) {
163+
LOG.error("Missing refreshUrl claim.");
164+
return;
165+
}
166+
try {
167+
randomSleeper.sleep(0, 59, ChronoUnit.MINUTES); // add random sleep to reduce infrastructure load
168+
var refreshUrl = URI.create(refreshUrlClaim.asString());
169+
var refreshedLicense = requestLicenseRefresh(refreshUrl, get().getToken());
170+
set(refreshedLicense);
171+
} catch (LicenseRefreshFailedException e) {
172+
LOG.errorv("Failed to refresh license token. Request to {0} was answered with response code {1,number,integer}", refreshUrlClaim, e.statusCode);
173+
} catch (IllegalArgumentException | IOException e) {
174+
LOG.error("Failed to refresh license token", e);
175+
} catch (JWTVerificationException e) {
176+
LOG.error("Failed to refresh license token. Refreshed token is invalid.", e);
177+
} catch (InterruptedException e) {
178+
Thread.currentThread().interrupt();
179+
LOG.warn("License refresh was interrupted", e);
174180
}
175181
}
176182

backend/src/main/java/org/cryptomator/hub/license/LicenseValidator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ public DecodedJWT validate(String token, String expectedHubId) throws JWTVerific
3232
throw new InvalidClaimException("Token ID " + jwt.getId() + " does not match your Hub ID " + expectedHubId);
3333
}
3434
for (var claim : REQUIRED_CLAIMS) {
35-
if (Objects.isNull(jwt.getClaim(claim))) {
35+
if (jwt.getClaim(claim).isMissing()) {
3636
throw new InvalidClaimException("The claim " + claim + " is required, but not present.");
3737
}
3838
}

backend/src/main/java/org/cryptomator/hub/license/RandomMinuteSleeper.java

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.cryptomator.hub.license;
2+
3+
import jakarta.enterprise.context.ApplicationScoped;
4+
5+
import java.time.Duration;
6+
import java.time.temporal.TemporalUnit;
7+
import java.util.random.RandomGenerator;
8+
9+
@ApplicationScoped
10+
public class RandomSleeper {
11+
12+
private final RandomGenerator rng;
13+
14+
public RandomSleeper() {
15+
this.rng = RandomGenerator.getDefault();
16+
}
17+
18+
void sleep(int min, int max, TemporalUnit unit) throws InterruptedException {
19+
var delay = Duration.of(rng.nextInt(min, max), unit);
20+
Thread.sleep(delay.toMillis());
21+
}
22+
23+
}

backend/src/test/java/org/cryptomator/hub/license/LicenseHolderTest.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
public class LicenseHolderTest {
3131

3232
Settings.Repository settingsRepo = mock(Settings.Repository.class);
33-
RandomMinuteSleeper randomMinuteSleeper = mock(RandomMinuteSleeper.class);
33+
RandomSleeper randomSleeper = mock(RandomSleeper.class);
3434
LicenseValidator validator = mock(LicenseValidator.class);
3535
LicenseApi licenseApi = mock(LicenseApi.class);
3636

@@ -41,7 +41,7 @@ public void resetTestclass() {
4141
licenseHolder = new LicenseHolder();
4242
licenseHolder.licenseValidator = validator;
4343
licenseHolder.settingsRepo = settingsRepo;
44-
licenseHolder.randomMinuteSleeper = randomMinuteSleeper;
44+
licenseHolder.randomSleeper = randomSleeper;
4545
licenseHolder.licenseApi = licenseApi;
4646
}
4747

@@ -221,7 +221,7 @@ class TestSetter {
221221

222222
@BeforeEach
223223
void setup() throws InterruptedException {
224-
Mockito.doNothing().when(randomMinuteSleeper).sleep();
224+
Mockito.doNothing().when(randomSleeper).sleep(Mockito.anyInt(), Mockito.anyInt(), Mockito.any());
225225
}
226226

227227
@Test
@@ -280,7 +280,9 @@ void setup() {
280280
@Test
281281
@DisplayName("If license does not have a refreshUrl, skip refresh")
282282
void testRefreshLicenseNoRefreshURL() throws InterruptedException, IOException {
283-
Mockito.doReturn(null).when(licenseJwt).getClaim("refreshUrl");
283+
var missingClaim = mock(Claim.class);
284+
Mockito.doReturn(true).when(missingClaim).isMissing();
285+
Mockito.doReturn(missingClaim).when(licenseJwt).getClaim("refreshUrl");
284286

285287
licenseHolderSpy.refreshLicense();
286288

0 commit comments

Comments
 (0)