Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactored access management #207

Merged
merged 93 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from 72 commits
Commits
Show all changes
93 commits
Select commit Hold shift + click to select a range
58f4260
prepare table for new intermediary "user keys"
overheadhunter Apr 24, 2023
ec32f4b
generate user key + device key on first login
overheadhunter Apr 28, 2023
1f5e7f3
grant access by encrypting vault key for user
overheadhunter Apr 28, 2023
9bb8a23
prepare /api/devices for repeated PUT and GET
overheadhunter May 3, 2023
497592f
updated paths to obtain JWEs
overheadhunter May 5, 2023
0949aa1
made Device.userKeyJwe nullable
overheadhunter May 5, 2023
2a3c106
re-added legacy API /api/vaults/{vaultId}/keys/{deviceId} for a smoot…
overheadhunter May 5, 2023
28afe35
diversified test case
overheadhunter May 5, 2023
074a173
moved service
overheadhunter May 8, 2023
40f4b33
prepare more test data for testing with other platforms
overheadhunter May 8, 2023
c6d3811
keep device public key base64url-encoded for compatibility with exist…
overheadhunter May 9, 2023
a67f212
Allow use of validation annotations for nullable fields.
overheadhunter May 9, 2023
2198602
adjust unlock success screen for new authentication model
overheadhunter May 15, 2023
86e790d
encrypt user key for a device
overheadhunter May 16, 2023
45b0968
store recovery code for later display
overheadhunter May 25, 2023
4578b3a
added SetupUserKey component
tobihagemann May 26, 2023
274ffa6
added "enter recovery code" flow in SetupUserKey
tobihagemann May 26, 2023
ed57bde
deduplicated code
tobihagemann May 26, 2023
2f1afc6
fix recovering user key from setup code
overheadhunter May 29, 2023
efc4cdf
always roll a new key pair when creating/recovering a user key
overheadhunter May 29, 2023
2ed6624
added current device badge, hidden remove button
tobihagemann May 30, 2023
4c21c86
added device name input to SetupUserKey
tobihagemann May 30, 2023
740fd10
updated current device badge
tobihagemann May 30, 2023
4766afe
added last seen column to DeviceList
tobihagemann May 30, 2023
cfd280c
added type and last_seen_time to device entity
tobihagemann May 30, 2023
11f818f
show Personal Hub Secret (in device list??)
overheadhunter May 31, 2023
29e45bc
added missing 200 response code to /api/vaults/{vaultId}/users-requir…
chenkins May 31, 2023
9b35286
fixed Hibernate error: Schema-validation: wrong column type encountered
overheadhunter May 31, 2023
5df9236
use single `PUT /api/users/me` API for all create/update requests
overheadhunter May 31, 2023
f590182
alternative CSP headers for using Quarkus Dev UI
overheadhunter May 31, 2023
5eedc46
make requests idempotent:
overheadhunter May 31, 2023
c480ea4
updated wording and layout in SetupUserKey
tobihagemann May 31, 2023
0846927
further refinements
tobihagemann May 31, 2023
c4c1e99
store "owner" role during vault creation
overheadhunter May 31, 2023
2f7f9bc
immediately encrypt vault keys for owner during vault creation
overheadhunter May 31, 2023
e549937
Merge commit '0846927f628b5b789424f1998acfb3ce63370dcd' into feature/…
overheadhunter May 31, 2023
f9199e4
added UserProfile and ManageRecoveryCode components
tobihagemann Jun 1, 2023
601ddd9
renamed and revised `GET /api/vaults/accessible`
overheadhunter Jun 1, 2023
b261d48
rotate setup code
overheadhunter Jun 1, 2023
3afe8de
cleanup: removed barely used `GET /api/users/me?withAccessibleVaults=…
overheadhunter Jun 1, 2023
2ab6495
fixed tests
overheadhunter Jun 1, 2023
9f15d3f
fix code smells
overheadhunter Jun 1, 2023
ee58ae2
Merge commit 'a2822b65f79616ca0f08481aad0bb7cdd3700857' into feature/…
overheadhunter Jun 1, 2023
1f833d8
added confirmation dialog for regenerating recovery code
tobihagemann Jun 1, 2023
06d9d8c
added vault owner handling in VaultList (badge, filter)
tobihagemann Jun 2, 2023
0c5008c
New, fluent API for JWE building+parsing, added PBES2-HS512+A256KW
overheadhunter Jun 2, 2023
6e2112a
simplified database model, store User's Setup Code in single JWE field
overheadhunter Jun 2, 2023
115aae3
increase PBKDF2 iteration count
overheadhunter Jun 2, 2023
3b5003d
added confirmation dialog for resetting user account
tobihagemann Jun 6, 2023
b6cdc62
added manage account button/link in user profile
tobihagemann Jun 6, 2023
f6534e3
removed user settings and moved them to user profile
tobihagemann Jun 6, 2023
05d0d9a
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jun 16, 2023
7973b93
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jun 16, 2023
9fc2b17
migrate `::set-output`
overheadhunter Jun 16, 2023
e935311
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jun 16, 2023
c32c832
try building with node 20
overheadhunter Jun 16, 2023
f5a335d
fix migrations after merging V8
overheadhunter Jun 16, 2023
54156b3
allow device registration from older Cryptomator desktop clients
overheadhunter Jun 16, 2023
eb63a93
Fix 204 without description.
chenkins Jun 19, 2023
a621a0c
Merge pull request #204 from chenkins/feature/refactored-access-grant…
tobihagemann Jun 19, 2023
85e8a2f
skip "setup" in unlock success screen
overheadhunter Jul 6, 2023
822d8b1
generate setup code test data for java unit tests
overheadhunter Jul 6, 2023
491739a
data model + API cleanup
overheadhunter Jul 6, 2023
02f0419
use base64 encoding for new devices
overheadhunter Jul 6, 2023
700c3a6
API cleanup:
overheadhunter Jul 6, 2023
dabc2c3
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 6, 2023
93778ee
Remove "validate device" from frontend
overheadhunter Jul 6, 2023
615f9ba
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 8, 2023
5e0c281
use low PBKDF2 iteration count in unit tests
overheadhunter Jul 8, 2023
f41b17b
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 8, 2023
3c3f32e
Revert changes to `effective_vault_access` view done in 2eb435d: Arch…
overheadhunter Jul 10, 2023
dd29e3e
cleanup
overheadhunter Jul 10, 2023
29bcb78
renamed "recovery code" → "setup code" in code
overheadhunter Jul 12, 2023
f4d0c0d
added missing translations (tbd)
overheadhunter Jul 12, 2023
51d6742
cleanup
overheadhunter Jul 12, 2023
a5b26fb
fix NPE in "Grant Permissions" dialog
overheadhunter Jul 12, 2023
57ce308
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 13, 2023
bf4a625
added inline comment
overheadhunter Jul 13, 2023
a81ec97
fixed `org.flywaydb.core.api.FlywayException: Found more than one mig…
overheadhunter Jul 13, 2023
cdd0937
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 19, 2023
0bcbc3a
add `AuditEventVaultMemberAdd.role`
overheadhunter Jul 19, 2023
8795fab
delete legacy device during re-registration to "new" device table
overheadhunter Jul 25, 2023
2baf0d1
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 27, 2023
0f88fe8
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Jul 27, 2023
21264cf
fix compile error
overheadhunter Jul 27, 2023
bcf5dcc
adjusted flyway migration version
overheadhunter Jul 27, 2023
e9411b4
log unlock attempts
overheadhunter Jul 27, 2023
0bad652
minor improvement displaying role
tobihagemann Jul 28, 2023
19faa14
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Aug 2, 2023
52052ca
Merge branch 'develop' into feature/refactored-access-grant
overheadhunter Aug 3, 2023
963d1d8
renamed personal hub secret to setup code
tobihagemann Aug 4, 2023
e55caa1
Merge branch 'develop' into feature/refactored-access-grant
tobihagemann Aug 15, 2023
1baf734
Fix unlock, query fails due to Hibernate bug
SailReal Aug 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions backend/src/main/java/org/cryptomator/hub/api/AuthorityDto.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,13 @@ protected AuthorityDto(String id, Type type, String name, String pictureUrl) {
}

static AuthorityDto fromEntity(Authority a) {
if (a instanceof User u) {
return new UserDto(u.id, u.name, u.pictureUrl, u.email, null);
} else if (a instanceof Group) {
return new GroupDto(a.id, a.name);
// TODO refactor to JEP 441 in JDK 21
if (a instanceof User user) {
return UserDto.justPublicInfo(user);
} else if (a instanceof Group group) {
return GroupDto.fromEntity(group);
} else {
throw new IllegalArgumentException("Authority of this type does not exist");
throw new IllegalStateException("authority is not of type user or group");
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public List<AuthorityDto> search(@QueryParam("query") @NotBlank String query) {
@RolesAllowed("admin")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@Operation(summary = "lists all authorities matching the given ids", description ="lists for each id in the list its corresponding authority. Ignores all id's where an authority cannot be found")
@Operation(summary = "lists all authorities matching the given ids", description = "lists for each id in the list its corresponding authority. Ignores all id's where an authority cannot be found")
@APIResponse(responseCode = "200")
public List<AuthorityDto> getSome(@QueryParam("ids") List<String> authorityIds) {
return Authority.findAllInList(authorityIds).map(AuthorityDto::fromEntity).toList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
Expand Down Expand Up @@ -51,10 +52,10 @@ public BillingDto get() {
@RolesAllowed("admin")
@Consumes(MediaType.TEXT_PLAIN)
@Operation(summary = "set the token")
@APIResponse(responseCode = "204")
@APIResponse(responseCode = "204", description = "token set")
@APIResponse(responseCode = "400", description = "token is invalid (e.g., expired or invalid signature)")
@APIResponse(responseCode = "403", description = "only admins are allowed to set the token")
public Response setToken(@ValidJWS String token) {
public Response setToken(@NotNull @ValidJWS String token) {
try {
licenseHolder.set(token);
return Response.status(Response.Status.NO_CONTENT).build();
Expand All @@ -69,7 +70,7 @@ public record BillingDto(@JsonProperty("hubId") String hubId, @JsonProperty("has

public static BillingDto create(String hubId, LicenseHolder licenseHolder) {
var seats = licenseHolder.getNoLicenseSeats();
var remainingSeats = Math.max(seats - EffectiveVaultAccess.countEffectiveVaultUsers(), 0);
var remainingSeats = Math.max(seats - EffectiveVaultAccess.countSeatOccupyingUsers(), 0);
var managedInstance = licenseHolder.isManagedInstance();
return new BillingDto(hubId, false, null, (int) seats, (int) remainingSeats, null, null, managedInstance);
}
Expand All @@ -78,7 +79,7 @@ public static BillingDto fromDecodedJwt(DecodedJWT jwt, LicenseHolder licenseHol
var id = jwt.getId();
var email = jwt.getSubject();
var totalSeats = jwt.getClaim("seats").asInt();
var remainingSeats = Math.max(totalSeats - (int) EffectiveVaultAccess.countEffectiveVaultUsers(), 0);
var remainingSeats = Math.max(totalSeats - (int) EffectiveVaultAccess.countSeatOccupyingUsers(), 0);
var issuedAt = jwt.getIssuedAt().toInstant();
var expiresAt = jwt.getExpiresAt().toInstant();
var managedInstance = licenseHolder.isManagedInstance();
Expand Down
77 changes: 45 additions & 32 deletions backend/src/main/java/org/cryptomator/hub/api/DeviceResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,16 @@
import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.NoResultException;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.ws.rs.ClientErrorException;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.InternalServerErrorException;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
Expand All @@ -22,18 +23,19 @@
import org.cryptomator.hub.entities.Device;
import org.cryptomator.hub.entities.User;
import org.cryptomator.hub.validation.NoHtmlOrScriptChars;
import org.cryptomator.hub.validation.OnlyBase64UrlChars;
import org.cryptomator.hub.validation.OnlyBase64Chars;
import org.cryptomator.hub.validation.ValidId;
import org.cryptomator.hub.validation.ValidJWE;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.eclipse.microprofile.openapi.annotations.Operation;
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.hibernate.exception.ConstraintViolationException;
import org.jboss.resteasy.reactive.NoCache;

import java.net.URI;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Set;

@Path("/devices")
public class DeviceResource {
Expand All @@ -58,24 +60,46 @@ public List<DeviceDto> getSome(@QueryParam("ids") List<String> deviceIds) {
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Transactional
@Operation(summary = "adds a device", description = "the device will be owned by the currently logged-in user")
@APIResponse(responseCode = "201", description = "device created")
@APIResponse(responseCode = "409", description = "Device already exists")
public Response create(@Valid DeviceDto deviceDto, @PathParam("deviceId") @ValidId String deviceId) {
if (deviceId == null || deviceId.trim().length() == 0 || deviceDto == null) {
return Response.status(Response.Status.BAD_REQUEST).entity("deviceId or deviceDto cannot be empty").build();
@Operation(summary = "creates or updates a device", description = "the device will be owned by the currently logged-in user")
@APIResponse(responseCode = "201", description = "Device created or updated")
@APIResponse(responseCode = "409", description = "Conflicting device id or name")
overheadhunter marked this conversation as resolved.
Show resolved Hide resolved
public Response createOrUpdate(@Valid @NotNull DeviceDto dto, @PathParam("deviceId") @ValidId String deviceId) {
Device device;
try {
device = Device.findByIdAndUser(deviceId, jwt.getSubject());
} catch (NoResultException e) {
device = new Device();
device.id = deviceId;
device.owner = User.findById(jwt.getSubject());
device.creationTime = Instant.now().truncatedTo(ChronoUnit.MILLIS);
device.type = dto.type != null ? dto.type : Device.Type.DESKTOP; // default to desktop for backwards compatibility
}
User currentUser = User.findById(jwt.getSubject());
var device = deviceDto.toDevice(currentUser, deviceId, Instant.now().truncatedTo(ChronoUnit.MILLIS));
device.name = dto.name;
device.publickey = dto.publicKey;
device.userPrivateKey = dto.userPrivateKey;
try {
device.persistAndFlush();
return Response.created(URI.create(".")).build();
} catch (PersistenceException e) {
if (e instanceof ConstraintViolationException) {
throw new ClientErrorException(Response.Status.CONFLICT, e);
} else {
throw new InternalServerErrorException(e);
}
} catch (ConstraintViolationException e) {
throw new ClientErrorException(Response.Status.CONFLICT, e);
}
}

@GET
@Path("/{deviceId}")
@RolesAllowed("user")
@Produces(MediaType.APPLICATION_JSON)
@NoCache
@Transactional
@Operation(summary = "get the device", description = "the device must be owned by the currently logged-in user")
@APIResponse(responseCode = "200", description = "Device found")
@APIResponse(responseCode = "404", description = "Device not found or owned by a different user")
public DeviceDto get(@PathParam("deviceId") @ValidId String deviceId) {
try {
Device device = Device.findByIdAndUser(deviceId, jwt.getSubject());
return DeviceDto.fromEntity(device);
} catch (NoResultException e) {
throw new NotFoundException(e);
}
}

Expand Down Expand Up @@ -105,24 +129,13 @@ public Response remove(@PathParam("deviceId") @ValidId String deviceId) {
public record DeviceDto(@JsonProperty("id") @ValidId String id,
@JsonProperty("name") @NoHtmlOrScriptChars @NotBlank String name,
@JsonProperty("type") Device.Type type,
@JsonProperty("publicKey") @OnlyBase64UrlChars String publicKey,
@JsonProperty("publicKey") @NotNull @OnlyBase64Chars String publicKey,
@JsonProperty("userPrivateKey") @NotNull @ValidJWE String userPrivateKey,
@JsonProperty("owner") @ValidId String ownerId,
@JsonProperty("accessTo") @Valid Set<VaultResource.VaultDto> accessTo,
@JsonProperty("creationTime") Instant creationTime) {

public Device toDevice(User user, String id, Instant creationTime) {
var device = new Device();
device.id = id;
device.owner = user;
device.name = name;
device.type = type != null ? type : Device.Type.DESKTOP; // default to desktop for backwards compatibility
device.publickey = publicKey;
device.creationTime = creationTime;
return device;
}

public static DeviceDto fromEntity(Device entity) {
return new DeviceDto(entity.id, entity.name, entity.type, entity.publickey, entity.owner.id, Set.of(), entity.creationTime.truncatedTo(ChronoUnit.MILLIS));
return new DeviceDto(entity.id, entity.name, entity.type, entity.publickey, entity.userPrivateKey, entity.owner.id, entity.creationTime.truncatedTo(ChronoUnit.MILLIS));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import jakarta.ws.rs.ClientErrorException;
import jakarta.ws.rs.core.Response;

public class PaymentRequiredException extends ClientErrorException {
class PaymentRequiredException extends ClientErrorException {
public PaymentRequiredException() {
super(Response.Status.PAYMENT_REQUIRED);
}
Expand Down
19 changes: 16 additions & 3 deletions backend/src/main/java/org/cryptomator/hub/api/UserDto.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package org.cryptomator.hub.api;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.annotation.Nullable;
import org.cryptomator.hub.entities.User;
import org.cryptomator.hub.validation.OnlyBase64Chars;
import org.cryptomator.hub.validation.ValidJWE;

import java.util.Set;

Expand All @@ -11,14 +14,24 @@ public final class UserDto extends AuthorityDto {
public final String email;
@JsonProperty("devices")
public final Set<DeviceResource.DeviceDto> devices;
@JsonProperty("publicKey")
public final String publicKey;
@JsonProperty("privateKey")
public final String privateKey;
@JsonProperty("setupCode")
public final String setupCode;

UserDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("email") String email, @JsonProperty("devices") Set<DeviceResource.DeviceDto> devices) {
UserDto(@JsonProperty("id") String id, @JsonProperty("name") String name, @JsonProperty("pictureUrl") String pictureUrl, @JsonProperty("email") String email, @JsonProperty("devices") Set<DeviceResource.DeviceDto> devices,
@Nullable @JsonProperty("publicKey") @OnlyBase64Chars String publicKey, @Nullable @JsonProperty("privateKey") @ValidJWE String privateKey, @Nullable @JsonProperty("setupCode") @ValidJWE String setupCode) {
super(id, Type.USER, name, pictureUrl);
this.email = email;
this.devices = devices;
this.publicKey = publicKey;
this.privateKey = privateKey;
this.setupCode = setupCode;
}

public static UserDto fromEntity(User user) {
return new UserDto(user.id, user.name, user.pictureUrl, user.email, Set.of());
public static UserDto justPublicInfo(User user) {
return new UserDto(user.id, user.name, user.pictureUrl, user.email, Set.of(), user.publicKey, null, null);
}
}
31 changes: 18 additions & 13 deletions backend/src/main/java/org/cryptomator/hub/api/UsersResource.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package org.cryptomator.hub.api;

import jakarta.annotation.Nullable;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import org.cryptomator.hub.entities.AccessToken;
import org.cryptomator.hub.entities.Device;
import org.cryptomator.hub.entities.User;
import org.eclipse.microprofile.jwt.JsonWebToken;
Expand All @@ -35,10 +37,11 @@ public class UsersResource {
@PUT
@Path("/me")
@RolesAllowed("user")
@Consumes(MediaType.APPLICATION_JSON)
@Transactional
@Operation(summary = "sync the logged-in user from the remote user provider to hub")
@Operation(summary = "update the logged-in user")
@APIResponse(responseCode = "201", description = "user created or updated")
public Response syncMe() {
public Response putMe(@Nullable @Valid UserDto dto) {
var userId = jwt.getSubject();
User user = User.findById(userId);
if (user == null) {
Expand All @@ -48,6 +51,11 @@ public Response syncMe() {
user.name = jwt.getName();
user.pictureUrl = jwt.getClaim("picture");
user.email = jwt.getClaim("email");
if (dto != null) {
user.publicKey = dto.publicKey;
user.privateKey = dto.privateKey;
user.setupCode = dto.setupCode;
}
user.persist();
return Response.created(URI.create(".")).build();
}
Expand All @@ -59,16 +67,13 @@ public Response syncMe() {
@NoCache
@Transactional
@Operation(summary = "get the logged-in user")
public UserDto getMe(@QueryParam("withDevices") boolean withDevices, @QueryParam("withAccessibleVaults") boolean withAccessibleVaults) {
@APIResponse(responseCode = "200", description = "returns the current user")
@APIResponse(responseCode = "404", description = "no user matching the subject of the JWT passed as Bearer Token")
public UserDto getMe(@QueryParam("withDevices") boolean withDevices) {
User user = User.findById(jwt.getSubject());
Function<AccessToken, VaultResource.VaultDto> mapAccessibleVaults =
a -> new VaultResource.VaultDto(a.vault.id, a.vault.name, a.vault.description, a.vault.archived, a.vault.creationTime.truncatedTo(ChronoUnit.MILLIS), null, 0, null, null, null);
Function<Device, DeviceResource.DeviceDto> mapDevices = withAccessibleVaults //
? d -> new DeviceResource.DeviceDto(d.id, d.name, d.type, d.publickey, d.owner.id, d.accessTokens.stream().map(mapAccessibleVaults).collect(Collectors.toSet()), d.creationTime.truncatedTo(ChronoUnit.MILLIS)) //
: d -> new DeviceResource.DeviceDto(d.id, d.name, d.type, d.publickey, d.owner.id, Set.of(), d.creationTime.truncatedTo(ChronoUnit.MILLIS));
return withDevices //
? new UserDto(user.id, user.name, user.pictureUrl, user.email, user.devices.stream().map(mapDevices).collect(Collectors.toSet()))
: new UserDto(user.id, user.name, user.pictureUrl, user.email, Set.of());
Function<Device, DeviceResource.DeviceDto> mapDevices = d -> new DeviceResource.DeviceDto(d.id, d.name, d.type, d.publickey, d.userPrivateKey, d.owner.id, d.creationTime.truncatedTo(ChronoUnit.MILLIS));
var devices = withDevices ? user.devices.stream().map(mapDevices).collect(Collectors.toSet()) : Set.<DeviceResource.DeviceDto>of();
return new UserDto(user.id, user.name, user.pictureUrl, user.email, devices, user.publicKey, user.privateKey, user.setupCode);
}

@GET
Expand All @@ -77,7 +82,7 @@ public UserDto getMe(@QueryParam("withDevices") boolean withDevices, @QueryParam
@Produces(MediaType.APPLICATION_JSON)
@Operation(summary = "list all users")
public List<UserDto> getAll() {
return User.findAll().<User>stream().map(UserDto::fromEntity).toList();
return User.findAll().<User>stream().map(UserDto::justPublicInfo).toList();
}

}
Loading