Skip to content

Commit 4b39b08

Browse files
committed
Move metrics into separate service and add clientAddress to PostVerifyRequest
1 parent 046da19 commit 4b39b08

File tree

5 files changed

+94
-42
lines changed

5 files changed

+94
-42
lines changed

captchaservice-backend/src/main/java/de/muenchen/captchaservice/controller/captcha/CaptchaController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public PostVerifyResponse postVerify(@Valid @RequestBody final PostVerifyRequest
4545
throw new UnauthorizedException("Wrong credentials.");
4646
}
4747

48-
final boolean isValid = captchaService.verify(request.getSiteKey(), request.getPayload());
48+
final SourceAddress sourceAddress = sourceAddressService.parse(request.getSiteKey(), request.getClientAddress());
49+
final boolean isValid = captchaService.verify(request.getSiteKey(), request.getPayload(), sourceAddress);
4950
return new PostVerifyResponse(isValid);
5051
}
5152
}

captchaservice-backend/src/main/java/de/muenchen/captchaservice/controller/captcha/request/PostVerifyRequest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package de.muenchen.captchaservice.controller.captcha.request;
22

3+
import de.muenchen.captchaservice.validation.ValidSourceAddress;
34
import jakarta.validation.constraints.NotBlank;
45
import jakarta.validation.constraints.NotNull;
56
import lombok.AllArgsConstructor;
@@ -21,6 +22,11 @@ public class PostVerifyRequest {
2122
@NotBlank
2223
private String siteSecret;
2324

25+
@NotNull
26+
@NotBlank
27+
@ValidSourceAddress
28+
private String clientAddress;
29+
2430
@NotNull
2531
private ExtendedPayload payload;
2632

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

Lines changed: 12 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,6 @@
88
import de.muenchen.captchaservice.repository.InvalidatedPayloadRepository;
99
import de.muenchen.captchaservice.service.difficulty.DifficultyService;
1010
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
11-
import io.micrometer.core.instrument.MeterRegistry;
12-
import io.micrometer.core.instrument.Counter;
13-
import io.micrometer.core.instrument.DistributionSummary;
14-
import io.micrometer.core.instrument.Gauge;
15-
import java.util.concurrent.atomic.AtomicLong;
1611
import lombok.extern.slf4j.Slf4j;
1712
import org.altcha.altcha.Altcha;
1813
import org.apache.commons.codec.digest.DigestUtils;
@@ -27,39 +22,24 @@ public class CaptchaService {
2722
private final CaptchaProperties captchaProperties;
2823
private final InvalidatedPayloadRepository invalidatedPayloadRepository;
2924
private final DifficultyService difficultyService;
30-
private final Counter challengeCounter;
31-
private final Counter verifySuccessCounter;
32-
private final DistributionSummary tookTimeSummary;
33-
private final AtomicLong invalidatedPayloadCount = new AtomicLong(0);
25+
private final MetricsService metricsService;
3426

3527
@SuppressFBWarnings(value = { "EI_EXPOSE_REP2" }, justification = "Dependency Injection")
3628
public CaptchaService(final CaptchaProperties captchaProperties, final DifficultyService difficultyService,
37-
final InvalidatedPayloadRepository invalidatedPayloadRepository, MeterRegistry registry) {
29+
final InvalidatedPayloadRepository invalidatedPayloadRepository, MetricsService metricsService) {
3830
this.captchaProperties = captchaProperties;
3931
this.invalidatedPayloadRepository = invalidatedPayloadRepository;
4032
this.difficultyService = difficultyService;
33+
this.metricsService = metricsService;
4134

42-
// Initialize counter with current count from database
43-
this.invalidatedPayloadCount.set(invalidatedPayloadRepository.countByExpiresAtGreaterThan(Instant.now()));
44-
45-
// Initialize metrics
46-
this.challengeCounter = Counter.builder("captcha.challenge.requests")
47-
.description("Counter for captcha challenge requests")
48-
.register(registry);
49-
this.verifySuccessCounter = Counter.builder("captcha.verify.success")
50-
.description("Counter for captcha verify success requests")
51-
.register(registry);
52-
this.tookTimeSummary = DistributionSummary.builder("captcha.verify.took.time")
53-
.description("Summary of the time taken to verify captcha payloads")
54-
.register(registry);
55-
Gauge.builder("captcha.invalidated.payloads", invalidatedPayloadCount, AtomicLong::get)
56-
.description("Gauge for the number of currently invalidated payloads")
57-
.register(registry);
35+
metricsService.initializeInvalidatedPayloadsGauge();
5836
}
5937

6038
public Altcha.Challenge createChallenge(final String siteKey, final SourceAddress sourceAddress) {
61-
challengeCounter.increment();
6239
final long difficulty = difficultyService.getDifficultyForSourceAddress(siteKey, sourceAddress);
40+
41+
metricsService.recordChallengeRequest(siteKey, sourceAddress);
42+
6343
difficultyService.registerRequest(siteKey, sourceAddress);
6444
final Altcha.ChallengeOptions options = new Altcha.ChallengeOptions();
6545
options.algorithm = Altcha.Algorithm.SHA256;
@@ -74,17 +54,19 @@ public Altcha.Challenge createChallenge(final String siteKey, final SourceAddres
7454
return null;
7555
}
7656

77-
public boolean verify(final String siteKey, final ExtendedPayload payload) {
57+
public boolean verify(final String siteKey, final ExtendedPayload payload, final SourceAddress sourceAddress) {
7858
if (isPayloadInvalidated(siteKey, payload)) {
7959
return false;
8060
}
8161
try {
8262
final boolean isValid = Altcha.verifySolution(payload, captchaProperties.hmacKey(), true);
8363
if (isValid) {
64+
metricsService.recordVerifySuccess(siteKey, sourceAddress);
65+
8466
if (payload.getTook() != null) {
85-
tookTimeSummary.record(payload.getTook());
67+
metricsService.recordClientSolveTime(siteKey, sourceAddress, payload.getTook());
8668
}
87-
verifySuccessCounter.increment();
69+
8870
invalidatePayload(payload);
8971
}
9072
return isValid;
@@ -97,7 +79,6 @@ public boolean verify(final String siteKey, final ExtendedPayload payload) {
9779
public void invalidatePayload(final Altcha.Payload payload) {
9880
final String payloadHash = getPayloadHash(payload);
9981
final InvalidatedPayload invalidatedPayload = new InvalidatedPayload(payloadHash, Instant.now().plusSeconds(captchaProperties.captchaTimeoutSeconds()));
100-
invalidatedPayloadCount.incrementAndGet();
10182
invalidatedPayloadRepository.save(invalidatedPayload);
10283
log.debug("Invalidated payloadHash: {}", payloadHash);
10384
}
@@ -118,13 +99,4 @@ private static String getPayloadHash(final Altcha.Payload payload) {
11899
payload.salt,
119100
payload.signature));
120101
}
121-
122-
public void decrementInvalidatedPayloadCount(long count) {
123-
invalidatedPayloadCount.addAndGet(-count);
124-
}
125-
126-
public void resetInvalidatedPayloadCount() {
127-
this.invalidatedPayloadCount.set(
128-
invalidatedPayloadRepository.countByExpiresAtGreaterThan(java.time.Instant.now()));
129-
}
130102
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package de.muenchen.captchaservice.service.captcha;
2+
3+
import de.muenchen.captchaservice.data.SourceAddress;
4+
import de.muenchen.captchaservice.repository.InvalidatedPayloadRepository;
5+
import de.muenchen.captchaservice.service.difficulty.DifficultyService;
6+
import io.micrometer.core.instrument.Counter;
7+
import io.micrometer.core.instrument.DistributionSummary;
8+
import io.micrometer.core.instrument.Gauge;
9+
import io.micrometer.core.instrument.MeterRegistry;
10+
import lombok.extern.slf4j.Slf4j;
11+
import org.springframework.stereotype.Service;
12+
13+
import java.time.Instant;
14+
15+
@Service
16+
@Slf4j
17+
public class MetricsService {
18+
19+
private final MeterRegistry meterRegistry;
20+
private final DifficultyService difficultyService;
21+
private final InvalidatedPayloadRepository invalidatedPayloadRepository;
22+
23+
public MetricsService(MeterRegistry meterRegistry, DifficultyService difficultyService,
24+
InvalidatedPayloadRepository invalidatedPayloadRepository) {
25+
this.meterRegistry = meterRegistry;
26+
this.difficultyService = difficultyService;
27+
this.invalidatedPayloadRepository = invalidatedPayloadRepository;
28+
}
29+
30+
public void recordChallengeRequest(String siteKey, SourceAddress sourceAddress) {
31+
final long difficulty = difficultyService.getDifficultyForSourceAddress(siteKey, sourceAddress);
32+
33+
Counter.builder("captcha.challenge.requests")
34+
.tag("site_key", siteKey)
35+
.tag("difficulty", String.valueOf(difficulty))
36+
.description("Counter for captcha challenge requests")
37+
.register(meterRegistry)
38+
.increment();
39+
}
40+
41+
public void recordVerifySuccess(String siteKey, SourceAddress sourceAddress) {
42+
final long difficulty = difficultyService.getDifficultyForSourceAddress(siteKey, sourceAddress);
43+
44+
Counter.builder("captcha.verify.success")
45+
.tag("site_key", siteKey)
46+
.tag("difficulty", String.valueOf(difficulty))
47+
.description("Counter for captcha verify success requests")
48+
.register(meterRegistry)
49+
.increment();
50+
}
51+
52+
public void recordClientSolveTime(String siteKey, SourceAddress sourceAddress, long solveTime) {
53+
if (solveTime <= 0) {
54+
log.warn("Invalid solve time value: {} for site: {}", solveTime, siteKey);
55+
return;
56+
}
57+
58+
final long difficulty = difficultyService.getDifficultyForSourceAddress(siteKey, sourceAddress);
59+
60+
DistributionSummary.builder("captcha.client.solve.time")
61+
.tag("site_key", siteKey)
62+
.tag("difficulty", String.valueOf(difficulty))
63+
.description("Summary of the time taken by clients to solve captcha challenges")
64+
.register(meterRegistry)
65+
.record(solveTime);
66+
}
67+
68+
public void initializeInvalidatedPayloadsGauge() {
69+
Gauge.builder("captcha.invalidated.payloads",
70+
() -> invalidatedPayloadRepository.countByExpiresAtGreaterThan(Instant.now()))
71+
.description("Gauge for the number of currently invalidated payloads")
72+
.register(meterRegistry);
73+
}
74+
}

captchaservice-backend/src/main/java/de/muenchen/captchaservice/service/expireddata/ExpiredDataService.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ public void deleteExpiredData() {
3535
final long deletedInvalidatedPayloadCount = invalidatedPayloadRepository.deleteByExpiresAtLessThan(now);
3636
if (deletedInvalidatedPayloadCount > 0) {
3737
log.info("Deleted {} expired InvalidatedPayloads", deletedInvalidatedPayloadCount);
38-
captchaService.decrementInvalidatedPayloadCount(deletedInvalidatedPayloadCount);
3938
}
4039
}
4140
}

0 commit comments

Comments
 (0)