View Javadoc
1   package com.atlassian.refapp.applinks;
2   
3   import com.atlassian.applinks.api.ApplicationLink;
4   import com.atlassian.applinks.api.ApplicationLinkRequestFactory;
5   import com.atlassian.applinks.api.ApplicationLinkResponseHandler;
6   import com.atlassian.applinks.api.ApplicationLinkService;
7   import com.atlassian.applinks.api.CredentialsRequiredException;
8   import com.atlassian.sal.api.net.Request;
9   import com.atlassian.sal.api.net.Response;
10  import com.atlassian.sal.api.net.ResponseException;
11  import com.atlassian.templaterenderer.TemplateRenderer;
12  import com.atlassian.webresource.api.assembler.PageBuilderService;
13  import com.google.common.collect.ImmutableMap;
14  
15  import javax.servlet.ServletException;
16  import javax.servlet.http.HttpServlet;
17  import javax.servlet.http.HttpServletRequest;
18  import javax.servlet.http.HttpServletResponse;
19  import java.io.IOException;
20  import java.net.URI;
21  import java.net.URISyntaxException;
22  import java.util.Map;
23  
24  public class WhoamiApplinksServlet extends HttpServlet {
25      private static final String TEMPLATE = "/templates/whoami.vm";
26      private static final String ENDPOINT = "/plugins/servlet/applinks/whoami";
27  
28      private final TemplateRenderer templateRenderer;
29      private final ApplicationLinkService applicationLinkService;
30      private final PageBuilderService pageBuilderService;
31  
32      public WhoamiApplinksServlet(final ApplicationLinkService applicationLinkService,
33                                   final TemplateRenderer templateRenderer,
34                                   final PageBuilderService pageBuilderService) {
35          this.templateRenderer = templateRenderer;
36          this.applicationLinkService = applicationLinkService;
37          this.pageBuilderService = pageBuilderService;
38      }
39  
40      @Override
41      protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
42              throws ServletException, IOException {
43          final ImmutableMap.Builder<String, Object> contextBuilder = ImmutableMap.builder();
44          final ImmutableMap.Builder<ApplicationLink, AuthenticationInformation> recordBuilder = ImmutableMap.builder();
45  
46          for (ApplicationLink link : applicationLinkService.getApplicationLinks()) {
47              recordBuilder.put(link, getAuthenticationInformation(request, link));
48          }
49  
50          contextBuilder.put("records", recordBuilder.build());
51          render(TEMPLATE, contextBuilder.build(), response);
52      }
53  
54      private AuthenticationInformation getAuthenticationInformation(final HttpServletRequest request, final ApplicationLink link) {
55          final ApplicationLinkRequestFactory requestFactory = link.createAuthenticatedRequestFactory();
56          final Holder<String> username = new Holder<String>();
57          final Holder<AuthenticationStatus> status = new Holder<AuthenticationStatus>(AuthenticationStatus.COMMUNICATION_ERROR);
58          final Holder<String> errorMessage = new Holder<String>();
59          try {
60              requestFactory
61                      .createRequest(Request.MethodType.GET, ENDPOINT)
62                      .execute(new ApplicationLinkResponseHandler() {
63                          public Void handle(final Response response) throws ResponseException {
64                              if (response.isSuccessful()) {
65                                  final String body = response.getResponseBodyAsString();
66                                  if (body == null || "".equals(body)) {
67                                      status.set(AuthenticationStatus.ANONYMOUS);
68                                  } else {
69                                      username.set(body);
70                                      status.set(AuthenticationStatus.ACCEPTED);
71                                  }
72                              } else {
73                                  status.set(AuthenticationStatus.COMMUNICATION_ERROR);
74                                  errorMessage.set(String.format("%s: %s", response.getStatusCode(), response.getStatusText()));
75                              }
76                              return null;
77                          }
78  
79                          public Void credentialsRequired(final Response response) throws ResponseException {
80                              status.set(AuthenticationStatus.CREDENTIALS_REQUIRED);
81                              return null;
82                          }
83                      });
84  
85          } catch (CredentialsRequiredException cre) {
86              status.set(AuthenticationStatus.CREDENTIALS_REQUIRED);
87          } catch (ResponseException re) {
88              status.set(AuthenticationStatus.COMMUNICATION_ERROR);
89              errorMessage.set(re.getMessage());
90          }
91          final String authorisationURL = requestFactory.getAuthorisationURI() == null ? null :
92                  requestFactory.getAuthorisationURI(getCurrentLocation(request)).toString();
93          return new AuthenticationInformation(username.get(), errorMessage.get(), authorisationURL, status.get());
94      }
95  
96      private URI getCurrentLocation(final HttpServletRequest request) {
97          try {
98              return new URI(request.getRequestURL().toString());
99          } catch (URISyntaxException e) {
100             // should never happen
101             throw new RuntimeException(e);
102         }
103     }
104 
105     private void render(final String template, Map<String, Object> context, final HttpServletResponse response)
106             throws IOException {
107         response.setContentType("text/html; charset=utf-8");
108         pageBuilderService.assembler().resources().requireWebResource("com.atlassian.auiplugin:ajs");
109         templateRenderer.render(template, context, response.getWriter());
110     }
111 
112     public static class AuthenticationInformation {
113         private final AuthenticationStatus status;
114         private final String username;
115         private final String errorMessage;
116         private final String authorisationURL;
117 
118         public AuthenticationInformation(String username, String errorMessage, String authorisationURL, AuthenticationStatus status) {
119             this.errorMessage = errorMessage;
120             this.authorisationURL = authorisationURL;
121             this.status = status;
122             this.username = username;
123         }
124 
125         public String getAuthorisationURL() {
126             return authorisationURL;
127         }
128 
129         public String getErrorMessage() {
130             return errorMessage;
131         }
132 
133         public String getStatus() {
134             return status.name();
135         }
136 
137         /**
138          * {@code null} implies anonymous.
139          */
140         public String getUsername() {
141             return username;
142         }
143     }
144 
145     public static enum AuthenticationStatus {
146         ACCEPTED,
147         CREDENTIALS_REQUIRED,
148         COMMUNICATION_ERROR,
149         ANONYMOUS
150     }
151 
152     /**
153      * Generic holder class
154      */
155     private static class Holder<T> {
156         private T value;
157 
158         public Holder() {
159             value = null;
160         }
161 
162         public void set(final T value) {
163             this.value = value;
164         }
165 
166         public T get() {
167             return value;
168         }
169 
170         public Holder(final T value) {
171             this.value = value;
172         }
173     }
174 }