2 * Copyright (c) 2016 Orange and others. All rights reserved.
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
8 package org.opendaylight.aaa.shiro.filters;
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;
15 import java.io.IOException;
16 import java.io.PrintWriter;
17 import javax.servlet.ServletRequest;
18 import javax.servlet.ServletResponse;
19 import javax.servlet.http.HttpServletRequest;
20 import javax.servlet.http.HttpServletResponse;
21 import org.apache.oltu.oauth2.as.response.OAuthASResponse;
22 import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
23 import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
24 import org.apache.oltu.oauth2.common.message.OAuthResponse;
25 import org.apache.oltu.oauth2.common.message.types.TokenType;
26 import org.apache.shiro.SecurityUtils;
27 import org.apache.shiro.authc.AuthenticationException;
28 import org.apache.shiro.authc.AuthenticationToken;
29 import org.apache.shiro.authc.UsernamePasswordToken;
30 import org.apache.shiro.subject.Subject;
31 import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
32 import org.opendaylight.aaa.api.Authentication;
33 import org.opendaylight.aaa.api.Claim;
34 import org.opendaylight.aaa.api.TokenStore;
35 import org.opendaylight.aaa.shiro.moon.MoonPrincipal;
36 import org.opendaylight.aaa.shiro.oauth2.OAuthRequest;
37 import org.opendaylight.aaa.shiro.tokenauthrealm.auth.AuthenticationBuilder;
38 import org.opendaylight.aaa.shiro.tokenauthrealm.auth.ClaimBuilder;
39 import org.opendaylight.aaa.shiro.web.env.ThreadLocals;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
44 * MoonOAuthFilter filters oauth1 requests form token based authentication
46 * @author Alioune BA alioune.ba@orange.com
48 public class MoonOAuthFilter extends AuthenticatingFilter {
50 private static final Logger LOG = LoggerFactory.getLogger(MoonOAuthFilter.class);
52 private static final String DOMAIN_SCOPE_REQUIRED = "Domain scope required";
53 private static final String NOT_IMPLEMENTED = "not_implemented";
54 private static final String UNAUTHORIZED = "unauthorized";
55 private static final String UNAUTHORIZED_CREDENTIALS = "Unauthorized: Login/Password incorrect";
57 static final String TOKEN_GRANT_ENDPOINT = "/token";
58 static final String TOKEN_REVOKE_ENDPOINT = "/revoke";
59 static final String TOKEN_VALIDATE_ENDPOINT = "/validate";
61 private final TokenStore tokenStore;
63 public MoonOAuthFilter() {
64 tokenStore = ThreadLocals.TOKEN_STORE_TL.get();
68 protected UsernamePasswordToken createToken(final ServletRequest request, final ServletResponse response) throws Exception {
69 final HttpServletRequest httpRequest;
70 final OAuthRequest oauthRequest;
72 httpRequest = (HttpServletRequest) request;
73 oauthRequest = new OAuthRequest(httpRequest);
74 } catch (final ClassCastException e) {
75 LOG.debug("createToken() failed since the request could not be cast appropriately", e);
78 return new UsernamePasswordToken(oauthRequest.getUsername(), oauthRequest.getPassword());
82 protected boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception {
83 final Subject currentUser = SecurityUtils.getSubject();
84 return executeLogin(request, response);
88 protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject,
89 final ServletRequest request, final ServletResponse response) throws Exception {
91 final HttpServletResponse httpResponse;
93 httpResponse = (HttpServletResponse) response;
94 } catch (final ClassCastException e) {
95 LOG.debug("onLoginSuccess() failed since the response could not be cast appropriately", e);
99 final MoonPrincipal principal;
101 principal = (MoonPrincipal) subject.getPrincipals().getPrimaryPrincipal();
102 } catch (final ClassCastException e) {
103 LOG.debug("onLoginSuccess() failed since the subject could not be cast appropriately", e);
107 final Claim claim = principal.principalToClaim();
108 oauthAccessTokenResponse(httpResponse, claim, "", principal.getToken());
113 protected boolean onLoginFailure(final AuthenticationToken token, final AuthenticationException e,
114 final ServletRequest request, final ServletResponse response) {
116 final HttpServletResponse resp;
118 resp = (HttpServletResponse) response;
119 error(resp, SC_BAD_REQUEST, UNAUTHORIZED_CREDENTIALS);
120 } catch (final ClassCastException cce) {
121 LOG.warn("onLoginFailure() failed since the response could not be cast appropriately", cce);
128 protected boolean executeLogin(final ServletRequest request, final ServletResponse response) throws Exception {
130 final HttpServletRequest req;
132 req = (HttpServletRequest) request;
133 } catch (final ClassCastException e) {
134 LOG.debug("executeLogin() failed since the request could not be cast appropriately", e);
138 final HttpServletResponse resp;
140 resp = (HttpServletResponse) response;
141 } catch (final ClassCastException e) {
142 LOG.debug("executeLogin() failed since the request could not be cast apprioately", e);
147 if (req.getServletPath().equals(TOKEN_GRANT_ENDPOINT)) {
148 final UsernamePasswordToken token = createToken(request, response);
150 final String msg = "A valid non-null AuthenticationToken " +
151 "must be created in order to execute a login attempt.";
152 throw new IllegalStateException(msg);
155 final Subject subject = getSubject(request, response);
156 subject.login(token);
157 return onLoginSuccess(token, subject, request, response);
158 } catch (final AuthenticationException e) {
159 return onLoginFailure(token, e, request, response);
161 } else if (req.getServletPath().equals(TOKEN_REVOKE_ENDPOINT)) {
162 //TODO: deleteAccessToken(req, resp);
163 } else if (req.getServletPath().equals(TOKEN_VALIDATE_ENDPOINT)) {
164 //TODO: validateToken(req, resp);
166 } catch (final AuthenticationException e) {
167 error(resp, SC_UNAUTHORIZED, e.getMessage());
168 } catch (final OAuthProblemException oe) {
170 } catch (final Exception e) {
176 private void oauthAccessTokenResponse(final HttpServletResponse resp, final Claim claim, final String clientId, final String token)
177 throws OAuthSystemException, IOException {
180 throw new AuthenticationException(UNAUTHORIZED);
183 // Cache this token...
184 final Authentication auth = new AuthenticationBuilder(new ClaimBuilder(claim).setClientId(
185 clientId).build()).setExpiration(tokenExpiration()).build();
186 tokenStore.put(token, auth);
188 final OAuthResponse r = OAuthASResponse.tokenResponse(SC_CREATED).setAccessToken(token)
189 .setTokenType(TokenType.BEARER.toString())
190 .setExpiresIn(Long.toString(auth.expiration()))
195 private void write(final HttpServletResponse resp, final OAuthResponse r) throws IOException {
196 resp.setStatus(r.getResponseStatus());
197 PrintWriter pw = resp.getWriter();
198 pw.print(r.getBody());
203 private long tokenExpiration() {
204 return tokenStore.tokenExpiration();
208 * Utility method used to emit an error OAuthResponse with the given HTTP code
210 private void error(final HttpServletResponse resp, final int httpCode, final String error) {
212 final OAuthResponse r = OAuthResponse.errorResponse(httpCode).setError(error)
215 } catch (final IOException | OAuthSystemException ex) {
216 LOG.error("Failed to write the error ", ex);
220 private void error(final HttpServletResponse resp, final OAuthProblemException e) {
222 final OAuthResponse r = OAuthResponse.errorResponse(SC_BAD_REQUEST).error(e)
225 } catch (final IOException | OAuthSystemException ex) {
226 LOG.error("Failed to write the error ", ex);
230 private void error(final HttpServletResponse resp, final Exception e) {
232 final OAuthResponse r = OAuthResponse.errorResponse(SC_INTERNAL_SERVER_ERROR)
233 .setError(e.getClass().getName())
234 .setErrorDescription(e.getMessage()).buildJSONMessage();
236 } catch (final IOException | OAuthSystemException ex) {
237 LOG.error("Failed to write the error ", ex);