1 package com.atlassian.asap.service.core.impl;
2
3
4 import com.atlassian.asap.api.Jwt;
5 import com.atlassian.asap.api.exception.CannotRetrieveKeyException;
6 import com.atlassian.asap.api.exception.InvalidTokenException;
7 import com.atlassian.asap.core.keys.KeyProvider;
8 import com.atlassian.asap.core.parser.JwtParser;
9 import com.atlassian.asap.core.validator.JwtClaimsValidator;
10 import com.atlassian.asap.core.validator.JwtValidator;
11 import com.atlassian.asap.core.validator.JwtValidatorImpl;
12 import com.atlassian.asap.nimbus.parser.NimbusJwtParser;
13 import com.atlassian.asap.service.api.ValidationResult;
14 import com.atlassian.asap.service.core.spi.AsapConfiguration;
15 import com.google.common.annotations.VisibleForTesting;
16
17 import java.security.PublicKey;
18 import java.util.Optional;
19 import java.util.Set;
20
21 import static com.atlassian.asap.core.JwtConstants.HTTP_AUTHORIZATION_HEADER_VALUE_PREFIX;
22 import static java.util.Objects.requireNonNull;
23
24 public class TokenValidatorImpl extends AbstractTokenValidator {
25 private final KeyProvider<PublicKey> publicKeyProvider;
26 private final JwtClaimsValidator jwtClaimsValidator;
27 private final JwtParser jwtParser;
28
29 public TokenValidatorImpl(AsapConfiguration config, KeyProvider<PublicKey> publicKeyProvider,
30 JwtClaimsValidator jwtClaimsValidator) {
31 this(config, publicKeyProvider, jwtClaimsValidator, new NimbusJwtParser());
32 }
33
34 @VisibleForTesting
35 TokenValidatorImpl(AsapConfiguration config, KeyProvider<PublicKey> publicKeyProvider,
36 JwtClaimsValidator jwtClaimsValidator, JwtParser jwtParser) {
37 super(config);
38 this.publicKeyProvider = requireNonNull(publicKeyProvider, "publicKeyProvider");
39 this.jwtClaimsValidator = requireNonNull(jwtClaimsValidator, "jwtClaimsValidator");
40 this.jwtParser = requireNonNull(jwtParser, "jwtParser");
41 }
42
43 @Override
44 public ValidationResult validate(Optional<String> authHeader) {
45 requireNonNull(authHeader, "authHeader");
46 switch (policy()) {
47 case REJECT:
48 return rejectAsap(authHeader);
49 case IGNORE:
50 return ValidationResultImpl.abstain();
51 case OPTIONAL:
52 return optionalAsap(authHeader);
53 case REQUIRE:
54 return requireAsap(authHeader);
55 default:
56 throw new IllegalStateException("Unknown authorization policy: " + policy());
57 }
58 }
59
60 private ValidationResult rejectAsap(Optional<String> authHeader) {
61 return extractSerializedJwt(authHeader)
62 .flatMap(jwtParser::determineUnverifiedIssuer)
63 .map(ValidationResultImpl::rejected)
64 .orElseGet(ValidationResultImpl::abstain);
65 }
66
67 private ValidationResult optionalAsap(Optional<String> authHeader) {
68 return extractSerializedJwt(authHeader)
69 .flatMap(this::parseAndVerifyToken)
70 .orElseGet(ValidationResultImpl::abstain);
71 }
72
73 private ValidationResult requireAsap(Optional<String> authHeader) {
74 return extractSerializedJwt(authHeader)
75 .flatMap(this::parseAndVerifyToken)
76 .orElseGet(ValidationResultImpl::notAuthenticated);
77 }
78
79 private Optional<ValidationResult> parseAndVerifyToken(String serializedJwt) {
80 try {
81 final JwtValidator jwtValidator = createJwtValidator(publicKeyProvider, jwtParser, jwtClaimsValidator,
82 acceptableAudienceValues());
83 final Jwt jwt = jwtValidator.readAndValidate(serializedJwt);
84 return Optional.of(verifyAuthorization(jwt));
85 } catch (InvalidTokenException e) {
86
87
88
89
90 return jwtParser.determineUnverifiedIssuer(serializedJwt)
91 .map(issuer -> ValidationResultImpl.notAuthenticated());
92 } catch (CannotRetrieveKeyException e) {
93 final String issuer = jwtParser.determineUnverifiedIssuer(serializedJwt).orElse(null);
94 return Optional.of(ValidationResultImpl.notVerified(issuer));
95 }
96 }
97
98
99
100
101
102
103
104
105
106
107 protected JwtValidator createJwtValidator(KeyProvider<PublicKey> publicKeyProvider, JwtParser jwtParser,
108 JwtClaimsValidator jwtClaimsValidator, Set<String> allowedAudiences) {
109 return new JwtValidatorImpl(publicKeyProvider, jwtParser, jwtClaimsValidator, allowedAudiences);
110 }
111
112 private ValidationResult verifyAuthorization(Jwt jwt) {
113 if ((isImpersonationIssuerAuthorized(jwt) || isIssuerAuthorized(jwt)) && isSubjectAuthorized(jwt)) {
114 return ValidationResultImpl.authorized(jwt);
115 }
116
117 return ValidationResultImpl.notAuthorized(jwt.getClaims().getIssuer());
118 }
119
120 private boolean subjectImpersonation(Jwt jwt) {
121 return subjectImpersonation() || isImpersonationIssuerAuthorized(jwt);
122 }
123
124 private boolean isImpersonationIssuerAuthorized(Jwt jwt) {
125 return impersonationAuthorizedIssuers().contains(jwt.getClaims().getIssuer());
126 }
127
128
129 private boolean isIssuerAuthorized(Jwt jwt) {
130 if (authorizedIssuers().isEmpty()) {
131 if (subjectImpersonation(jwt)) {
132 throw new IllegalStateException("Subject impersonation requires an explicit issuer whitelist");
133 }
134 return true;
135 }
136 return authorizedIssuers().contains(jwt.getClaims().getIssuer());
137 }
138
139 private boolean isSubjectAuthorized(Jwt jwt) {
140 final Set<String> subjects = authorizedSubjects();
141 if (subjects.isEmpty()) {
142 return true;
143 }
144
145
146 final String defaultSubject = subjectImpersonation(jwt) ? null : jwt.getClaims().getIssuer();
147 final String effectiveSubject = jwt.getClaims().getSubject().orElse(defaultSubject);
148 return subjects.contains(effectiveSubject);
149 }
150
151 private static Optional<String> extractSerializedJwt(Optional<String> authHeader) {
152 return authHeader
153 .filter(hdr -> hdr.startsWith(HTTP_AUTHORIZATION_HEADER_VALUE_PREFIX))
154 .map(hdr -> hdr.substring(HTTP_AUTHORIZATION_HEADER_VALUE_PREFIX.length()));
155 }
156 }