1 package com.atlassian.asap.core.server.jersey;
2
3 import com.atlassian.asap.api.Jwt;
4 import com.atlassian.asap.api.exception.AuthorizationFailedException;
5 import org.apache.commons.lang3.StringUtils;
6
7 import java.util.Arrays;
8 import java.util.Collections;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.function.Function;
12 import java.util.stream.Collectors;
13 import java.util.stream.Stream;
14
15 import static com.atlassian.asap.core.server.jersey.Memoizer.memoize;
16 import static java.lang.String.format;
17 import static java.util.Objects.requireNonNull;
18
19
20
21
22 @SuppressWarnings("WeakerAccess")
23 public class AsapValidator {
24
25 private final Function<Asap, Whitelist> whitelistProvider;
26
27 public AsapValidator(Function<Asap, Whitelist> whitelistProvider) {
28 this.whitelistProvider = requireNonNull(whitelistProvider);
29 }
30
31
32 public static AsapValidator newAnnotationValidator() {
33 return new AsapValidator(memoize(new AsapWhitelistProvider()));
34 }
35
36 public static AsapValidator newEnvironmentVariablesValidator() {
37 return new AsapValidator(memoize(new EnvironmentVariablesWhitelistProvider()));
38 }
39
40
41
42
43
44
45
46
47
48 public static AsapValidator newAnnotationWithConfigValidator(final Set<String> authorizedSubjects, final Set<String> authorizedIssuers) {
49 return new AsapValidator(new AsapAnnotationWhitelistProviderWithConfigSupport(authorizedSubjects, authorizedIssuers));
50 }
51
52
53 private static String joinSet(final Set<String> validIssuers) {
54 return String.join(",", validIssuers);
55 }
56
57
58
59
60
61
62
63
64 public void validate(Asap asap, Jwt jwt) throws AuthorizationFailedException {
65 Whitelist whitelist = whitelistProvider.apply(asap);
66 validateSubject(whitelist, jwt);
67 validateIssuer(whitelist, jwt);
68 }
69
70 private void validateIssuer(final Whitelist whitelist, final Jwt jwt)
71 throws AuthorizationFailedException {
72 String issuer = jwt.getClaims().getIssuer();
73 Set<String> validIssuers = !whitelist.authorizedIssuers.isEmpty() ? whitelist.authorizedIssuers :
74 whitelist.authorizedSubjects;
75 if (!validIssuers.isEmpty() && !validIssuers.contains(issuer)) {
76 throw new AuthorizationFailedException(format("Unacceptable issuer ('%s' not in '%s')", issuer,
77 joinSet(validIssuers)));
78 }
79 }
80
81 private void validateSubject(final Whitelist whitelist, final Jwt jwt) throws AuthorizationFailedException {
82 String subject = jwt.getClaims().getSubject().orElse(jwt.getClaims().getIssuer());
83 if (!whitelist.authorizedSubjects.isEmpty() &&
84 !whitelist.authorizedSubjects.contains(subject)) {
85 throw new AuthorizationFailedException(format("Unacceptable subject ('%s' not in '%s')", subject,
86 joinSet(whitelist.authorizedSubjects)));
87 }
88 }
89
90
91
92
93 @SuppressWarnings("checkstyle:VisiblityModifier")
94 public static class Whitelist {
95 private final Set<String> authorizedSubjects;
96 private final Set<String> authorizedIssuers;
97
98 public Whitelist(final Set<String> authorizedSubjects, final Set<String> authorizedIssuers) {
99 this.authorizedSubjects = Collections.unmodifiableSet(requireNonNull(authorizedSubjects));
100 this.authorizedIssuers = Collections.unmodifiableSet(requireNonNull(authorizedIssuers));
101 }
102
103 Set<String> getAuthorizedSubjects() {
104 return authorizedSubjects;
105 }
106
107 Set<String> getAuthorizedIssuers() {
108 return authorizedIssuers;
109 }
110 }
111
112 public static class AsapWhitelistProvider implements Function<Asap, Whitelist> {
113 @Override
114 public Whitelist apply(final Asap asap) {
115 return new Whitelist(
116 Arrays.stream(asap.authorizedSubjects()).collect(Collectors.toSet()),
117 Arrays.stream(asap.authorizedIssuers()).collect(Collectors.toSet())
118 );
119 }
120 }
121
122
123 public static class AsapAnnotationWhitelistProviderWithConfigSupport implements Function<Asap, Whitelist> {
124
125 private final Whitelist whiteList;
126
127 public AsapAnnotationWhitelistProviderWithConfigSupport(final Set<String> authorizedSubjects, final Set<String> authorizedIssuers) {
128 this.whiteList = new Whitelist(authorizedSubjects, authorizedIssuers);
129
130 }
131
132 @Override
133 public Whitelist apply(final Asap asap) {
134
135 if (asap.authorizedIssuers().length == 0 && asap.authorizedSubjects().length == 0) {
136 return whiteList;
137 }
138 return new Whitelist(
139 Arrays.stream(asap.authorizedSubjects()).collect(Collectors.toSet()),
140 Arrays.stream(asap.authorizedIssuers()).collect(Collectors.toSet())
141 );
142 }
143 }
144
145
146
147
148
149
150 public static class EnvironmentVariablesWhitelistProvider implements Function<Asap, Whitelist> {
151 public static final String AUTHORIZED_SUBJECTS_KEY = "ASAP_AUTHORIZED_SUBJECTS";
152 public static final String AUTHORIZED_ISSUERS_KEY = "ASAP_AUTHORIZED_ISSUERS";
153
154 private final String authorizedSubjectsVariableName;
155 private final String authorizedIssuersVariableName;
156 private final Map<String, String> variables;
157
158 public EnvironmentVariablesWhitelistProvider() {
159 this(AUTHORIZED_SUBJECTS_KEY, AUTHORIZED_ISSUERS_KEY, System.getenv());
160 }
161
162 public EnvironmentVariablesWhitelistProvider(final String authorizedSubjectsVariableName,
163 final String authorizedIssuersVariableName, Map<String, String> variables) {
164 this.authorizedSubjectsVariableName = requireNonNull(authorizedSubjectsVariableName);
165 this.authorizedIssuersVariableName = requireNonNull(authorizedIssuersVariableName);
166 this.variables = variables;
167 }
168
169 @Override
170 public Whitelist apply(final Asap asap) {
171 return new Whitelist(
172 getEnv(authorizedSubjectsVariableName),
173 getEnv(authorizedIssuersVariableName)
174 );
175 }
176
177 private Set<String> getEnv(String name) {
178 return Stream.of(variables.getOrDefault(name, "").split(","))
179 .map(String::trim)
180 .filter(StringUtils::isNotEmpty)
181 .collect(Collectors.toSet());
182 }
183 }
184 }