Skip to content

Commit

Permalink
Merge branch 'develop' into feature/quarkus-3.8
Browse files Browse the repository at this point in the history
  • Loading branch information
overheadhunter authored Apr 26, 2024
2 parents dce2c89 + ce0e65c commit 45be869
Show file tree
Hide file tree
Showing 20 changed files with 962 additions and 845 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public class AuditLogResource {
@APIResponse(responseCode = "200", description = "Body contains list of events in the specified time interval")
@APIResponse(responseCode = "400", description = "startDate or endDate not specified, startDate > endDate, order specified and not in ['asc','desc'] or pageSize not in [1 .. 100]")
@APIResponse(responseCode = "402", description = "Community license used or license expired")
@APIResponse(responseCode = "403", description = "requesting user is does not have admin role")
@APIResponse(responseCode = "403", description = "requesting user does not have admin role")
public List<AuditEventDto> getAllEvents(@QueryParam("startDate") Instant startDate, @QueryParam("endDate") Instant endDate, @QueryParam("paginationId") Long paginationId, @QueryParam("order") @DefaultValue("desc") String order, @QueryParam("pageSize") @DefaultValue("20") int pageSize) {
if (!license.isSet() || license.isExpired()) {
throw new PaymentRequiredException("Community license used or license expired");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.time.Instant;
import java.util.Optional;

//TODO: redirect ot /license path
@Path("/billing")
public class BillingResource {

Expand Down Expand Up @@ -69,7 +70,7 @@ public record BillingDto(@JsonProperty("hubId") String hubId, @JsonProperty("has
@JsonProperty("issuedAt") Instant issuedAt, @JsonProperty("expiresAt") Instant expiresAt, @JsonProperty("managedInstance") Boolean managedInstance) {

public static BillingDto create(String hubId, LicenseHolder licenseHolder) {
var licensedSeats = licenseHolder.getNoLicenseSeats();
var licensedSeats = licenseHolder.getSeats();
var usedSeats = EffectiveVaultAccess.countSeatOccupyingUsers();
var managedInstance = licenseHolder.isManagedInstance();
return new BillingDto(hubId, false, null, (int) licensedSeats, (int) usedSeats, null, null, managedInstance);
Expand Down
49 changes: 49 additions & 0 deletions backend/src/main/java/org/cryptomator/hub/api/LicenseResource.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.cryptomator.hub.api;

import com.auth0.jwt.interfaces.DecodedJWT;
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import org.cryptomator.hub.entities.EffectiveVaultAccess;
import org.cryptomator.hub.license.LicenseHolder;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;

import java.time.Instant;
import java.util.Optional;

@Path("/license")
public class LicenseResource {

@Inject
LicenseHolder licenseHolder;

@GET
@Path("/user-info")
@Produces(MediaType.APPLICATION_JSON)
@RolesAllowed("user")
@Operation(summary = "Get license information for regular users", description = "Information includes the licensed seats, the already used seats and if defined, the license expiration date.")
@APIResponse(responseCode = "200")
public LicenseUserInfoDto get() {
return LicenseUserInfoDto.create(licenseHolder);
}


public record LicenseUserInfoDto(@JsonProperty("licensedSeats") Integer licensedSeats,
@JsonProperty("usedSeats") Integer usedSeats,
@JsonProperty("expiresAt") Instant expiresAt) {

public static LicenseUserInfoDto create(LicenseHolder licenseHolder) {
var licensedSeats = (int) licenseHolder.getSeats();
var usedSeats = (int) EffectiveVaultAccess.countSeatOccupyingUsers();
var expiresAt = Optional.ofNullable(licenseHolder.get()).map(DecodedJWT::getExpiresAtAsInstant).orElse(null);
return new LicenseUserInfoDto(licensedSeats, usedSeats, expiresAt);
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ public Response addUser(@PathParam("vaultId") UUID vaultId, @PathParam("userId")
}
//otherwise block
throw new PaymentRequiredException("Number of effective vault users greater than or equal to the available license seats");

}

@PUT
Expand Down Expand Up @@ -334,12 +333,8 @@ public Response unlock(@PathParam("vaultId") UUID vaultId, @QueryParam("evenIfAr
@APIResponse(responseCode = "402", description = "number of users granted access exceeds available license seats")
@APIResponse(responseCode = "403", description = "not a vault owner")
@APIResponse(responseCode = "404", description = "at least one user has not been found")
@APIResponse(responseCode = "410", description = "vault is archived")
public Response grantAccess(@PathParam("vaultId") UUID vaultId, @NotEmpty Map<String, String> tokens) {
var vault = Vault.<Vault>findById(vaultId); // should always be found, since @VaultRole filter would have triggered
if (vault.archived) {
throw new GoneException("Vault is archived.");
}

// check number of available seats
long occupiedSeats = EffectiveVaultAccess.countSeatOccupyingUsers();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ void applyInitialHubIdAndLicense(String initialId, String initialLicense) {
settings.hubId = initialId;
settings.persistAndFlush();
} catch (JWTVerificationException e) {
LOG.warn("Provided initial license is invalid.");
LOG.warn("Provided initial license is invalid.", e);
}
}

Expand Down Expand Up @@ -176,10 +176,11 @@ public long getSeats() {
return Optional.ofNullable(license) //
.map(l -> l.getClaim("seats")) //
.map(Claim::asLong) //
.orElseGet(this::getNoLicenseSeats);
.orElseGet(this::seatsOnNotExisingLicense);
}

public long getNoLicenseSeats() {
//visible for testing
public long seatsOnNotExisingLicense() {
if (!managedInstance) {
return SelfHostedNoLicenseConstants.SEATS;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@ public class AsAdmin {
@DisplayName("GET /billing returns 200 with empty license self-hosted")
public void testGetEmptySelfHosted() {
Mockito.when(licenseHolder.get()).thenReturn(null);
Mockito.when(licenseHolder.getNoLicenseSeats()).thenReturn(5L);
Mockito.when(licenseHolder.getSeats()).thenReturn(3L);
Mockito.when(licenseHolder.getSeats()).thenReturn(5L);
when().get("/billing")
.then().statusCode(200)
.body("hubId", is("42"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,10 +162,10 @@ public void testUnlockArchived2() {
}

@Test
@DisplayName("GET /vaults/7E57C0DE-0000-4000-8000-00010000AAAA/access-token returns 403 for archived vaults with evenIfArchived set to true")
@DisplayName("GET /vaults/7E57C0DE-0000-4000-8000-00010000AAAA/access-token returns 200 for archived vaults with evenIfArchived set to true")
public void testUnlockArchived3() throws SQLException {
when().get("/vaults/{vaultId}/access-token?evenIfArchived=true", "7E57C0DE-0000-4000-8000-00010000AAAA")
.then().statusCode(403);
.then().statusCode(200);
}

@Nested
Expand Down Expand Up @@ -378,11 +378,11 @@ public void testGrantAccess5() {
}

@Test
@DisplayName("POST /vaults/7E57C0DE-0000-4000-8000-00010000AAAA/access-tokens returns 410")
@DisplayName("POST /vaults/7E57C0DE-0000-4000-8000-00010000AAAA/access-tokens returns 200 for user1 and vault archived")
public void testGrantAccessArchived() {
given().contentType(ContentType.JSON).body(Map.of("user1", "jwe.jwe.jwe.vaultAAA.user1"))
.when().post("/vaults/{vaultId}/access-tokens/", "7E57C0DE-0000-4000-8000-00010000AAAA")
.then().statusCode(410);
.then().statusCode(200);
}

}
Expand Down
Loading

0 comments on commit 45be869

Please sign in to comment.