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;
18 import javax.servlet.ServletRequest;
19 import javax.servlet.ServletResponse;
20 import javax.servlet.http.HttpServletRequest;
21 import javax.servlet.http.HttpServletResponse;
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;
45 * MoonOAuthFilter filters oauth1 requests form token based authentication
47 * @author Alioune BA alioune.ba@orange.com
49 public class MoonOAuthFilter extends AuthenticatingFilter {
51 private static final Logger LOG = LoggerFactory.getLogger(MoonOAuthFilter.class);
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";
58 static final String TOKEN_GRANT_ENDPOINT = "/token";
59 static final String TOKEN_REVOKE_ENDPOINT = "/revoke";
60 static final String TOKEN_VALIDATE_ENDPOINT = "/validate";
63 protected UsernamePasswordToken createToken(final ServletRequest request, final ServletResponse response) throws Exception {
64 final HttpServletRequest httpRequest;
65 final OAuthRequest oauthRequest;
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);
73 return new UsernamePasswordToken(oauthRequest.getUsername(), oauthRequest.getPassword());
77 protected boolean onAccessDenied(final ServletRequest request, final ServletResponse response) throws Exception {
78 final Subject currentUser = SecurityUtils.getSubject();
79 return executeLogin(request, response);
83 protected boolean onLoginSuccess(final AuthenticationToken token, final Subject subject,
84 final ServletRequest request, final ServletResponse response) throws Exception {
86 final HttpServletResponse httpResponse;
88 httpResponse = (HttpServletResponse) response;
89 } catch (final ClassCastException e) {
90 LOG.debug("onLoginSuccess() failed since the response could not be cast appropriately", e);
94 final MoonPrincipal principal;
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);
102 final Claim claim = principal.principalToClaim();
103 oauthAccessTokenResponse(httpResponse, claim, "", principal.getToken());
108 protected boolean onLoginFailure(final AuthenticationToken token, final AuthenticationException e,
109 final ServletRequest request, final ServletResponse response) {
111 final HttpServletResponse resp;
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);
122 protected boolean executeLogin(final ServletRequest request, final ServletResponse response) throws Exception {
124 final HttpServletRequest req;
126 req = (HttpServletRequest) request;
127 } catch (final ClassCastException e) {
128 LOG.debug("executeLogin() failed since the request could not be cast appropriately", e);
132 final HttpServletResponse resp;
134 resp = (HttpServletResponse) response;
135 } catch (final ClassCastException e) {
136 LOG.debug("executeLogin() failed since the request could not be cast apprioately", e);
141 if (req.getServletPath().equals(TOKEN_GRANT_ENDPOINT)) {
142 final UsernamePasswordToken token = createToken(request, response);
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);
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);
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);
160 } catch (final AuthenticationException e) {
161 error(resp, SC_UNAUTHORIZED, e.getMessage());
162 } catch (final OAuthProblemException oe) {
164 } catch (final Exception e) {
170 private void oauthAccessTokenResponse(final HttpServletResponse resp, final Claim claim, final String clientId, final String token)
171 throws OAuthSystemException, IOException {
174 throw new AuthenticationException(UNAUTHORIZED);
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);
182 final OAuthResponse r = OAuthASResponse.tokenResponse(SC_CREATED).setAccessToken(token)
183 .setTokenType(TokenType.BEARER.toString())
184 .setExpiresIn(Long.toString(auth.expiration()))
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());
197 private long tokenExpiration() {
198 return ServiceLocator.getInstance().getTokenStore().tokenExpiration();
202 * Utility method used to emit an error OAuthResponse with the given HTTP code
204 private void error(final HttpServletResponse resp, final int httpCode, final String error) {
206 final OAuthResponse r = OAuthResponse.errorResponse(httpCode).setError(error)
209 } catch (final IOException | OAuthSystemException ex) {
210 LOG.error("Failed to write the error ", ex);
214 private void error(final HttpServletResponse resp, final OAuthProblemException e) {
216 final OAuthResponse r = OAuthResponse.errorResponse(SC_BAD_REQUEST).error(e)
219 } catch (final IOException | OAuthSystemException ex) {
220 LOG.error("Failed to write the error ", ex);
224 private void error(final HttpServletResponse resp, final Exception e) {
226 final OAuthResponse r = OAuthResponse.errorResponse(SC_INTERNAL_SERVER_ERROR)
227 .setError(e.getClass().getName())
228 .setErrorDescription(e.getMessage()).buildJSONMessage();
230 } catch (final IOException | OAuthSystemException ex) {
231 LOG.error("Failed to write the error ", ex);