aboutsummaryrefslogtreecommitdiffstats
path: root/upstream/odl-aaa-moon/aaa/aaa-authn-sts/src/main/java/org/opendaylight/aaa/sts/TokenAuthFilter.java
blob: 3fa7a66c9cc3dab94e45b756a8b5bd46bd5118ea (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Copyright (c) 2014, 2015 Hewlett-Packard Development Company, L.P. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.aaa.sts;

import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerRequestFilter;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.opendaylight.aaa.api.Authentication;
import org.opendaylight.aaa.api.AuthenticationException;
import org.opendaylight.aaa.api.TokenAuth;

/**
 * A token-based authentication filter for resource providers.
 *
 * Deprecated: Use <code>AAAFilter</code> instead.
 *
 * @author liemmn
 *
 */
@Deprecated
public class TokenAuthFilter implements ContainerRequestFilter {

    private final String OPTIONS = "OPTIONS";
    private final String ACCESS_CONTROL_REQUEST_HEADERS = "Access-Control-Request-Headers";
    private final String AUTHORIZATION = "authorization";

    @Context
    private HttpServletRequest httpRequest;

    @Override
    public ContainerRequest filter(ContainerRequest request) {

        // Do the CORS check first
        if (checkCORSOptionRequest(request)) {
            return request;
        }

        // Are we up yet?
        if (ServiceLocator.getInstance().getAuthenticationService() == null) {
            throw new WebApplicationException(
                    Response.status(Status.SERVICE_UNAVAILABLE).type(MediaType.APPLICATION_JSON)
                            .entity("{\"error\":\"Authentication service unavailable\"}").build());
        }

        // Are we doing authentication or not?
        if (ServiceLocator.getInstance().getAuthenticationService().isAuthEnabled()) {
            Map<String, List<String>> headers = request.getRequestHeaders();

            // Go through and invoke other TokenAuth first...
            List<TokenAuth> tokenAuthCollection = ServiceLocator.getInstance()
                                                                .getTokenAuthCollection();
            for (TokenAuth ta : tokenAuthCollection) {
                try {
                    Authentication auth = ta.validate(headers);
                    if (auth != null) {
                        ServiceLocator.getInstance().getAuthenticationService().set(auth);
                        return request;
                    }
                } catch (AuthenticationException ae) {
                    throw unauthorized();
                }
            }

            // OK, last chance to validate token...
            try {
                OAuthAccessResourceRequest or = new OAuthAccessResourceRequest(httpRequest,
                        ParameterStyle.HEADER);
                validate(or.getAccessToken());
            } catch (OAuthSystemException | OAuthProblemException e) {
                throw unauthorized();
            }
        }

        return request;
    }

    /**
     * CORS access control : when browser sends cross-origin request, it first
     * sends the OPTIONS method with a list of access control request headers,
     * which has a list of custom headers and access control method such as GET.
     * POST etc. You custom header "Authorization will not be present in request
     * header, instead it will be present as a value inside
     * Access-Control-Request-Headers. We should not do any authorization
     * against such request. for more details :
     * https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
     */

    private boolean checkCORSOptionRequest(ContainerRequest request) {
        if (OPTIONS.equals(request.getMethod())) {
            List<String> headerList = request.getRequestHeader(ACCESS_CONTROL_REQUEST_HEADERS);
            if (headerList != null && !headerList.isEmpty()) {
                String header = headerList.get(0);
                if (header != null && header.toLowerCase().contains(AUTHORIZATION)) {
                    return true;
                }
            }
        }
        return false;
    }

    // Validate an ODL token...
    private Authentication validate(final String token) {
        Authentication auth = ServiceLocator.getInstance().getTokenStore().get(token);
        if (auth == null) {
            throw unauthorized();
        } else {
            ServiceLocator.getInstance().getAuthenticationService().set(auth);
        }
        return auth;
    }

    // Houston, we got a problem!
    private static final WebApplicationException unauthorized() {
        ServiceLocator.getInstance().getAuthenticationService().clear();
        return new UnauthorizedException();
    }

    // A custom 401 web exception that handles http basic response as well
    static final class UnauthorizedException extends WebApplicationException {
        private static final long serialVersionUID = -1732363804773027793L;
        static final String WWW_AUTHENTICATE = "WWW-Authenticate";
        static final Object OPENDAYLIGHT = "Basic realm=\"opendaylight\"";
        private static final Response response = Response.status(Status.UNAUTHORIZED)
                                                         .header(WWW_AUTHENTICATE, OPENDAYLIGHT)
                                                         .build();

        public UnauthorizedException() {
            super(response);
        }
    }
}