Skip to content

Commit 160369e

Browse files
authored
Merge pull request #86 from it-at-m/add-custom-prometheus-metrics
Add custom prometheus metrics
2 parents d5b8850 + 2d8bd1e commit 160369e

File tree

10 files changed

+593
-29
lines changed

10 files changed

+593
-29
lines changed

.github/README.md

Lines changed: 309 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,29 +25,326 @@
2525
[made-with-love-shield]: https://img.shields.io/badge/made%20with%20%E2%9D%A4%20by-it%40M-yellow?style=for-the-badge
2626
[license-shield]: https://img.shields.io/github/license/it-at-m/refarch-templates?style=for-the-badge
2727

28-
# RefArch Templates
28+
# CaptchaService
2929

3030
[![Documentation][documentation-shield]][documentation]
3131
[![New issue][new-issue-shield]][new-issue]
3232
[![Made with love by it@M][made-with-love-shield]][itm-opensource]
3333
[![GitHub license][license-shield]][license]
3434

35-
This project acts as a template and provides starter files for web application projects based on the RefArch (reference architecture) of it@M.
35+
A Spring Boot microservice that provides proof-of-work CAPTCHA challenges using the [ALTCHA library](https://altcha.org/). This service offers an alternative to traditional image-based CAPTCHAs with adaptive difficulty management and multi-tenant support.
3636

37-
To learn more about the architecture itself, checkout its [documentation][refarch-documentation] or [code][refarch-code].
37+
## Architecture
3838

39-
The templates are based on [Spring][spring-documentation] and [Vue.js][vuejs-documentation].
39+
```mermaid
40+
graph TB
41+
%% External Client
42+
Client
4043
41-
## Usage
44+
%% External Services
45+
subgraph "External Services"
46+
PostgreSQL[(PostgreSQL<br/>Database)]
47+
end
4248
43-
To get set up and learn more about the templates, please check out the [Getting Started][getting-started-documentation] page.
44-
Also check the respective pages with suggestions on how to [develop][develop-documentation], [document][document-documentation] and [organize][organize-documentation] your project.
49+
%% Main CaptchaService Application
50+
subgraph "CaptchaService Application"
4551
46-
## Roadmap
52+
%% Monitoring & Management
53+
subgraph "Monitoring"
54+
Actuator["**Spring Actuator**<br/>/actuator/info<br/>/actuator/health<br/>/actuator/metrics"]
55+
end
4756
48-
See the [open issues][open-issues] for a full list of proposed features (and known issues).
49-
To get a better overview on what's currently being worked on, check out our [project board][project-board].
50-
We often also plan our issues in [milestones][milestones].
57+
%% API Endpoints Detail
58+
subgraph "CaptchaController"
59+
ChallengeEndpoint["**postChallenge()**<br/>Create CAPTCHA Challenge"]
60+
VerifyEndpoint["**postVerify()**<br/>Verify CAPTCHA Solution"]
61+
end
62+
63+
%% Service Layer
64+
subgraph "Service Layer"
65+
CaptchaService["**CaptchaService**<br/>Core CAPTCHA Logic"]
66+
DifficultyService["**DifficultyService**<br/>Adaptive Difficulty Management"]
67+
SiteAuthService["**SiteAuthService**<br/>Site Key/Secret Validation"]
68+
SourceAddressService["**SourceAddressService**<br/>IP Address Validation"]
69+
ExpiredDataService["**ExpiredDataService**<br/>Cleanup Scheduler"]
70+
end
71+
72+
%% Data Layer
73+
subgraph "Data Layer"
74+
CaptchaRequestRepo["**CaptchaRequestRepository**<br/>JPA Repository"]
75+
InvalidatedPayloadRepo["**InvalidatedPayloadRepository**<br/>JPA Repository"]
76+
77+
subgraph "JPA Entities"
78+
CaptchaRequestEntity["**CaptchaRequest**<br/>Entity"]
79+
InvalidatedPayloadEntity["**InvalidatedPayload**<br/>Entity"]
80+
end
81+
end
82+
83+
%% Properties/Configuration
84+
subgraph "Configuration Properties"
85+
CaptchaProperties["**CaptchaProperties**<br/>HMAC Key, Sites Config"]
86+
CaptchaSite["**CaptchaSite**<br/>Site-specific Settings"]
87+
DifficultyItem["**DifficultyItem**<br/>Difficulty Mappings"]
88+
end
89+
end
90+
91+
%% Request Flow
92+
Client -->|POST /api/v1/challenge| ChallengeEndpoint
93+
Client -->|POST /api/v1/verify| VerifyEndpoint
94+
Client --> Monitoring
95+
96+
%% Controller to Services
97+
ChallengeEndpoint --> CaptchaService
98+
VerifyEndpoint --> CaptchaService
99+
ChallengeEndpoint --> SiteAuthService
100+
VerifyEndpoint --> SiteAuthService
101+
ChallengeEndpoint --> SourceAddressService
102+
103+
%% Service Interactions
104+
CaptchaService --> DifficultyService
105+
CaptchaService --> CaptchaRequestRepo
106+
CaptchaService --> InvalidatedPayloadRepo
107+
CaptchaService --> AltchaLib
108+
109+
DifficultyService --> CaptchaRequestRepo
110+
ExpiredDataService -->|Scheduled Cleanup| CaptchaRequestRepo
111+
ExpiredDataService -->|Scheduled Cleanup| InvalidatedPayloadRepo
112+
113+
%% Data Layer
114+
CaptchaRequestRepo --> CaptchaRequestEntity
115+
InvalidatedPayloadRepo --> InvalidatedPayloadEntity
116+
CaptchaRequestEntity -.->|JPA/Hibernate| PostgreSQL
117+
InvalidatedPayloadEntity -.->|JPA/Hibernate| PostgreSQL
118+
119+
%% Configuration Dependencies
120+
CaptchaService -.->|Uses| CaptchaProperties
121+
SiteAuthService -.->|Uses| CaptchaProperties
122+
SourceAddressService -.->|Uses| CaptchaProperties
123+
DifficultyService -.->|Uses| CaptchaProperties
124+
125+
%% Database Migration
126+
Flyway -.->|Schema Management| PostgreSQL
127+
128+
class CaptchaService,DifficultyService,SiteAuthService,SourceAddressService,ExpiredDataService service
129+
class CaptchaRequestRepo,InvalidatedPayloadRepo,CaptchaRequestEntity,InvalidatedPayloadEntity data
130+
class PostgreSQL,Client external
131+
class ChallengeEndpoint,VerifyEndpoint endpoint
132+
class CaptchaProperties,CaptchaSite,DifficultyItem properties
133+
```
134+
135+
## Features
136+
137+
- **Proof-of-Work CAPTCHA**: Uses ALTCHA library for crypto-based challenge verification
138+
- **Adaptive Difficulty**: Automatically adjusts challenge difficulty based on request patterns
139+
- **Multi-Tenant Support**: Site-specific configuration with individual keys and secrets
140+
- **Source Address Validation**: IP-based filtering and network address validation
141+
- **Scheduled Cleanup**: Automatic removal of expired challenges and invalidated payloads
142+
- **Monitoring**: Comprehensive health checks and metrics via Spring Actuator
143+
- **Database Persistence**: PostgreSQL storage with automated Flyway migrations
144+
145+
## Prerequisites
146+
147+
- Java 21 or later
148+
- Maven 3.8+
149+
- PostgreSQL 16+
150+
- Docker and Docker Compose (for local development)
151+
152+
## Quick Start
153+
154+
1. **Clone the repository**
155+
156+
```bash
157+
git clone https://github.com/it-at-m/captchaservice.git
158+
cd captchaservice
159+
```
160+
161+
2. **Start the development stack**
162+
163+
```bash
164+
cd stack
165+
docker compose up -d
166+
```
167+
168+
3. **Build and run the application**
169+
170+
```bash
171+
cd captchaservice-backend
172+
bash runLocal.sh
173+
```
174+
175+
4. **Verify the service is running**
176+
177+
```bash
178+
curl http://localhost:39146/actuator/health
179+
```
180+
181+
## Configuration
182+
183+
### Environment Variables
184+
185+
| Variable | Description | Default |
186+
| --------------------------------------- | ------------------------------ | ------------------------------------------------- |
187+
| `SPRING_DATASOURCE_URL` | PostgreSQL connection URL | `jdbc:postgresql://localhost:5432/captchaservice` |
188+
| `SPRING_DATASOURCE_USERNAME` | Database username | - |
189+
| `SPRING_DATASOURCE_PASSWORD` | Database password | - |
190+
| `CAPTCHA_HMAC_KEY` | HMAC key for challenge signing | - |
191+
| `CAPTCHA_CAPTCHA_TIMEOUT_SECONDS` | Challenge validity period | `300` |
192+
| `CAPTCHA_SOURCE_ADDRESS_WINDOW_SECONDS` | Source address tracking window | `3600` |
193+
194+
### Site Configuration
195+
196+
Configure multiple sites in your `application.yml`:
197+
198+
```yaml
199+
captcha:
200+
hmac-key: secret # HMAC key for signing challenges
201+
captcha-timeout-seconds: 300 # How long a CAPTCHA challenge is valid
202+
source-address-window-seconds: 3600 # How long a source address is stored
203+
sites:
204+
site1: # Site key for site1
205+
site-secret: "secret1" # Site secret for site1
206+
max-verifies-per-payload: 1 # How many times a payload can be verified
207+
whitelisted_source-addresses:
208+
- "192.0.2.0/24" # Whitelisted IP address range
209+
site2:
210+
site-secret: "secret2"
211+
whitelisted_source-addresses:
212+
- "192.0.2.0/24" # Whitelisted IP address range
213+
difficulty-map:
214+
- min-visits: 1 # From the first visit on...
215+
max-number: 1000 # ...the difficulty is 1000
216+
- min-visits: 10 # From the 10th visit on...
217+
max-number: 10000 # ...the difficulty is 10000
218+
```
219+
220+
## API Documentation
221+
222+
### Create Challenge
223+
224+
**POST** `/api/v1/captcha/challenge`
225+
226+
Creates a new CAPTCHA challenge for the specified site.
227+
228+
**Request Body:**
229+
230+
```json
231+
{
232+
"siteKey": "site1",
233+
"siteSecret": "secret1",
234+
"clientAddress": "192.168.1.100"
235+
}
236+
```
237+
238+
**Response:**
239+
240+
```json
241+
{
242+
"algorithm": "SHA-256",
243+
"challenge": "abc123...",
244+
"maxNumber": 1000,
245+
"salt": "def456...",
246+
"signature": "ghi789..."
247+
}
248+
```
249+
250+
### Verify Solution
251+
252+
**POST** `/api/v1/captcha/verify`
253+
254+
Verifies a CAPTCHA solution payload.
255+
256+
**Request Body:**
257+
258+
```json
259+
{
260+
"siteKey": "site1",
261+
"siteSecret": "secret1",
262+
"clientAddress": "192.168.1.100",
263+
"payload": {
264+
"algorithm": "SHA-256",
265+
"challenge": "abc123...",
266+
"number": 542,
267+
"salt": "def456...",
268+
"signature": "ghi789...",
269+
"took": 4400
270+
}
271+
}
272+
```
273+
274+
**Response:**
275+
276+
```jsonc
277+
{
278+
"valid": true
279+
}
280+
281+
// or if the solution is invalid
282+
283+
{
284+
"valid": false
285+
}
286+
```
287+
288+
### Error Responses
289+
290+
**401 Unauthorized** - Invalid site credentials
291+
292+
```json
293+
{
294+
"status": 401,
295+
"error": "Authentication Error"
296+
}
297+
```
298+
299+
**400 Bad Request** - Invalid request format
300+
301+
```json
302+
{
303+
"status": 400,
304+
"error": "Bad Request"
305+
}
306+
```
307+
308+
## Database
309+
310+
### Schema Management
311+
312+
Database schema is managed using Flyway migrations located in `src/main/resources/db/migration/`
313+
314+
### Manual Migration
315+
316+
```bash
317+
# Run migrations manually
318+
mvn flyway:migrate
319+
320+
# Check migration status
321+
mvn flyway:info
322+
323+
# Validate migrations
324+
mvn flyway:validate
325+
```
326+
327+
## Monitoring
328+
329+
### Health Checks
330+
331+
- **Liveness**: `GET /actuator/health/liveness`
332+
- **Readiness**: `GET /actuator/health/readiness`
333+
- **Overall Health**: `GET /actuator/health`
334+
335+
### Application Information
336+
337+
- **Info Endpoint**: `GET /actuator/info`
338+
- **Metrics**: `GET /actuator/metrics`
339+
340+
### Metrics
341+
342+
Prometheus metrics available at `/actuator/prometheus`:
343+
344+
- Application metrics
345+
- JVM metrics
346+
- Database connection pool metrics
347+
- Custom CAPTCHA metrics
51348

52349
## Contributing
53350

@@ -61,4 +358,4 @@ Distributed under the MIT License. See [LICENSE][license] file for more informat
61358

62359
## Contact
63360

64-
361+
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66
import org.springframework.scheduling.annotation.EnableScheduling;
77

88
/**
9-
* Application class for starting the microservice.
9+
* Application class for starting the Captcha Service application.
1010
*/
1111
@SpringBootApplication
1212
@ConfigurationPropertiesScan
1313
@EnableScheduling
1414
@SuppressWarnings("PMD.UseUtilityClass")
15-
public class MicroServiceApplication {
15+
public class CaptchaServiceApplication {
1616
public static void main(final String[] args) {
17-
SpringApplication.run(MicroServiceApplication.class, args);
17+
SpringApplication.run(CaptchaServiceApplication.class, args);
1818
}
1919
}

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: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
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;
67
import lombok.Data;
78
import lombok.NoArgsConstructor;
8-
import org.altcha.altcha.Altcha;
9+
10+
import de.muenchen.captchaservice.data.ExtendedPayload;
911

1012
@Data
1113
@NoArgsConstructor
@@ -21,6 +23,11 @@ public class PostVerifyRequest {
2123
private String siteSecret;
2224

2325
@NotNull
24-
private Altcha.Payload payload;
26+
@NotBlank
27+
@ValidSourceAddress
28+
private String clientAddress;
29+
30+
@NotNull
31+
private ExtendedPayload payload;
2532

2633
}

0 commit comments

Comments
 (0)