Skip to content

Commit 948e577

Browse files
committed
moved data storage from hazelcast to postgres database
1 parent 30777d2 commit 948e577

File tree

14 files changed

+169
-182
lines changed

14 files changed

+169
-182
lines changed

captchaservice-backend/pom.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,6 @@
313313
<artifactId>altcha</artifactId>
314314
<version>1.1.2</version>
315315
</dependency>
316-
<dependency>
317-
<groupId>com.hazelcast</groupId>
318-
<artifactId>hazelcast</artifactId>
319-
<version>5.3.8</version>
320-
</dependency>
321316
</dependencies>
322317

323318
<scm>

captchaservice-backend/src/main/java/de/muenchen/captchaservice/common/HazelcastConstants.java

Lines changed: 0 additions & 6 deletions
This file was deleted.

captchaservice-backend/src/main/java/de/muenchen/captchaservice/configuration/hazelcast/HazelcastConfiguration.java

Lines changed: 0 additions & 82 deletions
This file was deleted.

captchaservice-backend/src/main/java/de/muenchen/captchaservice/configuration/hazelcast/HazelcastProperties.java

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package de.muenchen.captchaservice.entity;
2+
3+
import de.muenchen.captchaservice.common.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.Index;
7+
import jakarta.persistence.Table;
8+
import jakarta.validation.constraints.NotNull;
9+
import jakarta.validation.constraints.Size;
10+
import lombok.*;
11+
12+
import java.time.Instant;
13+
14+
@Entity
15+
@Table(
16+
indexes = {
17+
@Index(name = "idx_captcha_request_source_address_hash", columnList = "sourceAddressHash")
18+
}
19+
)
20+
21+
// Definition of getter, setter, ...
22+
@Getter
23+
@Setter
24+
@ToString(callSuper = true)
25+
@EqualsAndHashCode(callSuper = true)
26+
@NoArgsConstructor
27+
@AllArgsConstructor
28+
public class CaptchaRequest extends BaseEntity {
29+
30+
private static final long serialVersionUID = 1L;
31+
32+
// ========= //
33+
// Variables //
34+
// ========= //
35+
36+
@Column(nullable = false, length = 64)
37+
@NotNull
38+
@Size(min = 64, max = 64)
39+
private String sourceAddressHash;
40+
41+
@NotNull
42+
private Instant validUntil;
43+
44+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package de.muenchen.captchaservice.entity;
2+
3+
import de.muenchen.captchaservice.common.BaseEntity;
4+
import jakarta.persistence.Column;
5+
import jakarta.persistence.Entity;
6+
import jakarta.persistence.Index;
7+
import jakarta.persistence.Table;
8+
import jakarta.validation.constraints.NotNull;
9+
import jakarta.validation.constraints.Size;
10+
import lombok.*;
11+
12+
import java.time.Instant;
13+
14+
@Entity
15+
@Table(
16+
indexes = {
17+
@Index(name = "idx_invalidated_payload_payload_hash", columnList = "payloadHash")
18+
}
19+
)
20+
// Definition of getter, setter, ...
21+
@Getter
22+
@Setter
23+
@ToString(callSuper = true)
24+
@EqualsAndHashCode(callSuper = true)
25+
@NoArgsConstructor
26+
@AllArgsConstructor
27+
public class InvalidatedPayload extends BaseEntity {
28+
29+
private static final long serialVersionUID = 1L;
30+
31+
// ========= //
32+
// Variables //
33+
// ========= //
34+
35+
@Column(nullable = false, length = 64)
36+
@NotNull
37+
@Size(min = 64, max = 64)
38+
private String payloadHash;
39+
40+
@NotNull
41+
private Instant validUntil;
42+
43+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package de.muenchen.captchaservice.repository;
2+
3+
import de.muenchen.captchaservice.entity.CaptchaRequest;
4+
import org.springframework.data.repository.CrudRepository;
5+
import org.springframework.data.repository.PagingAndSortingRepository;
6+
7+
import java.time.Instant;
8+
import java.util.UUID;
9+
10+
public interface CaptchaRequestRepository extends PagingAndSortingRepository<CaptchaRequest, UUID>, CrudRepository<CaptchaRequest, UUID> {
11+
long countBySourceAddressHashIgnoreCaseAndValidUntilGreaterThanEqual(String sourceAddressHash, Instant validUntil);
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package de.muenchen.captchaservice.repository;
2+
3+
import de.muenchen.captchaservice.entity.InvalidatedPayload;
4+
import org.springframework.data.repository.CrudRepository;
5+
import org.springframework.data.repository.PagingAndSortingRepository;
6+
7+
import java.time.Instant;
8+
import java.util.UUID;
9+
10+
public interface InvalidatedPayloadRepository extends PagingAndSortingRepository<InvalidatedPayload, UUID>, CrudRepository<InvalidatedPayload, UUID> {
11+
long countByPayloadHashIgnoreCaseAndValidUntilGreaterThanEqual(String payloadHash, Instant validUntil);
12+
}

captchaservice-backend/src/main/java/de/muenchen/captchaservice/service/captcha/CaptchaService.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,36 @@
11
package de.muenchen.captchaservice.service.captcha;
22

3-
import com.hazelcast.core.HazelcastInstance;
4-
import com.hazelcast.map.IMap;
5-
import de.muenchen.captchaservice.common.HazelcastConstants;
63
import de.muenchen.captchaservice.configuration.captcha.CaptchaProperties;
74
import de.muenchen.captchaservice.configuration.captcha.CaptchaSite;
85
import de.muenchen.captchaservice.data.SourceAddress;
6+
import de.muenchen.captchaservice.entity.InvalidatedPayload;
7+
import de.muenchen.captchaservice.repository.InvalidatedPayloadRepository;
98
import de.muenchen.captchaservice.service.difficulty.DifficultyService;
109
import lombok.extern.slf4j.Slf4j;
1110
import org.altcha.altcha.Altcha;
1211
import org.apache.commons.codec.digest.DigestUtils;
1312
import org.springframework.stereotype.Service;
1413

15-
import java.util.UUID;
16-
import java.util.concurrent.TimeUnit;
14+
import java.time.Instant;
1715

1816
@Service
1917
@Slf4j
2018
public class CaptchaService {
2119

2220
private final CaptchaProperties captchaProperties;
23-
private final IMap<String, String> invalidatedPayloads;
21+
private final InvalidatedPayloadRepository invalidatedPayloadRepository;
2422
private final DifficultyService difficultyService;
2523

26-
public CaptchaService(final CaptchaProperties captchaProperties, final DifficultyService difficultyService, final HazelcastInstance hazelcastInstance) {
24+
public CaptchaService(final CaptchaProperties captchaProperties, final DifficultyService difficultyService,
25+
InvalidatedPayloadRepository invalidatedPayloadRepository) {
2726
this.captchaProperties = captchaProperties;
27+
this.invalidatedPayloadRepository = invalidatedPayloadRepository;
2828
this.difficultyService = difficultyService;
29-
invalidatedPayloads = hazelcastInstance.getMap(HazelcastConstants.INVALIDATED_PAYLOADS);
3029
}
3130

3231
public Altcha.Challenge createChallenge(final String siteKey, final SourceAddress sourceAddress) {
3332
final long difficulty = difficultyService.getDifficultyForSourceAddress(siteKey, sourceAddress);
34-
difficultyService.pokeSourceAddress(sourceAddress);
33+
difficultyService.registerRequest(sourceAddress);
3534
final Altcha.ChallengeOptions options = new Altcha.ChallengeOptions();
3635
options.algorithm = Altcha.Algorithm.SHA256;
3736
options.hmacKey = captchaProperties.hmacKey();
@@ -63,15 +62,15 @@ public boolean verify(final String siteKey, final Altcha.Payload payload) {
6362

6463
public void invalidatePayload(final Altcha.Payload payload) {
6564
final String payloadHash = getPayloadHash(payload);
66-
invalidatedPayloads.set(String.format("%s_%s_%s", payloadHash, System.currentTimeMillis(), UUID.randomUUID()), "",
67-
captchaProperties.captchaTimeoutSeconds(), TimeUnit.SECONDS);
65+
final InvalidatedPayload invalidatedPayload = new InvalidatedPayload(payloadHash, Instant.now().plusSeconds(captchaProperties.captchaTimeoutSeconds()));
66+
invalidatedPayloadRepository.save(invalidatedPayload);
6867
log.debug("Invalidated payloadHash: {}", payloadHash);
6968
}
7069

7170
public boolean isPayloadInvalidated(final String siteKey, final Altcha.Payload payload) {
7271
final CaptchaSite site = captchaProperties.sites().get(siteKey);
7372
final String payloadHash = getPayloadHash(payload);
74-
final long payloadHashCount = invalidatedPayloads.keySet().stream().filter(s -> s.startsWith(String.format("%s_", payloadHash))).count();
73+
final long payloadHashCount = invalidatedPayloadRepository.countByPayloadHashIgnoreCaseAndValidUntilGreaterThanEqual(payloadHash, Instant.now());
7574
return payloadHashCount >= site.maxVerifiesPerPayload();
7675
}
7776

captchaservice-backend/src/main/java/de/muenchen/captchaservice/service/difficulty/DifficultyService.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
11
package de.muenchen.captchaservice.service.difficulty;
22

3-
import com.hazelcast.core.HazelcastInstance;
4-
import com.hazelcast.map.IMap;
5-
import de.muenchen.captchaservice.common.HazelcastConstants;
63
import de.muenchen.captchaservice.configuration.captcha.CaptchaProperties;
74
import de.muenchen.captchaservice.configuration.captcha.CaptchaSite;
85
import de.muenchen.captchaservice.configuration.captcha.DifficultyItem;
96
import de.muenchen.captchaservice.data.SourceAddress;
7+
import de.muenchen.captchaservice.entity.CaptchaRequest;
8+
import de.muenchen.captchaservice.repository.CaptchaRequestRepository;
109
import lombok.extern.slf4j.Slf4j;
1110
import org.springframework.stereotype.Service;
1211

12+
import java.time.Instant;
1313
import java.util.Optional;
14-
import java.util.UUID;
15-
import java.util.concurrent.TimeUnit;
1614

1715
@Service
1816
@Slf4j
1917
public class DifficultyService {
2018

2119
private final CaptchaProperties captchaProperties;
22-
private final IMap<String, String> sourceAddresses;
2320

24-
public DifficultyService(final CaptchaProperties captchaProperties, final HazelcastInstance hazelcastInstance) {
21+
private final CaptchaRequestRepository captchaRequestRepository;
22+
23+
public DifficultyService(final CaptchaProperties captchaProperties, CaptchaRequestRepository captchaRequestRepository) {
2524
this.captchaProperties = captchaProperties;
26-
sourceAddresses = hazelcastInstance.getMap(HazelcastConstants.SOURCE_ADDRESSES);
25+
this.captchaRequestRepository = captchaRequestRepository;
2726
}
2827

29-
public void pokeSourceAddress(final SourceAddress sourceAddress) {
28+
public void registerRequest(final SourceAddress sourceAddress) {
3029
final String sourceAddressHash = sourceAddress.getHash();
31-
sourceAddresses.set(String.format("%s_%s_%s", sourceAddressHash, System.currentTimeMillis(), UUID.randomUUID()), "",
32-
captchaProperties.sourceAddressWindowSeconds(), TimeUnit.SECONDS);
30+
final CaptchaRequest captchaRequest = new CaptchaRequest(sourceAddressHash,
31+
Instant.now().plusSeconds(captchaProperties.sourceAddressWindowSeconds()));
32+
captchaRequestRepository.save(captchaRequest);
33+
log.debug("Poked source address with hash {}", sourceAddressHash);
3334
}
3435

3536
public long getDifficultyForSourceAddress(final String siteKey, final SourceAddress sourceAddress) {
@@ -38,7 +39,8 @@ public long getDifficultyForSourceAddress(final String siteKey, final SourceAddr
3839
throw new IllegalArgumentException("siteKey not found");
3940
}
4041
final String sourceAddressHash = sourceAddress.getHash();
41-
final long sourceVisitCount = sourceAddresses.keySet().stream().filter(s -> s.startsWith(String.format("%s_", sourceAddressHash))).count();
42+
final long sourceVisitCount = captchaRequestRepository.countBySourceAddressHashIgnoreCaseAndValidUntilGreaterThanEqual(sourceAddressHash,
43+
Instant.now());
4244
final Optional<DifficultyItem> difficultyItem = captchaSite
4345
.difficultyMap()
4446
.stream()

0 commit comments

Comments
 (0)