1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package com.linecorp.centraldogma.server.internal.thrift;
17
18 import static java.util.Objects.requireNonNull;
19
20 import java.net.InetSocketAddress;
21 import java.time.Clock;
22 import java.time.Instant;
23 import java.time.temporal.ChronoUnit;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
26 import java.util.regex.Pattern;
27
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31 import com.google.common.annotations.VisibleForTesting;
32
33 import com.linecorp.armeria.common.HttpHeaderNames;
34 import com.linecorp.armeria.common.HttpRequest;
35 import com.linecorp.armeria.common.HttpResponse;
36 import com.linecorp.armeria.server.HttpService;
37 import com.linecorp.armeria.server.ServiceRequestContext;
38 import com.linecorp.armeria.server.SimpleDecoratingHttpService;
39
40 public class TokenlessClientLogger extends SimpleDecoratingHttpService {
41
42 private static final Logger logger = LoggerFactory.getLogger(TokenlessClientLogger.class);
43
44 private static final Pattern PATTERN = Pattern.compile("\\s*[Bb][Ee][Aa][Rr][Ee][Rr]\\s+anonymous\\s*");
45
46 private final Clock clock;
47 private final ConcurrentMap<String, Instant> reportedAddresses = new ConcurrentHashMap<>();
48
49 public TokenlessClientLogger(HttpService delegate) {
50 this(delegate, Clock.systemUTC());
51 }
52
53 @VisibleForTesting
54 TokenlessClientLogger(HttpService delegate, Clock clock) {
55 super(delegate);
56 this.clock = requireNonNull(clock, "clock");
57 }
58
59 @Override
60 public HttpResponse serve(ServiceRequestContext ctx, HttpRequest req) throws Exception {
61 final String authorization = req.headers().get(HttpHeaderNames.AUTHORIZATION);
62 if (authorization == null || !PATTERN.matcher(authorization).matches()) {
63 final InetSocketAddress raddr = ctx.remoteAddress();
64 final String ip = raddr.getAddress().getHostAddress();
65 final Instant now = Instant.now(clock);
66 final Instant lastReport = reportedAddresses.putIfAbsent(ip, now);
67 final boolean report;
68 if (lastReport == null) {
69 report = true;
70 } else if (ChronoUnit.DAYS.between(lastReport, now) >= 1) {
71 report = reportedAddresses.replace(ip, lastReport, now);
72 } else {
73 report = false;
74 }
75
76 if (report) {
77 report(raddr.getHostString(), ip);
78 }
79 }
80
81 return unwrap().serve(ctx, req);
82 }
83
84 @VisibleForTesting
85 void report(String hostname, String ip) {
86 logger.debug("Received a request without 'authorization' header from: {}/{}", hostname, ip);
87 }
88 }