View Javadoc

1   package com.atlassian.asap.core.server;
2   
3   import com.atlassian.asap.api.server.http.RequestAuthenticator;
4   import com.atlassian.asap.core.keys.KeyProvider;
5   import com.atlassian.asap.core.keys.PemReader;
6   import com.atlassian.asap.core.keys.publickey.HttpPublicKeyProvider;
7   import com.atlassian.asap.core.keys.publickey.PublicKeyProviderFactory;
8   import com.atlassian.asap.core.parser.JwtParser;
9   import com.atlassian.asap.core.server.http.RequestAuthenticatorImpl;
10  import com.atlassian.asap.core.validator.JwtClaimsValidator;
11  import com.atlassian.asap.core.validator.JwtValidator;
12  import com.atlassian.asap.core.validator.JwtValidatorImpl;
13  import com.atlassian.asap.nimbus.parser.NimbusJwtParser;
14  import com.google.common.annotations.VisibleForTesting;
15  import org.apache.commons.lang3.StringUtils;
16  import org.apache.http.client.HttpClient;
17  import org.springframework.beans.factory.annotation.Autowired;
18  import org.springframework.beans.factory.annotation.Qualifier;
19  import org.springframework.beans.factory.annotation.Value;
20  import org.springframework.context.annotation.Bean;
21  import org.springframework.context.annotation.Configuration;
22  import org.springframework.context.annotation.Lazy;
23  
24  import java.security.PublicKey;
25  import java.time.Clock;
26  import java.util.Collections;
27  import java.util.Set;
28  import java.util.regex.Pattern;
29  import java.util.stream.Collectors;
30  
31  /**
32   * Server-side ASAP configuration.
33   */
34  @Configuration
35  public class AsapServerConfiguration {
36      private static final Pattern CSV_PATTERN = Pattern.compile(",");
37  
38      private final String audience;
39  
40      private final String audienceOverride;
41  
42      private final String publicKeyRepositoryAdditionalUrl;
43  
44  
45      /**
46       * @param audience                         the single audience this server would be responding to
47       * @param audienceOverride                 if non-blank, this CSV set of audiences will take precedence over the single audience
48       * @param publicKeyRepositoryAdditionalUrl if non-blank, this provides an additional public key repository
49       */
50      @Autowired
51      AsapServerConfiguration(@Value("${asap.audience}") String audience,
52                              @Value("${asap.audience_override:}") String audienceOverride,
53                              @Value("${asap.public_key_repository.additional_url:}") String publicKeyRepositoryAdditionalUrl) {
54          this.audience = audience;
55          this.audienceOverride = audienceOverride;
56          this.publicKeyRepositoryAdditionalUrl = publicKeyRepositoryAdditionalUrl;
57      }
58  
59      /**
60       * Definition of the {@link JwtValidator} bean.
61       *
62       * @param publicKeyProvider  the public key provider to look up public keys
63       * @param jwtParser          the parser to use for JWT token parsing
64       * @param jwtClaimsValidator the validator for the claims in the JWT token
65       * @return a token validator for the given audience and public key repository
66       */
67      @Bean
68      public JwtValidator jwtValidator(KeyProvider<PublicKey> publicKeyProvider,
69                                       JwtParser jwtParser,
70                                       JwtClaimsValidator jwtClaimsValidator) {
71          return new JwtValidatorImpl(publicKeyProvider, jwtParser, jwtClaimsValidator, getAllAudiences());
72      }
73  
74      Set<String> getAllAudiences() {
75          if (StringUtils.isBlank(audienceOverride)) {
76              return Collections.singleton(audience);
77          } else {
78              return CSV_PATTERN.splitAsStream(audienceOverride)
79                      .map(String::trim)
80                      .filter(StringUtils::isNotBlank)
81                      .collect(Collectors.toSet());
82          }
83      }
84  
85      /**
86       * Definition of the {@link JwtClaimsValidator} bean.
87       *
88       * @return a validator of JWT claims
89       */
90      @Bean
91      public JwtClaimsValidator jwtClaimsValidator() {
92          return new JwtClaimsValidator(Clock.systemUTC());
93      }
94  
95      /**
96       * Definition of the {@link JwtParser} bean.
97       *
98       * @return a parser of JWT tokens
99       */
100     @Bean
101     public JwtParser jwtParser() {
102         return new NimbusJwtParser();
103     }
104 
105     /**
106      * Definition of the {@link KeyProvider} bean.
107      *
108      * @param publicKeyRepoBaseUrl base URL of the public key repository. It may be a composite URL.
109      * @return a public key provider
110      */
111     @Bean
112     public KeyProvider<PublicKey> publicKeyProvider(@Value("${asap.public_key_repository.url}") String publicKeyRepoBaseUrl) {
113         return new PublicKeyProviderFactory(asapHttpClient(), new PemReader())
114                 .createPublicKeyProvider(getCombinedPublicKeyRepositoryBaseUrl(publicKeyRepoBaseUrl));
115     }
116 
117     @VisibleForTesting
118     String getCombinedPublicKeyRepositoryBaseUrl(String publicKeyRepoBaseUrl) {
119         return StringUtils.isBlank(publicKeyRepositoryAdditionalUrl) ?
120                 publicKeyRepoBaseUrl : publicKeyRepoBaseUrl + " , " + publicKeyRepositoryAdditionalUrl;
121     }
122 
123     /**
124      * Definition of the {@link HttpClient} bean.
125      *
126      * @return a HTTP client
127      */
128     @Bean
129     @Lazy
130     @Qualifier("asap")
131     public HttpClient asapHttpClient() {
132         return HttpPublicKeyProvider.defaultHttpClient();
133     }
134 
135     /**
136      * Definition of the {@link RequestAuthenticator} bean.
137      *
138      * @param jwtValidator the validator of tokens
139      * @return a request authenticator that authenticates valid tokens
140      */
141     @Bean
142     public RequestAuthenticator requestAuthenticator(JwtValidator jwtValidator) {
143         return new RequestAuthenticatorImpl(jwtValidator);
144     }
145 
146     /**
147      * @return the audience that the server accepts requests for
148      */
149     public String getAudience() {
150         return audience;
151     }
152 }