From c908614bafd34cf33b69987372a843038471106b Mon Sep 17 00:00:00 2001 From: spoonman01 Date: Wed, 9 Oct 2024 12:01:39 +0200 Subject: [PATCH 1/3] WIP(add-domain-table-columns) #WPB-11298 --- .../com/wire/bots/hold/DAO/AccessDAO.java | 33 ++++++++++++------- .../bots/hold/DAO/AccessResultSetMapper.java | 5 ++- .../com/wire/bots/hold/DAO/EventsDAO.java | 20 ++++++++--- src/main/java/com/wire/bots/hold/Service.java | 2 ++ .../bots/hold/healthchecks/SanityCheck.java | 8 ++--- .../wire/bots/hold/model/database/Event.java | 2 ++ .../bots/hold/model/database/LHAccess.java | 4 ++- .../bots/hold/monitoring/StatusResource.java | 2 +- .../v0/audit/ConversationResource.java | 5 ++- .../resource/v0/audit/DevicesResource.java | 9 ++--- .../resource/v0/audit/EventsResource.java | 3 +- .../v0/backend/ConfirmResourceV0.java | 2 +- .../v0/backend/InitiateResourceV0.java | 2 +- .../resource/v0/backend/RemoveResourceV0.java | 2 +- .../hold/service/DeviceManagementService.java | 3 +- .../wire/bots/hold/utils/HoldClientRepo.java | 5 ++- ...08__add_domain_column_in_access_events.sql | 16 +++++++++ .../java/com/wire/bots/hold/DatabaseTest.java | 6 ++-- 18 files changed, 88 insertions(+), 41 deletions(-) create mode 100644 src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql diff --git a/src/main/java/com/wire/bots/hold/DAO/AccessDAO.java b/src/main/java/com/wire/bots/hold/DAO/AccessDAO.java index e772614..3b86863 100644 --- a/src/main/java/com/wire/bots/hold/DAO/AccessDAO.java +++ b/src/main/java/com/wire/bots/hold/DAO/AccessDAO.java @@ -10,33 +10,42 @@ import java.util.UUID; public interface AccessDAO { - @SqlUpdate("INSERT INTO Access (userId, clientId, cookie, updated, created, enabled) " + - "VALUES (:userId, :clientId, :cookie, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1) " + - "ON CONFLICT (userId) DO UPDATE SET cookie = EXCLUDED.cookie, clientId = EXCLUDED.clientId, " + + @SqlUpdate("INSERT INTO Access (userId, userDomain, clientId, cookie, updated, created, enabled) " + + "VALUES (:userId, :userDomain, :clientId, :cookie, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP, 1) " + + "ON CONFLICT (userId, userDomain) DO UPDATE SET cookie = EXCLUDED.cookie, clientId = EXCLUDED.clientId, " + "updated = EXCLUDED.updated, enabled = EXCLUDED.enabled") int insert(@Bind("userId") UUID userId, + @Bind("userDomain") String userDomain, @Bind("clientId") String clientId, @Bind("cookie") String cookie); - @SqlUpdate("UPDATE Access SET enabled = 0, updated = CURRENT_TIMESTAMP WHERE userId = :userId") - int disable(@Bind("userId") UUID userId); + @SqlUpdate("UPDATE Access SET enabled = 0, updated = CURRENT_TIMESTAMP WHERE userId = :userId " + + "AND (( :userDomain IS NULL AND userDomain IS null ) or ( :userDomain IS NOT NULL AND userDomain = :userDomain ))") + int disable(@Bind("userId") UUID userId, + @Bind("userDomain") String userDomain); - @SqlUpdate("UPDATE Access SET token = :token, cookie = :cookie, updated = CURRENT_TIMESTAMP WHERE userId = :userId") + @SqlUpdate("UPDATE Access SET token = :token, cookie = :cookie, updated = CURRENT_TIMESTAMP WHERE userId = :userId " + + "AND (( :userDomain IS NULL AND userDomain IS null ) OR ( :userDomain IS NOT NULL AND userDomain = :userDomain ))") int update(@Bind("userId") UUID userId, - @Bind("token") String token, - @Bind("cookie") String cookie); + @Bind("userDomain") String userDomain, + @Bind("token") String token, + @Bind("cookie") String cookie); - @SqlUpdate("UPDATE Access SET last = :last, updated = CURRENT_TIMESTAMP WHERE userId = :userId") + @SqlUpdate("UPDATE Access SET last = :last, updated = CURRENT_TIMESTAMP WHERE userId = :userId AND (( :userDomain IS NULL AND userDomain IS null ) " + + "OR ( :userDomain IS NOT NULL AND userDomain = :userDomain ))") int updateLast(@Bind("userId") UUID userId, - @Bind("last") UUID last); + @Bind("userDomain") String userDomain, + @Bind("last") UUID last); @SqlQuery("SELECT * FROM Access WHERE token IS NOT NULL AND enabled = 1 ORDER BY created DESC LIMIT 1") @RegisterColumnMapper(AccessResultSetMapper.class) LHAccess getSingle(); - @SqlQuery("SELECT * FROM Access WHERE userId = :userId") + @SqlQuery("SELECT * FROM Access WHERE userId = :userId AND (( :userDomain IS NULL AND userDomain is null ) " + + "or ( :userDomain is NOT NULL AND userDomain = :userDomain ))") @RegisterColumnMapper(AccessResultSetMapper.class) - LHAccess get(@Bind("userId") UUID userId); + LHAccess get(@Bind("userId") UUID userId, + @Bind("userDomain") String userDomain); @SqlQuery("SELECT * FROM Access WHERE enabled = 1 ORDER BY created DESC") @RegisterColumnMapper(AccessResultSetMapper.class) diff --git a/src/main/java/com/wire/bots/hold/DAO/AccessResultSetMapper.java b/src/main/java/com/wire/bots/hold/DAO/AccessResultSetMapper.java index d3f6bc7..eb1286b 100644 --- a/src/main/java/com/wire/bots/hold/DAO/AccessResultSetMapper.java +++ b/src/main/java/com/wire/bots/hold/DAO/AccessResultSetMapper.java @@ -1,6 +1,7 @@ package com.wire.bots.hold.DAO; import com.wire.bots.hold.model.database.LHAccess; +import com.wire.xenon.backend.models.QualifiedId; import org.jdbi.v3.core.mapper.ColumnMapper; import org.jdbi.v3.core.statement.StatementContext; @@ -13,7 +14,9 @@ public class AccessResultSetMapper implements ColumnMapper { public LHAccess map(ResultSet rs, int columnNumber, StatementContext ctx) throws SQLException { LHAccess LHAccess = new LHAccess(); LHAccess.last = (UUID) rs.getObject("last"); - LHAccess.userId = (UUID) rs.getObject("userId"); + UUID userId = (UUID) rs.getObject("userId"); + String userDomain = rs.getString("userDomain"); + LHAccess.userId = new QualifiedId(userId, userDomain); LHAccess.clientId = rs.getString("clientId"); LHAccess.token = rs.getString("token"); LHAccess.cookie = rs.getString("cookie"); diff --git a/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java b/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java index d3c6511..e79a1cb 100644 --- a/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java +++ b/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java @@ -27,13 +27,25 @@ int insert(@Bind("eventId") UUID eventId, @RegisterColumnMapper(EventsResultSetMapper.class) Event get(@Bind("eventId") UUID eventId); - @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId ORDER BY time DESC") + @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND (conversationDomain is NULL OR conversationDomain = :conversationDomain) ORDER BY time DESC") @RegisterColumnMapper(EventsResultSetMapper.class) - List listAll(@Bind("conversationId") UUID conversationId); + List listAllDefaultDomain(@Bind("conversationId") UUID conversationId, + @Bind("conversationDomain") String conversationDomain); - @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId ORDER BY time ASC") + @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND conversationDomain = :conversationDomain ORDER BY time DESC") @RegisterColumnMapper(EventsResultSetMapper.class) - List listAllAsc(@Bind("conversationId") UUID conversationId); + List listAll(@Bind("conversationId") UUID conversationId, + @Bind("conversationDomain") String conversationDomain); + + @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND (conversationDomain is NULL OR conversationDomain = :conversationDomain) ORDER BY time ASC") + @RegisterColumnMapper(EventsResultSetMapper.class) + List listAllDefaultDomainAsc(@Bind("conversationId") UUID conversationId, + @Bind("conversationDomain") String conversationDomain); + + @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND conversationDomain = :conversationDomain ORDER BY time ASC") + @RegisterColumnMapper(EventsResultSetMapper.class) + List listAllAsc(@Bind("conversationId") UUID conversationId, + @Bind("conversationDomain") String conversationDomain); @SqlQuery("SELECT DISTINCT conversationId, MAX(time) AS time " + "FROM Events " + diff --git a/src/main/java/com/wire/bots/hold/Service.java b/src/main/java/com/wire/bots/hold/Service.java index aa470cd..876ab78 100644 --- a/src/main/java/com/wire/bots/hold/Service.java +++ b/src/main/java/com/wire/bots/hold/Service.java @@ -25,6 +25,7 @@ import com.wire.bots.hold.DAO.MetadataDAO; import com.wire.bots.hold.filters.ServiceAuthenticationFilter; import com.wire.bots.hold.healthchecks.SanityCheck; +import com.wire.bots.hold.monitoring.ApiVersionResource; import com.wire.bots.hold.monitoring.RequestMdcFactoryFilter; import com.wire.bots.hold.monitoring.StatusResource; import com.wire.bots.hold.resource.v0.audit.*; @@ -126,6 +127,7 @@ public void run(Config config, Environment environment) throws ExecutionExceptio // Monitoring resources addResource(new StatusResource()); + addResource(new ApiVersionResource()); addResource(new RequestMdcFactoryFilter()); // Used by Wire Server diff --git a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java index d6e6047..c23f530 100644 --- a/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java +++ b/src/main/java/com/wire/bots/hold/healthchecks/SanityCheck.java @@ -5,7 +5,6 @@ import com.wire.bots.hold.model.database.LHAccess; import com.wire.bots.hold.utils.Cache; import com.wire.helium.API; -import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.tools.Logger; import javax.ws.rs.client.Client; @@ -37,10 +36,11 @@ protected Result check() { while (!accessList.isEmpty()) { Logger.info("SanityCheck: checking %d devices, created: %s", accessList.size(), created); for (LHAccess access : accessList) { - // TODO(WPB-11287): Use user domain if exists, otherwise default - // TODO: String domain = (access.domain != null) ? access.domain : Cache.DEFAULT_DOMAIN; + if (access.userId.domain == null) { + access.userId.domain = Cache.getFallbackDomain(); + } boolean hasDevice = api.hasDevice( - new QualifiedId(access.userId, Cache.getFallbackDomain()), // TODO(WPB-11287): Change null to default domain + access.userId, access.clientId ); diff --git a/src/main/java/com/wire/bots/hold/model/database/Event.java b/src/main/java/com/wire/bots/hold/model/database/Event.java index a7c49eb..ed5e4bd 100644 --- a/src/main/java/com/wire/bots/hold/model/database/Event.java +++ b/src/main/java/com/wire/bots/hold/model/database/Event.java @@ -5,7 +5,9 @@ public class Event { public UUID eventId; public UUID conversationId; + public String conversationDomain; // Keeping values split instead of using QualifiedId because of HTML templating public UUID userId; + public String userDomain; // Keeping values split instead of using QualifiedId because of HTML templating public String type; public String payload; public String time; diff --git a/src/main/java/com/wire/bots/hold/model/database/LHAccess.java b/src/main/java/com/wire/bots/hold/model/database/LHAccess.java index f6d79c6..568b953 100644 --- a/src/main/java/com/wire/bots/hold/model/database/LHAccess.java +++ b/src/main/java/com/wire/bots/hold/model/database/LHAccess.java @@ -1,10 +1,12 @@ package com.wire.bots.hold.model.database; +import com.wire.xenon.backend.models.QualifiedId; + import java.util.UUID; public class LHAccess { public UUID last; - public UUID userId; + public QualifiedId userId; public String clientId; public String token; public String cookie; diff --git a/src/main/java/com/wire/bots/hold/monitoring/StatusResource.java b/src/main/java/com/wire/bots/hold/monitoring/StatusResource.java index c44e87d..42bd0dd 100644 --- a/src/main/java/com/wire/bots/hold/monitoring/StatusResource.java +++ b/src/main/java/com/wire/bots/hold/monitoring/StatusResource.java @@ -28,7 +28,7 @@ import javax.ws.rs.core.Response; @Api -@Path("/status") +@Path("/{parameter: status|v1/status}") @Produces(MediaType.TEXT_PLAIN) public class StatusResource { @GET diff --git a/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java b/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java index 7637839..0131b97 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java @@ -65,9 +65,8 @@ public ConversationResource(Jdbi jdbi, Client httpClient) { public Response list(@ApiParam @PathParam("conversationId") UUID conversationId, @ApiParam @QueryParam("html") boolean isHtml) { try { - List events = eventsDAO.listAllAsc(conversationId); - - // TODO(WPB-11287) Verify default domain + //TODO Get DEFAULT_DOMAIN, then fetch events with domain = null and domain = DEFAULT_DOMAIN + List events = eventsDAO.listAllDefaultDomainAsc(conversationId); testAPI(); diff --git a/src/main/java/com/wire/bots/hold/resource/v0/audit/DevicesResource.java b/src/main/java/com/wire/bots/hold/resource/v0/audit/DevicesResource.java index a3659b9..8d6024b 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/audit/DevicesResource.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/audit/DevicesResource.java @@ -6,8 +6,9 @@ import com.wire.bots.hold.DAO.AccessDAO; import com.wire.bots.hold.filters.ServiceAuthorization; import com.wire.bots.hold.model.database.LHAccess; +import com.wire.bots.hold.utils.CryptoDatabaseFactory; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.crypto.Crypto; -import com.wire.xenon.factories.CryptoFactory; import com.wire.xenon.tools.Logger; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -32,10 +33,10 @@ @Produces(MediaType.TEXT_HTML) public class DevicesResource { private final static MustacheFactory mf = new DefaultMustacheFactory(); - private final CryptoFactory cryptoFactory; + private final CryptoDatabaseFactory cryptoFactory; private final AccessDAO accessDAO; - public DevicesResource(AccessDAO accessDAO, CryptoFactory cryptoFactory) { + public DevicesResource(AccessDAO accessDAO, CryptoDatabaseFactory cryptoFactory) { this.cryptoFactory = cryptoFactory; this.accessDAO = accessDAO; } @@ -95,7 +96,7 @@ private String execute(Object model) throws IOException { static class Legal { UUID last; - UUID userId; + QualifiedId userId; String clientId; String fingerprint; String updated; diff --git a/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java b/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java index 60236f3..077a2fb 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java @@ -40,8 +40,9 @@ public EventsResource(EventsDAO eventsDAO) { @ApiResponse(code = 200, message = "Wire events")}) public Response list(@ApiParam @PathParam("conversationId") UUID conversationId) { try { + //TODO Get DEFAULT_DOMAIN, then fetch events with domain = null and domain = DEFAULT_DOMAIN Model model = new Model(); - model.events = eventsDAO.listAll(conversationId); + model.events = eventsDAO.listAllDefaultDomain(conversationId); String html = execute(model); return Response. diff --git a/src/main/java/com/wire/bots/hold/resource/v0/backend/ConfirmResourceV0.java b/src/main/java/com/wire/bots/hold/resource/v0/backend/ConfirmResourceV0.java index 0f91e83..02c49d2 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/backend/ConfirmResourceV0.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/backend/ConfirmResourceV0.java @@ -35,7 +35,7 @@ public ConfirmResourceV0(DeviceManagementService deviceManagementService) { public Response confirm(@ApiParam @Valid @NotNull ConfirmPayloadV0 payload) { try { deviceManagementService.confirmDevice( - new QualifiedId(payload.userId, null), //TODO Probably a good place to put the DEFAULT_DOMAIN + new QualifiedId(payload.userId, null), payload.teamId, payload.clientId, payload.refreshToken diff --git a/src/main/java/com/wire/bots/hold/resource/v0/backend/InitiateResourceV0.java b/src/main/java/com/wire/bots/hold/resource/v0/backend/InitiateResourceV0.java index 38dfcb9..a68f9e5 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/backend/InitiateResourceV0.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/backend/InitiateResourceV0.java @@ -38,7 +38,7 @@ public Response initiate(@ApiParam @Valid @NotNull InitPayloadV0 init) { try { final InitializedDeviceDTO initializedDeviceDTO = deviceManagementService.initiateLegalHoldDevice( - new QualifiedId(init.userId, null), //TODO Probably a good place to put the DEFAULT_DOMAIN + new QualifiedId(init.userId, null), init.teamId ); diff --git a/src/main/java/com/wire/bots/hold/resource/v0/backend/RemoveResourceV0.java b/src/main/java/com/wire/bots/hold/resource/v0/backend/RemoveResourceV0.java index 7068233..10a07cb 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/backend/RemoveResourceV0.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/backend/RemoveResourceV0.java @@ -34,7 +34,7 @@ public RemoveResourceV0(DeviceManagementService deviceManagementService) { public Response remove(@ApiParam @Valid InitPayloadV0 payload) { try { deviceManagementService.removeDevice( - new QualifiedId(payload.userId, null), //TODO Probably a good place to put the DEFAULT_DOMAIN + new QualifiedId(payload.userId, null), payload.teamId ); diff --git a/src/main/java/com/wire/bots/hold/service/DeviceManagementService.java b/src/main/java/com/wire/bots/hold/service/DeviceManagementService.java index 8086e2c..1f655f7 100644 --- a/src/main/java/com/wire/bots/hold/service/DeviceManagementService.java +++ b/src/main/java/com/wire/bots/hold/service/DeviceManagementService.java @@ -67,6 +67,7 @@ public InitializedDeviceDTO initiateLegalHoldDevice(QualifiedId userId, UUID tea */ public void confirmDevice(QualifiedId userId, UUID teamId, String clientId, String refreshToken) { int insert = accessDAO.insert(userId.id, + userId.domain, clientId, refreshToken); @@ -95,7 +96,7 @@ public void removeDevice(QualifiedId userId, UUID teamId) throws IOException, Cr try (Crypto crypto = cf.create(userId)) { crypto.purge(); - int removeAccess = accessDAO.disable(userId.id); + int removeAccess = accessDAO.disable(userId.id, userId.domain); Logger.info( "RemoveResource: team: %s, user: %s, removed: %s", diff --git a/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java b/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java index db95d1c..1aef735 100644 --- a/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java +++ b/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java @@ -7,7 +7,6 @@ import com.wire.xenon.WireClient; import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.crypto.Crypto; -import com.wire.xenon.factories.CryptoFactory; import org.jdbi.v3.core.Jdbi; import javax.ws.rs.client.Client; @@ -16,10 +15,10 @@ public class HoldClientRepo { private final Jdbi jdbi; - private final CryptoFactory cf; + private final CryptoDatabaseFactory cf; private final Client httpClient; - public HoldClientRepo(Jdbi jdbi, CryptoFactory cf, Client httpClient) { + public HoldClientRepo(Jdbi jdbi, CryptoDatabaseFactory cf, Client httpClient) { this.jdbi = jdbi; this.cf = cf; this.httpClient = httpClient; diff --git a/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql b/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql new file mode 100644 index 0000000..db3da8e --- /dev/null +++ b/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql @@ -0,0 +1,16 @@ +ALTER TABLE Access +ADD COLUMN userDomain VARCHAR(255) DEFAULT null; + +ALTER TABLE Access +DROP CONSTRAINT IF EXISTS access_pkey; + +ALTER TABLE Access ADD CONSTRAINT access_user_id_user_domain_key UNIQUE (userId, userDomain); + +ALTER TABLE Events +ADD COLUMN userDomain VARCHAR(255) DEFAULT null; + +ALTER TABLE Events +ADD COLUMN conversationDomain VARCHAR(255) DEFAULT null; + +DROP INDEX conversation_id_idx; +CREATE INDEX events_conversation_id_conversation_domain_idx ON Events (conversationId, conversationDomain); diff --git a/src/test/java/com/wire/bots/hold/DatabaseTest.java b/src/test/java/com/wire/bots/hold/DatabaseTest.java index 3bb7f88..5f203ba 100644 --- a/src/test/java/com/wire/bots/hold/DatabaseTest.java +++ b/src/test/java/com/wire/bots/hold/DatabaseTest.java @@ -74,7 +74,7 @@ public void eventsTextMessageTest() throws JsonProcessingException { assert textMessage.getMessageId().equals(message.getMessageId()); - List events = eventsDAO.listAll(convId.id); + List events = eventsDAO.listAllDefaultDomain(convId.id); assert events.size() == 2; } @@ -97,14 +97,14 @@ public void assetsTest() { @Test public void accessTests() { - final UUID userId = UUID.randomUUID(); + final QualifiedId userId = new QualifiedId(UUID.randomUUID(), UUID.randomUUID().toString()); final String clientId = UUID.randomUUID().toString(); final String cookie = "cookie"; final UUID last = UUID.randomUUID(); final String cookie2 = "cookie2"; final String token = "token"; - final int insert = accessDAO.insert(userId, clientId, cookie); + final int insert = accessDAO.insert(userId.id, userId.domain, clientId, cookie); accessDAO.updateLast(userId, last); accessDAO.update(userId, token, cookie2); From 95156e486e8f7ebc434b065c3d1bbba0e5177c71 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 14 Oct 2024 12:34:30 +0200 Subject: [PATCH 2/3] feat(migrate-legalhold-database-to-support-qualified-ids) * Update LegalHold version to v1.1.0 * Update Dropwizard version to 2.1.12 * Adjust EventsDAO and AccessDAO queries to use ID and Domain * Adjust DAO query usages on ConversationResource and EventsResource * Change HoldWireClient to receive QualifiedID instead of UUID * From MessageHandler persist correct values of Conversation and User with qualified Ids --- pom.xml | 4 +- .../com/wire/bots/hold/DAO/EventsDAO.java | 10 +-- .../bots/hold/DAO/EventsResultSetMapper.java | 2 + .../wire/bots/hold/HoldMessageResource.java | 6 +- .../com/wire/bots/hold/MessageHandler.java | 68 ++++++++----------- .../wire/bots/hold/NotificationProcessor.java | 31 ++++----- .../v0/audit/ConversationResource.java | 7 +- .../resource/v0/audit/EventsResource.java | 12 ++-- .../wire/bots/hold/utils/HoldClientRepo.java | 5 +- .../wire/bots/hold/utils/HoldWireClient.java | 46 ++++++++++--- .../hold/ConfirmRemoveResourceV1Test.java | 4 +- .../java/com/wire/bots/hold/DatabaseTest.java | 24 +++---- 12 files changed, 119 insertions(+), 100 deletions(-) diff --git a/pom.xml b/pom.xml index 33772e3..772325c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.wire.bots hold - 1.0.7 + 1.1.0 Legal Hold Legal Hold Service For Wire @@ -26,7 +26,7 @@ UTF-8 UTF-8 - 2.1.2 + 2.1.12 1.0.10 0.16.0 diff --git a/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java b/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java index e79a1cb..3f780ad 100644 --- a/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java +++ b/src/main/java/com/wire/bots/hold/DAO/EventsDAO.java @@ -14,12 +14,14 @@ import java.util.UUID; public interface EventsDAO { - @SqlUpdate("INSERT INTO Events (eventId, conversationId, userId, type, payload, time) " + - "VALUES (:eventId, :conversationId, :userId, :type, to_jsonb(:payload)::json, CURRENT_TIMESTAMP) " + + @SqlUpdate("INSERT INTO Events (eventId, conversationId, conversationDomain, userId, userDomain, type, payload, time) " + + "VALUES (:eventId, :conversationId, :conversationDomain, :userId, :userDomain, :type, to_jsonb(:payload)::json, CURRENT_TIMESTAMP) " + "ON CONFLICT (eventId) DO NOTHING") int insert(@Bind("eventId") UUID eventId, @Bind("conversationId") UUID conversationId, + @Bind("conversationDomain") String conversationDomain, @Bind("userId") UUID userId, + @Bind("userDomain") String userDomain, @Bind("type") String type, @Bind("payload") String payload); @@ -27,7 +29,7 @@ int insert(@Bind("eventId") UUID eventId, @RegisterColumnMapper(EventsResultSetMapper.class) Event get(@Bind("eventId") UUID eventId); - @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND (conversationDomain is NULL OR conversationDomain = :conversationDomain) ORDER BY time DESC") + @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND (conversationDomain IS NULL OR conversationDomain = :conversationDomain) ORDER BY time DESC") @RegisterColumnMapper(EventsResultSetMapper.class) List listAllDefaultDomain(@Bind("conversationId") UUID conversationId, @Bind("conversationDomain") String conversationDomain); @@ -37,7 +39,7 @@ List listAllDefaultDomain(@Bind("conversationId") UUID conversationId, List listAll(@Bind("conversationId") UUID conversationId, @Bind("conversationDomain") String conversationDomain); - @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND (conversationDomain is NULL OR conversationDomain = :conversationDomain) ORDER BY time ASC") + @SqlQuery("SELECT * FROM Events WHERE conversationId = :conversationId AND (conversationDomain IS NULL OR conversationDomain = :conversationDomain) ORDER BY time ASC") @RegisterColumnMapper(EventsResultSetMapper.class) List listAllDefaultDomainAsc(@Bind("conversationId") UUID conversationId, @Bind("conversationDomain") String conversationDomain); diff --git a/src/main/java/com/wire/bots/hold/DAO/EventsResultSetMapper.java b/src/main/java/com/wire/bots/hold/DAO/EventsResultSetMapper.java index c2180cd..8b191ac 100644 --- a/src/main/java/com/wire/bots/hold/DAO/EventsResultSetMapper.java +++ b/src/main/java/com/wire/bots/hold/DAO/EventsResultSetMapper.java @@ -22,7 +22,9 @@ public Event map(ResultSet rs, int columnNumber, StatementContext ctx) throws SQ Event event = new Event(); event.eventId = (UUID) rs.getObject("eventId"); event.conversationId = getUuid(rs, "conversationId"); + event.conversationDomain = rs.getString("conversationDomain"); event.userId = getUuid(rs, "userId"); + event.userDomain = rs.getString("userDomain"); event.time = rs.getString("time"); event.type = rs.getString("type"); event.payload = getPayload(rs); diff --git a/src/main/java/com/wire/bots/hold/HoldMessageResource.java b/src/main/java/com/wire/bots/hold/HoldMessageResource.java index 3e455fd..cb3172c 100644 --- a/src/main/java/com/wire/bots/hold/HoldMessageResource.java +++ b/src/main/java/com/wire/bots/hold/HoldMessageResource.java @@ -6,6 +6,7 @@ import com.wire.xenon.MessageResourceBase; import com.wire.xenon.WireClient; import com.wire.xenon.backend.models.Payload; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.exceptions.MissingStateException; import com.wire.xenon.tools.Logger; @@ -19,12 +20,11 @@ public HoldMessageResource(MessageHandlerBase handler, HoldClientRepo repo) { this.repo = repo; } - protected WireClient getWireClient(UUID userId, Payload payload) throws CryptoException { + protected WireClient getWireClient(QualifiedId userId, Payload payload) throws CryptoException { return repo.getClient(userId, payload.data.recipient, payload.conversation); } - public boolean onNewMessage(UUID userId, UUID id, Payload payload) { - + public boolean onNewMessage(QualifiedId userId, UUID id, Payload payload) { try (WireClient client = getWireClient(userId, payload)) { handleMessage(id, payload, client); } catch (CryptoException | MissingStateException e) { diff --git a/src/main/java/com/wire/bots/hold/MessageHandler.java b/src/main/java/com/wire/bots/hold/MessageHandler.java index bf6a0a0..6f22d65 100644 --- a/src/main/java/com/wire/bots/hold/MessageHandler.java +++ b/src/main/java/com/wire/bots/hold/MessageHandler.java @@ -7,6 +7,7 @@ import com.wire.xenon.WireClient; import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.backend.models.SystemMessage; +import com.wire.xenon.backend.models.User; import com.wire.xenon.models.*; import com.wire.xenon.tools.Logger; import org.jdbi.v3.core.Jdbi; @@ -28,80 +29,72 @@ public class MessageHandler extends MessageHandlerBase { public void onNewConversation(WireClient client, SystemMessage msg) { UUID eventId = msg.id; QualifiedId conversationId = msg.conversation.id; - UUID userId = client.getId(); String type = msg.type; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onConversationRename(WireClient client, SystemMessage msg) { UUID eventId = msg.id; QualifiedId conversationId = msg.conversation.id; - UUID userId = client.getId(); String type = Const.CONVERSATION_RENAME; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onMemberJoin(WireClient client, SystemMessage msg) { UUID eventId = msg.id; QualifiedId conversationId = msg.conversation.id; - UUID userId = client.getId(); String type = msg.type; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onMemberLeave(WireClient client, SystemMessage msg) { UUID eventId = msg.id; QualifiedId conversationId = msg.conversation.id; - UUID userId = client.getId(); String type = msg.type; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onText(WireClient client, TextMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_NEW_TEXT; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onText(WireClient client, EphemeralTextMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_NEW_TEXT; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onEditText(WireClient client, EditedTextMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_EDIT_TEXT; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onPhotoPreview(WireClient client, PhotoPreviewMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_IMAGE_PREVIEW; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -110,10 +103,9 @@ public void onPhotoPreview(WireClient client, PhotoPreviewMessage msg) { public void onFilePreview(WireClient client, FilePreviewMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_FILE_PREVIEW; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -122,10 +114,9 @@ public void onFilePreview(WireClient client, FilePreviewMessage msg) { public void onAudioPreview(WireClient client, AudioPreviewMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_AUDIO_PREVIEW; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -134,10 +125,9 @@ public void onAudioPreview(WireClient client, AudioPreviewMessage msg) { public void onVideoPreview(WireClient client, VideoPreviewMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_VIDEO_PREVIEW; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); assetsDAO.insert(msg.getMessageId(), msg.getMimeType()); } @@ -146,15 +136,14 @@ public void onVideoPreview(WireClient client, VideoPreviewMessage msg) { public void onAssetData(WireClient client, RemoteMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_ASSET_DATA; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); try { final byte[] assetData = client.downloadAsset( msg.getAssetId(), - null, // TODO(WPB-11287): Change null to default domain + msg.getUserId().domain, msg.getAssetToken(), msg.getSha256(), msg.getOtrKey() @@ -169,29 +158,26 @@ public void onAssetData(WireClient client, RemoteMessage msg) { public void onDelete(WireClient client, DeletedTextMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_DELETE_TEXT; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override public void onCalling(WireClient client, CallingMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_CALL; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } public void onReaction(WireClient client, ReactionMessage msg) { UUID eventId = msg.getEventId(); QualifiedId conversationId = msg.getConversationId(); - UUID userId = client.getId(); String type = Const.CONVERSATION_OTR_MESSAGE_ADD_REACTION; - persist(eventId, conversationId.id, userId, type, msg); + persist(eventId, conversationId, client, type, msg); } @Override @@ -204,16 +190,20 @@ public void validatePreKeys(WireClient client, int size) { } - private void persist(UUID eventId, UUID convId, UUID userId, String type, Object msg) { + private void persist(UUID eventId, QualifiedId conversationId, WireClient client, String type, Object msg) { try { + User user = client.getSelf(); String payload = mapper.writeValueAsString(msg); - eventsDAO.insert(eventId, convId, userId, type, payload); - } catch (Exception e) { - Logger.exception(e, "%s: conv: %s, user: %s, id: %s, e: %s", - type, - convId, - userId, - eventId); + + eventsDAO.insert(eventId, conversationId.id, conversationId.domain, user.id.id, user.id.domain, type, payload); + } catch (Exception exception) { + Logger.exception( + exception, + "%s: conversation: %s, event: %s, e: %s", + type, + conversationId, + eventId + ); } } } diff --git a/src/main/java/com/wire/bots/hold/NotificationProcessor.java b/src/main/java/com/wire/bots/hold/NotificationProcessor.java index b28534f..0ca7e7e 100644 --- a/src/main/java/com/wire/bots/hold/NotificationProcessor.java +++ b/src/main/java/com/wire/bots/hold/NotificationProcessor.java @@ -10,6 +10,7 @@ import com.wire.helium.models.Event; import com.wire.helium.models.NotificationList; import com.wire.xenon.backend.models.Payload; +import com.wire.xenon.backend.models.QualifiedId; import com.wire.xenon.exceptions.AuthException; import com.wire.xenon.exceptions.HttpException; import com.wire.xenon.tools.Logger; @@ -53,12 +54,8 @@ private Access getAccess(Cookie cookie) throws HttpException { } private void process(LHAccess device) { - UUID userId = device.userId; - try { - final API api = new API(client, null, device.token); - - Logger.debug("`GET /notifications`: user: %s, last: %s", userId, device.last); + Logger.debug("`GET /notifications`: user: %s, last: %s", device.userId, device.last); String cookieValue = device.cookie; @@ -67,32 +64,33 @@ private void process(LHAccess device) { Access access = getAccess(cookie); if (access.getCookie() != null) { - Logger.info("Set-Cookie: user: %s", userId); + Logger.info("Set-Cookie: user: %s", device.userId); cookieValue = access.getCookie().value; } - accessDAO.update(userId, access.getAccessToken(), cookieValue); + accessDAO.update(device.userId.id, device.userId.domain, access.getAccessToken(), cookieValue); device.token = access.getAccessToken(); + final API api = new API(client, null, device.token); NotificationList notificationList = api.retrieveNotifications( device.clientId, device.last, DEFAULT_NOTIFICATION_SIZE ); - process(userId, notificationList); + process(device.userId, notificationList); } catch (AuthException e) { - accessDAO.disable(userId); - Logger.info("Disabled LH device for user: %s, error: %s", userId, e.getMessage()); + accessDAO.disable(device.userId.id, device.userId.domain); + Logger.exception(e, "NotificationProcessor: Disabled LH device for user: %s, error: %s", device.userId, e.getMessage()); } catch (HttpException e) { Logger.exception(e, "NotificationProcessor: Couldn't retrieve notifications, error: %s", e.getMessage()); } catch (Exception e) { - Logger.exception(e, "NotificationProcessor: user: %s, last: %s, error: %s", userId, device.last, e.getMessage()); + Logger.exception(e, "NotificationProcessor: user: %s, last: %s, error: %s", device.userId, device.last, e.getMessage()); } } - private void process(UUID userId, NotificationList notificationList) { + private void process(QualifiedId userId, NotificationList notificationList) { for (Event event : notificationList.notifications) { for (Payload payload : event.payload) { if (!process(userId, payload, event.id)) { @@ -106,17 +104,14 @@ private void process(UUID userId, NotificationList notificationList) { } } - accessDAO.updateLast(userId, event.id); + accessDAO.updateLast(userId.id, userId.domain, event.id); } } - private boolean process(UUID userId, Payload payload, UUID id) { + private boolean process(QualifiedId userId, Payload payload, UUID id) { trace(payload); - Logger.debug("Payload: %s %s, from: %s", - payload.type, - userId, - payload.from); + Logger.debug("Payload: %s %s, from: %s", payload.type, userId, payload.from); if (payload.from == null || payload.data == null) return true; diff --git a/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java b/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java index 0131b97..54a2e45 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/audit/ConversationResource.java @@ -65,8 +65,9 @@ public ConversationResource(Jdbi jdbi, Client httpClient) { public Response list(@ApiParam @PathParam("conversationId") UUID conversationId, @ApiParam @QueryParam("html") boolean isHtml) { try { - //TODO Get DEFAULT_DOMAIN, then fetch events with domain = null and domain = DEFAULT_DOMAIN - List events = eventsDAO.listAllDefaultDomainAsc(conversationId); + // TODO: When the parameters of this resource changes to accept a QualifiedId, then we will need + // to verify based on the domain which DAO query to call + List events = eventsDAO.listAllDefaultDomainAsc(conversationId, Cache.getFallbackDomain()); testAPI(); @@ -299,7 +300,7 @@ private API getLHApi() { private String formatConversation(Conversation conversation) { StringBuilder sb = new StringBuilder(); - QualifiedId creatorId = new QualifiedId(conversation.creator, null); // TODO(WPB-11287): Change null to default domain + QualifiedId creatorId = new QualifiedId(conversation.creator, Cache.getFallbackDomain()); sb.append(String.format("**%s** created conversation **%s** with: \n", getUserName(creatorId), conversation.name)); diff --git a/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java b/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java index 077a2fb..873d39d 100644 --- a/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java +++ b/src/main/java/com/wire/bots/hold/resource/v0/audit/EventsResource.java @@ -6,6 +6,7 @@ import com.wire.bots.hold.DAO.EventsDAO; import com.wire.bots.hold.filters.ServiceAuthorization; import com.wire.bots.hold.model.database.Event; +import com.wire.bots.hold.utils.Cache; import com.wire.xenon.tools.Logger; import io.swagger.annotations.*; @@ -40,18 +41,19 @@ public EventsResource(EventsDAO eventsDAO) { @ApiResponse(code = 200, message = "Wire events")}) public Response list(@ApiParam @PathParam("conversationId") UUID conversationId) { try { - //TODO Get DEFAULT_DOMAIN, then fetch events with domain = null and domain = DEFAULT_DOMAIN + // TODO: When the parameters of this resource changes to accept a QualifiedId, then we will need + // to verify based on the domain which DAO query to call Model model = new Model(); - model.events = eventsDAO.listAllDefaultDomain(conversationId); + model.events = eventsDAO.listAllDefaultDomain(conversationId, Cache.getFallbackDomain()); String html = execute(model); return Response. ok(html, MediaType.TEXT_HTML). build(); - } catch (Exception e) { - Logger.exception("EventsResource.list: %s", e, e.getMessage()); + } catch (Exception exception) { + Logger.exception(exception, "EventsResource.list: %s", exception.getMessage()); return Response - .ok(e.getMessage()) + .ok(exception.getMessage()) .status(500) .build(); } diff --git a/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java b/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java index 1aef735..45b8fcd 100644 --- a/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java +++ b/src/main/java/com/wire/bots/hold/utils/HoldClientRepo.java @@ -10,7 +10,6 @@ import org.jdbi.v3.core.Jdbi; import javax.ws.rs.client.Client; -import java.util.UUID; public class HoldClientRepo { @@ -24,9 +23,9 @@ public HoldClientRepo(Jdbi jdbi, CryptoDatabaseFactory cf, Client httpClient) { this.httpClient = httpClient; } - public WireClient getClient(UUID userId, String deviceId, QualifiedId conversationId) throws CryptoException { + public WireClient getClient(QualifiedId userId, String deviceId, QualifiedId conversationId) throws CryptoException { Crypto crypto = cf.create(userId); - final LHAccess single = jdbi.onDemand(AccessDAO.class).get(userId); + final LHAccess single = jdbi.onDemand(AccessDAO.class).get(userId.id, userId.domain); final API api = new API(httpClient, conversationId, single.token); return new HoldWireClient(userId, deviceId, conversationId, crypto, api); } diff --git a/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java b/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java index 2ad4701..0e31a47 100644 --- a/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java +++ b/src/main/java/com/wire/bots/hold/utils/HoldWireClient.java @@ -15,20 +15,37 @@ public class HoldWireClient extends WireClientBase implements WireClient { - private final UUID userId; - private final QualifiedId convId; + private final User user; + private final QualifiedId conversationId; private final String deviceId; - HoldWireClient(UUID userId, String deviceId, QualifiedId convId, Crypto crypto, API api) { + HoldWireClient(QualifiedId userId, String deviceId, QualifiedId convId, Crypto crypto, API api) { super(api, crypto, null); - this.userId = userId; - this.convId = convId; + + User user = new User(); + user.id = userId; + this.user = user; + + this.conversationId = convId; this.deviceId = deviceId; } + /** + *

+ * This method used to return the direct UUID (userId) saved in this Class, but userId is now changed to User. + * User contains the new QualifiedId, consisting of ID and Domain. + *

+ *

+ * As this method is still used in Xenon, this method needs to return something valid. + *

+ * + * @return UUID from User Qualified ID + * @Deprecated Use getSelf() instead + */ + @Deprecated @Override public UUID getId() { - return userId; + return user.id.id; } @Override @@ -38,14 +55,26 @@ public String getDeviceId() { @Override public QualifiedId getConversationId() { - return convId; + return conversationId; } //////////////////////////////////////////////////////////// + /** + *

+ * This method returns a User object with minimal data (only containing its QualifiedId) and deprecates the + * `getId()` method in this class, as previously by chance both User and Bot/Service only had one type of ID. + *

+ *

+ * Now, Users contain a combination of UUID(id) and String(domain) to compose the User ID, whilst Bot/Service + * still uses the same UUID(id) type. + *

+ * + * @return User with minimal data. + */ @Override public User getSelf() { - return null; + return user; } @Override @@ -72,5 +101,4 @@ public byte[] downloadProfilePicture(String assetKey, String domain) { public AssetKey uploadAsset(IAsset asset) { return null; } - } diff --git a/src/test/java/com/wire/bots/hold/ConfirmRemoveResourceV1Test.java b/src/test/java/com/wire/bots/hold/ConfirmRemoveResourceV1Test.java index 41474f1..b5d4de1 100644 --- a/src/test/java/com/wire/bots/hold/ConfirmRemoveResourceV1Test.java +++ b/src/test/java/com/wire/bots/hold/ConfirmRemoveResourceV1Test.java @@ -91,7 +91,7 @@ public void givenRepeatedConfirm_ReturnOk() { assert confirmSecondResponse.getStatus() == HttpStatus.SC_OK; // Confirm the same device is possible and the latest refresh_token should be persisted - final LHAccess lhAccess = accessDAO.get(userId.id); + final LHAccess lhAccess = accessDAO.get(userId.id, userId.domain); assert lhAccess != null; assert lhAccess.cookie.equals(updatedRefreshToken); assert !lhAccess.created.equals(lhAccess.updated); @@ -121,7 +121,7 @@ private Response postConfirmV1(ConfirmPayloadV1 confirmPayloadV1){ .post(Entity.entity(confirmPayloadV1, MediaType.APPLICATION_JSON)); } - private Response postRemoveV1(InitPayloadV1 initPayloadV1){ + private Response postRemoveV1(InitPayloadV1 initPayloadV1) { return client.target("http://localhost:" + SUPPORT.getLocalPort()) .path("v1") .path("remove") diff --git a/src/test/java/com/wire/bots/hold/DatabaseTest.java b/src/test/java/com/wire/bots/hold/DatabaseTest.java index 5f203ba..e2ec5d4 100644 --- a/src/test/java/com/wire/bots/hold/DatabaseTest.java +++ b/src/test/java/com/wire/bots/hold/DatabaseTest.java @@ -52,20 +52,20 @@ public void eventsTextMessageTest() throws JsonProcessingException { final String type = "conversation.otr-message-add.new-text"; final UUID eventId = UUID.randomUUID(); final UUID messageId = UUID.randomUUID(); - final QualifiedId convId = new QualifiedId(UUID.randomUUID(), null); + final QualifiedId convId = new QualifiedId(UUID.randomUUID(), "dummy_domain"); final String clientId = UUID.randomUUID().toString(); - final QualifiedId userId = new QualifiedId(UUID.randomUUID(), null); + final QualifiedId userId = new QualifiedId(UUID.randomUUID(), "dummy_domain"); final String time = new Date().toString(); final TextMessage textMessage = new TextMessage(eventId, messageId, convId, clientId, userId, time); - textMessage.addMention(new QualifiedId(UUID.randomUUID(), null), 0, 5); + textMessage.addMention(new QualifiedId(UUID.randomUUID(), "dummy_domain"), 0, 5); textMessage.setText("Some text"); String payload = mapper.writeValueAsString(textMessage); - int insert = eventsDAO.insert(eventId, convId.id, userId.id, type, payload); + int insert = eventsDAO.insert(eventId, convId.id, convId.domain, userId.id, userId.domain, type, payload); assert insert == 1; - insert = eventsDAO.insert(UUID.randomUUID(), convId.id, userId.id, type, payload); + insert = eventsDAO.insert(UUID.randomUUID(), convId.id, convId.domain, userId.id, userId.domain, type, payload); assert insert == 1; final Event event = eventsDAO.get(eventId); @@ -74,7 +74,7 @@ public void eventsTextMessageTest() throws JsonProcessingException { assert textMessage.getMessageId().equals(message.getMessageId()); - List events = eventsDAO.listAllDefaultDomain(convId.id); + List events = eventsDAO.listAllDefaultDomain(convId.id, convId.domain); assert events.size() == 2; } @@ -105,17 +105,17 @@ public void accessTests() { final String token = "token"; final int insert = accessDAO.insert(userId.id, userId.domain, clientId, cookie); - accessDAO.updateLast(userId, last); - accessDAO.update(userId, token, cookie2); + accessDAO.updateLast(userId.id, userId.domain, last); + accessDAO.update(userId.id, userId.domain, token, cookie2); - final LHAccess lhAccess = accessDAO.get(userId); + final LHAccess lhAccess = accessDAO.get(userId.id, userId.domain); assert lhAccess != null; assert lhAccess.userId.equals(userId); - accessDAO.disable(userId); + accessDAO.disable(userId.id, userId.domain); - final int insert2 = accessDAO.insert(userId, clientId, cookie); - final LHAccess lhAccess2 = accessDAO.get(userId); + final int insert2 = accessDAO.insert(userId.id, userId.domain, clientId, cookie); + final LHAccess lhAccess2 = accessDAO.get(userId.id, userId.domain); assert lhAccess2 != null; assert lhAccess2.created.equals(lhAccess.created); } From 2d6e7c0c62f6c9710cfa243c2a33429ea59a36f6 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 14 Oct 2024 17:22:45 +0200 Subject: [PATCH 3/3] feat(migrate-legalhold-database-to-support-qualified-ids) * Update LegalHold version to v1.1.0 * Update Dropwizard version to 2.1.12 * Adjust EventsDAO and AccessDAO queries to use ID and Domain * Adjust DAO query usages on ConversationResource and EventsResource * Change HoldWireClient to receive QualifiedID instead of UUID * From MessageHandler persist correct values of Conversation and User with qualified Ids * Remove last TODO for adding qualified domain * Comment decision on removing PRIMARY KEY and adding UNIQUE to Access DB table --- src/main/java/com/wire/bots/hold/utils/Collector.java | 6 +++--- .../migration/V108__add_domain_column_in_access_events.sql | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/wire/bots/hold/utils/Collector.java b/src/main/java/com/wire/bots/hold/utils/Collector.java index 3ab7814..ab54e64 100644 --- a/src/main/java/com/wire/bots/hold/utils/Collector.java +++ b/src/main/java/com/wire/bots/hold/utils/Collector.java @@ -68,7 +68,7 @@ public void addSystem(String text, String dateTime, String type) throws ParseExc private Sender sender(User user, Message message) { Sender sender = new Sender(); - sender.senderId = user.id; + sender.senderId = user.id.toString(); sender.name = user.name; sender.accent = toColor(user.accent); sender.avatar = getAvatar(user); @@ -79,7 +79,7 @@ private Sender sender(User user, Message message) { private Sender system(Message message, String type) { Sender sender = new Sender(); sender.system = "system"; - sender.senderId = new QualifiedId(UUID.randomUUID(), null); // TODO(WPB-11287): Change null to default domain + sender.senderId = "system"; sender.avatar = systemIcon(type); sender.messages.add(message); return sender; @@ -213,7 +213,7 @@ public static class Message { } public static class Sender { - QualifiedId senderId; + String senderId; String avatar; String name; String accent; diff --git a/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql b/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql index db3da8e..256caab 100644 --- a/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql +++ b/src/main/resources/db/migration/V108__add_domain_column_in_access_events.sql @@ -1,6 +1,9 @@ ALTER TABLE Access ADD COLUMN userDomain VARCHAR(255) DEFAULT null; +-- Dropping the PRIMARY KEY and adding a UNIQUE constraint with both Id and Domain, +-- because Postgresql doesn't like a nullable fields being PRIMARY KEY. +-- (previous tentative: PRIMARY KEY (userId, userDomain) ALTER TABLE Access DROP CONSTRAINT IF EXISTS access_pkey;