1 package com.atlassian.asap.core.keys.publickey;
2
3 import com.atlassian.asap.core.keys.KeyProvider;
4 import com.atlassian.asap.core.keys.PemReader;
5 import org.apache.commons.lang3.StringUtils;
6 import org.apache.http.client.HttpClient;
7 import org.slf4j.Logger;
8 import org.slf4j.LoggerFactory;
9
10 import java.io.File;
11 import java.net.URI;
12 import java.security.PublicKey;
13 import java.util.List;
14 import java.util.Objects;
15 import java.util.regex.Pattern;
16 import java.util.stream.Collectors;
17
18 import static com.google.common.base.Preconditions.checkArgument;
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 public class PublicKeyProviderFactory {
37 private static final Pattern CHAIN_SPLITTER_REGEX = Pattern.compile("\\s+,\\s+");
38 private static final Pattern MIRRORS_SPLITTER_REGEX = Pattern.compile("\\s+\\|\\s+");
39
40 private static final Logger logger = LoggerFactory.getLogger(PublicKeyProviderFactory.class);
41
42 private final PemReader pemReader;
43 private final HttpClient httpClient;
44
45 public PublicKeyProviderFactory(HttpClient httpClient, PemReader pemReader) {
46 this.pemReader = Objects.requireNonNull(pemReader);
47 this.httpClient = Objects.requireNonNull(httpClient);
48 }
49
50
51
52
53 public static PublicKeyProviderFactory createDefault() {
54 return new PublicKeyProviderFactory(HttpPublicKeyProvider.defaultHttpClient(), new PemReader());
55 }
56
57
58
59
60
61
62
63
64 public KeyProvider<PublicKey> createPublicKeyProvider(String publicKeyBaseUrl) {
65 Objects.requireNonNull(publicKeyBaseUrl);
66 logger.info("Using {} as public key base url", publicKeyBaseUrl);
67 return parseChainedPublicKeyProviders(publicKeyBaseUrl);
68 }
69
70 private KeyProvider<PublicKey> parseChainedPublicKeyProviders(String publicKeyBaseUrl) {
71 List<KeyProvider<PublicKey>> keyProviderChain = CHAIN_SPLITTER_REGEX.splitAsStream(publicKeyBaseUrl)
72 .map(String::trim)
73 .filter(StringUtils::isNotBlank)
74 .map(this::parseMirroredPublicKeyProviders)
75 .collect(Collectors.toList());
76
77 if (keyProviderChain.isEmpty()) {
78 logger.warn("No key providers available, all requests will be rejected.");
79 }
80
81 return ChainedKeyProvider.createChainedKeyProvider(keyProviderChain);
82 }
83
84 private KeyProvider<PublicKey> parseMirroredPublicKeyProviders(String baseUrlComponent) {
85 List<KeyProvider<PublicKey>> mirroredProviders = MIRRORS_SPLITTER_REGEX.splitAsStream(baseUrlComponent)
86 .map(String::trim)
87 .filter(StringUtils::isNotBlank)
88 .map(this::getPublicKeyKeyProvider)
89 .collect(Collectors.toList());
90
91 return MirroredKeyProvider.createMirroredKeyProvider(mirroredProviders);
92 }
93
94 private KeyProvider<PublicKey> getPublicKeyKeyProvider(String baseUrl) {
95 URI parsedBaseUrl = URI.create(baseUrl);
96 checkArgument(parsedBaseUrl.isAbsolute(), "URL must be absolute");
97 checkArgument(!baseUrl.contains(","), "Comma must be encoded if present in URI");
98 checkArgument(!baseUrl.contains("|"), "Pipe must be encoded if present in URI");
99 switch (parsedBaseUrl.getScheme()) {
100 case "https":
101 return new HttpPublicKeyProvider(parsedBaseUrl, httpClient, pemReader);
102 case "file":
103 return new FilePublicKeyProvider(new File(parsedBaseUrl), pemReader);
104 case "classpath":
105 return new ClasspathPublicKeyProvider(parsedBaseUrl.getPath(), pemReader);
106 default:
107 throw new IllegalArgumentException("Unsupported public key server base URL protocol, " +
108 "only https:, file: and classpath: are supported: " + parsedBaseUrl);
109 }
110 }
111
112 }