1 package com.atlassian.asap.core.server.jersey;
2
3 import com.atlassian.asap.api.Jwt;
4 import com.atlassian.asap.api.exception.AuthenticationFailedException;
5 import com.atlassian.asap.api.exception.PermanentAuthenticationFailedException;
6 import com.atlassian.asap.api.exception.TransientAuthenticationFailedException;
7 import com.atlassian.asap.api.server.http.RequestAuthenticator;
8 import com.atlassian.asap.core.keys.KeyProvider;
9 import com.atlassian.asap.core.server.AuthenticationContext;
10 import com.atlassian.asap.core.server.http.RequestAuthenticatorFactory;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import javax.annotation.Nullable;
15 import javax.annotation.Priority;
16 import javax.ws.rs.Priorities;
17 import javax.ws.rs.container.ContainerRequestContext;
18 import javax.ws.rs.container.ContainerRequestFilter;
19 import javax.ws.rs.container.ResourceInfo;
20 import javax.ws.rs.core.Context;
21 import javax.ws.rs.core.HttpHeaders;
22 import javax.ws.rs.ext.Provider;
23 import java.io.IOException;
24 import java.security.PublicKey;
25 import java.util.Optional;
26
27 import static com.atlassian.asap.core.server.filter.AbstractRequestAuthenticationFilter.AUTHENTIC_JWT_REQUEST_ATTRIBUTE;
28 import static java.util.Objects.requireNonNull;
29
30
31
32
33
34 @Provider
35 @Priority(Priorities.AUTHENTICATION)
36 public class AuthenticationRequestFilter implements ContainerRequestFilter {
37 static final int MAX_TRANSIENT_FAILURES_RETRIES = 10;
38
39 static final String ASAP_REQUEST_ATTRIBUTE = "asap.annotation";
40
41 private static final Logger LOG = LoggerFactory.getLogger(AuthenticationRequestFilter.class);
42
43 @SuppressWarnings("checkstyle:VisibilityModifier")
44 @Context
45 ResourceInfo resourceInfo;
46
47 private final RequestAuthenticator authenticator;
48 private final FailureHandler failureHandler;
49
50 @SuppressWarnings("WeakerAccess")
51 public AuthenticationRequestFilter(RequestAuthenticator authenticator, FailureHandler failureHandler) {
52 this.failureHandler = requireNonNull(failureHandler);
53 this.authenticator = requireNonNull(authenticator);
54 }
55
56 @Override
57 public void filter(ContainerRequestContext context) throws IOException {
58 needsAuthentication().ifPresent(asap ->
59 {
60 context.setProperty(AUTHENTIC_JWT_REQUEST_ATTRIBUTE, authenticateToken(context));
61 context.setProperty(ASAP_REQUEST_ATTRIBUTE, asap);
62 }
63 );
64 }
65
66 @Nullable
67 private Jwt authenticateToken(final ContainerRequestContext context) {
68 final String authorizationHeader = context.getHeaderString(HttpHeaders.AUTHORIZATION);
69
70 for (int tryCounter = 0; tryCounter < MAX_TRANSIENT_FAILURES_RETRIES; tryCounter++) {
71 try {
72 Jwt authenticJwt = authenticator.authenticateRequest(authorizationHeader);
73 LOG.trace("Accepting authentic token with identifier '{}'", authenticJwt.getClaims().getJwtId());
74 return authenticJwt;
75 } catch (TransientAuthenticationFailedException e) {
76 if (tryCounter >= MAX_TRANSIENT_FAILURES_RETRIES - 1) {
77 failureHandler.onAuthenticationFailure(context, e);
78 return null;
79 } else {
80 final boolean retry = failureHandler.onTransientAuthenticationFailure(context, e);
81 if (!retry) {
82 return null;
83 }
84 }
85 } catch (PermanentAuthenticationFailedException e) {
86 failureHandler.onPermanentAuthenticationFailure(context, e);
87 return null;
88 } catch (AuthenticationFailedException e) {
89 failureHandler.onAuthenticationFailure(context, e);
90 return null;
91 }
92 }
93 throw new IllegalStateException();
94 }
95
96
97
98
99 private Optional<Asap> needsAuthentication() {
100 Asap asap = null;
101 if (resourceInfo.getResourceMethod().isAnnotationPresent(Asap.class)) {
102 asap = resourceInfo.getResourceMethod().getAnnotation(Asap.class);
103 } else if (resourceInfo.getResourceClass().isAnnotationPresent(Asap.class)) {
104 asap = resourceInfo.getResourceClass().getAnnotation(Asap.class);
105 } else if (resourceInfo.getResourceClass().getPackage().isAnnotationPresent(Asap.class)) {
106 asap = resourceInfo.getResourceClass().getPackage().getAnnotation(Asap.class);
107 }
108 return Optional.ofNullable(asap)
109 .filter(Asap::enabled);
110 }
111
112
113
114
115
116
117
118
119
120
121 public static AuthenticationRequestFilter newInstance(String audience, String repoUrl) {
122 return newInstance(new AuthenticationContext(audience, repoUrl));
123 }
124
125
126
127
128
129
130
131
132
133 public static AuthenticationRequestFilter newInstance(String audience, KeyProvider<PublicKey> keyProvider) {
134 return newInstance(new AuthenticationContext(audience, keyProvider));
135 }
136
137
138
139
140
141
142
143 public static AuthenticationRequestFilter newInstance(AuthenticationContext authenticationContext) {
144 RequestAuthenticatorFactory requestAuthenticatorFactory = new RequestAuthenticatorFactory();
145 RequestAuthenticator authenticator = requestAuthenticatorFactory.create(authenticationContext);
146
147 return new AuthenticationRequestFilter(authenticator, new EmptyBodyFailureHandler());
148 }
149 }