Fix the package name
[aaa.git] / aaa-shiro / impl / src / main / java / org / opendaylight / aaa / shiro / filters / MoonOAuthFilter.java
1 /*
2  * Copyright (c) 2016 Orange and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.aaa.shiro.filters;
9
10 import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
11 import static javax.servlet.http.HttpServletResponse.SC_CREATED;
12 import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
13 import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
14
15 import java.io.IOException;
16 import java.io.PrintWriter;
17
18 import javax.servlet.ServletRequest;
19 import javax.servlet.ServletResponse;
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
22
23 import org.apache.oltu.oauth2.as.response.OAuthASResponse;
24 import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
25 import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
26 import org.apache.oltu.oauth2.common.message.OAuthResponse;
27 import org.apache.oltu.oauth2.common.message.types.TokenType;
28 import org.apache.shiro.SecurityUtils;
29 import org.apache.shiro.authc.AuthenticationException;
30 import org.apache.shiro.authc.AuthenticationToken;
31 import org.apache.shiro.authc.UsernamePasswordToken;
32 import org.apache.shiro.subject.Subject;
33 import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
34 import org.opendaylight.aaa.AuthenticationBuilder;
35 import org.opendaylight.aaa.ClaimBuilder;
36 import org.opendaylight.aaa.api.Authentication;
37 import org.opendaylight.aaa.api.Claim;
38 import org.opendaylight.aaa.shiro.moon.MoonPrincipal;
39 import org.opendaylight.aaa.sts.OAuthRequest;
40 import org.opendaylight.aaa.sts.ServiceLocator;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * MoonOAuthFilter filters oauth1 requests form token based authentication
46  *
47  * @author Alioune BA alioune.ba@orange.com
48  */
49 public class MoonOAuthFilter extends AuthenticatingFilter {
50
51     private static final Logger LOG = LoggerFactory.getLogger(MoonOAuthFilter.class);
52
53     private static final String DOMAIN_SCOPE_REQUIRED = "Domain scope required";
54     private static final String NOT_IMPLEMENTED = "not_implemented";
55     private static final String UNAUTHORIZED = "unauthorized";
56     private static final String UNAUTHORIZED_CREDENTIALS = "Unauthorized: Login/Password incorrect";
57
58     static final String TOKEN_GRANT_ENDPOINT = "/token";
59     static final String TOKEN_REVOKE_ENDPOINT = "/revoke";
60     static final String TOKEN_VALIDATE_ENDPOINT = "/validate";
61
62     @Override
63     protected UsernamePasswordToken createToken(final ServletRequest request, final ServletResponse response) throws Exception {
64         final HttpServletRequest httpRequest;
65         final OAuthRequest oauthRequest;
66         try {
67             httpRequest = (HttpServletRequest) request;
68             oauthRequest = new OAuthRequest(httpRequest);
69         } catch (final ClassCastException e) {
70             LOG.debug("createToken() failed since the request could not be cast appropriately", e);
71             throw e;
72         }
73         return new UsernamePasswordToken(oauthRequest.getUsername(), oauthRequest.getPassword());
74     }
75
76     @Override
77     protected boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception {
78         final Subject currentUser = SecurityUtils.getSubject();
79         return executeLogin(request, response);
80     }
81
82     @Override
83     protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject,
84             final ServletRequest request, final ServletResponse response) throws Exception {
85
86         final HttpServletResponse httpResponse;
87         try {
88             httpResponse = (HttpServletResponse) response;
89         } catch (final ClassCastException e) {
90             LOG.debug("onLoginSuccess() failed since the response could not be cast appropriately", e);
91             throw e;
92         }
93
94         final MoonPrincipal principal;
95         try {
96             principal = (MoonPrincipal) subject.getPrincipals().getPrimaryPrincipal();
97         } catch (final ClassCastException e) {
98             LOG.debug("onLoginSuccess() failed since the subject could not be cast appropriately", e);
99             throw e;
100         }
101
102         final Claim claim = principal.principalToClaim();
103         oauthAccessTokenResponse(httpResponse, claim, "", principal.getToken());
104         return true;
105     }
106
107     @Override
108     protected boolean onLoginFailure(final AuthenticationToken token, final AuthenticationException e,
109             final ServletRequest request, final ServletResponse response) {
110
111         final HttpServletResponse resp;
112         try {
113             resp = (HttpServletResponse) response;
114             error(resp, SC_BAD_REQUEST, UNAUTHORIZED_CREDENTIALS);
115         } catch (final ClassCastException cce) {
116             LOG.warn("onLoginFailure() failed since the response could not be cast appropriately", cce);
117         }
118
119         return false;
120     }
121
122     protected boolean executeLogin(final ServletRequest request, final ServletResponse response) throws Exception {
123
124         final HttpServletRequest req;
125         try {
126             req = (HttpServletRequest) request;
127         } catch (final ClassCastException e) {
128             LOG.debug("executeLogin() failed since the request could not be cast appropriately", e);
129             throw e;
130         }
131
132         final HttpServletResponse resp;
133         try {
134             resp = (HttpServletResponse) response;
135         } catch (final ClassCastException e) {
136             LOG.debug("executeLogin() failed since the request could not be cast apprioately", e);
137             throw e;
138         }
139
140         try {
141             if (req.getServletPath().equals(TOKEN_GRANT_ENDPOINT)) {
142                 final UsernamePasswordToken token = createToken(request, response);
143                 if (token == null) {
144                     final String msg = "A valid non-null AuthenticationToken " +
145                             "must be created in order to execute a login attempt.";
146                     throw new IllegalStateException(msg);
147                 }
148                 try {
149                     final Subject subject = getSubject(request, response);
150                     subject.login(token);
151                     return onLoginSuccess(token, subject, request, response);
152                 } catch (final AuthenticationException e) {
153                     return onLoginFailure(token, e, request, response);
154                 }
155             } else if (req.getServletPath().equals(TOKEN_REVOKE_ENDPOINT)) {
156                 //TODO: deleteAccessToken(req, resp);
157             } else if (req.getServletPath().equals(TOKEN_VALIDATE_ENDPOINT)) {
158                 //TODO: validateToken(req, resp);
159             }
160         } catch (final AuthenticationException e) {
161             error(resp, SC_UNAUTHORIZED, e.getMessage());
162         } catch (final OAuthProblemException oe) {
163             error(resp, oe);
164         } catch (final Exception e) {
165             error(resp, e);
166         }
167         return false;
168     }
169
170     private void oauthAccessTokenResponse(final HttpServletResponse resp, final Claim claim, final String clientId, final String token)
171             throws OAuthSystemException, IOException {
172
173         if (claim == null) {
174             throw new AuthenticationException(UNAUTHORIZED);
175         }
176
177         // Cache this token...
178         final Authentication auth = new AuthenticationBuilder(new ClaimBuilder(claim).setClientId(
179                 clientId).build()).setExpiration(tokenExpiration()).build();
180         ServiceLocator.getInstance().getTokenStore().put(token, auth);
181
182         final OAuthResponse r = OAuthASResponse.tokenResponse(SC_CREATED).setAccessToken(token)
183                                          .setTokenType(TokenType.BEARER.toString())
184                                          .setExpiresIn(Long.toString(auth.expiration()))
185                                          .buildJSONMessage();
186         write(resp, r);
187     }
188
189     private void write(final HttpServletResponse resp, final OAuthResponse r) throws IOException {
190         resp.setStatus(r.getResponseStatus());
191         PrintWriter pw = resp.getWriter();
192         pw.print(r.getBody());
193         pw.flush();
194         pw.close();
195     }
196
197     private long tokenExpiration() {
198         return ServiceLocator.getInstance().getTokenStore().tokenExpiration();
199     }
200
201     /**
202      * Utility method used to emit an error OAuthResponse with the given HTTP code
203      */
204     private void error(final HttpServletResponse resp, final int httpCode, final String error) {
205         try {
206             final OAuthResponse r = OAuthResponse.errorResponse(httpCode).setError(error)
207                                            .buildJSONMessage();
208             write(resp, r);
209         } catch (final IOException | OAuthSystemException ex) {
210             LOG.error("Failed to write the error ", ex);
211         }
212     }
213
214     private void error(final HttpServletResponse resp, final OAuthProblemException e) {
215         try {
216             final OAuthResponse r = OAuthResponse.errorResponse(SC_BAD_REQUEST).error(e)
217                                            .buildJSONMessage();
218             write(resp, r);
219         } catch (final IOException | OAuthSystemException ex) {
220             LOG.error("Failed to write the error ", ex);
221         }
222     }
223
224     private void error(final HttpServletResponse resp, final Exception e) {
225         try {
226             final OAuthResponse r = OAuthResponse.errorResponse(SC_INTERNAL_SERVER_ERROR)
227                                            .setError(e.getClass().getName())
228                                            .setErrorDescription(e.getMessage()).buildJSONMessage();
229             write(resp, r);
230         } catch (final IOException | OAuthSystemException ex) {
231             LOG.error("Failed to write the error ", ex);
232         }
233     }
234
235 }