Search in sources :

Example 1 with TokenState

use of co.cask.cdap.security.auth.TokenState in project cdap by caskdata.

the class SecurityAuthenticationHttpHandler method validateSecuredInterception.

/**
   * Intercepts the HttpMessage for getting the access token in authorization header
   *
   * @param ctx channel handler context delegated from MessageReceived callback
   * @param msg intercepted HTTP message
   * @param inboundChannel
   * @return {@code true} if the HTTP message has valid Access token
   * @throws Exception
   */
private boolean validateSecuredInterception(ChannelHandlerContext ctx, HttpRequest msg, Channel inboundChannel, AuditLogEntry logEntry) throws Exception {
    String auth = msg.getHeader(HttpHeaders.Names.AUTHORIZATION);
    String accessToken = null;
    /*
     * Parse the access token from authorization header.  The header will be in the form:
     *     Authorization: Bearer ACCESSTOKEN
     *
     * where ACCESSTOKEN is the base64 encoded serialized AccessToken instance.
     */
    if (auth != null) {
        int spIndex = auth.trim().indexOf(' ');
        if (spIndex != -1) {
            accessToken = auth.substring(spIndex + 1).trim();
        }
    }
    HttpMethod httpMethod = msg.getMethod();
    String uri = msg.getUri();
    logEntry.setClientIP(((InetSocketAddress) ctx.getChannel().getRemoteAddress()).getAddress());
    logEntry.setRequestLine(httpMethod, uri, msg.getProtocolVersion());
    TokenState tokenState = tokenValidator.validate(accessToken);
    if (!tokenState.isValid()) {
        HttpResponse httpResponse = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED);
        logEntry.setResponseCode(HttpResponseStatus.UNAUTHORIZED.getCode());
        JsonObject jsonObject = new JsonObject();
        if (tokenState == TokenState.MISSING) {
            httpResponse.addHeader(HttpHeaders.Names.WWW_AUTHENTICATE, String.format("Bearer realm=\"%s\"", realm));
            LOG.debug("Authentication failed due to missing token");
        } else {
            httpResponse.addHeader(HttpHeaders.Names.WWW_AUTHENTICATE, String.format("Bearer realm=\"%s\" error=\"invalid_token\"" + " error_description=\"%s\"", realm, tokenState.getMsg()));
            jsonObject.addProperty("error", "invalid_token");
            jsonObject.addProperty("error_description", tokenState.getMsg());
            LOG.debug("Authentication failed due to invalid token, reason={};", tokenState);
        }
        JsonArray externalAuthenticationURIs = new JsonArray();
        // Waiting for service to get discovered
        stopWatchWait(externalAuthenticationURIs);
        jsonObject.add("auth_uri", externalAuthenticationURIs);
        ChannelBuffer content = ChannelBuffers.wrappedBuffer(jsonObject.toString().getBytes(Charsets.UTF_8));
        httpResponse.setContent(content);
        int contentLength = content.readableBytes();
        httpResponse.setHeader(HttpHeaders.Names.CONTENT_LENGTH, contentLength);
        httpResponse.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/json;charset=UTF-8");
        logEntry.setResponseContentLength(new Long(contentLength));
        ChannelFuture writeFuture = Channels.future(inboundChannel);
        Channels.write(ctx, writeFuture, httpResponse);
        writeFuture.addListener(ChannelFutureListener.CLOSE);
        return false;
    } else {
        AccessTokenTransformer.AccessTokenIdentifierPair accessTokenIdentifierPair = accessTokenTransformer.transform(accessToken);
        AuditLogContent auditLogContent = AUDIT_LOG_LOOKUP_METHOD.contains(httpMethod) ? AUDIT_LOOK_UP.getAuditLogContent(msg.getUri(), httpMethod) : null;
        if (auditLogContent != null) {
            List<String> headerNames = auditLogContent.getHeaderNames();
            if (!headerNames.isEmpty()) {
                Map<String, String> headers = new HashMap<>();
                for (String headerName : headerNames) {
                    headers.put(headerName, msg.getHeader(headerName));
                }
                logEntry.setHeaders(headers);
            }
            if (auditLogContent.isLogRequestBody()) {
                ChannelBuffer body = msg.getContent();
                if (body.readable()) {
                    logEntry.setRequestBody(body.toString(Charsets.UTF_8));
                }
            }
            logEntry.setLogResponseBody(auditLogContent.isLogResponsebody());
        }
        logEntry.setUserName(accessTokenIdentifierPair.getAccessTokenIdentifierObj().getUsername());
        msg.setHeader(HttpHeaders.Names.AUTHORIZATION, "CDAP-verified " + accessTokenIdentifierPair.getAccessTokenIdentifierStr());
        msg.setHeader(Constants.Security.Headers.USER_ID, accessTokenIdentifierPair.getAccessTokenIdentifierObj().getUsername());
        msg.setHeader(Constants.Security.Headers.USER_IP, ((InetSocketAddress) ctx.getChannel().getRemoteAddress()).getAddress().getHostAddress());
        return true;
    }
}
Also used : ChannelFuture(org.jboss.netty.channel.ChannelFuture) HashMap(java.util.HashMap) InetSocketAddress(java.net.InetSocketAddress) DefaultHttpResponse(org.jboss.netty.handler.codec.http.DefaultHttpResponse) HttpResponse(org.jboss.netty.handler.codec.http.HttpResponse) JsonObject(com.google.gson.JsonObject) TokenState(co.cask.cdap.security.auth.TokenState) ChannelBuffer(org.jboss.netty.buffer.ChannelBuffer) JsonArray(com.google.gson.JsonArray) AccessTokenTransformer(co.cask.cdap.security.auth.AccessTokenTransformer) AuditLogContent(co.cask.cdap.common.logging.AuditLogContent) DefaultHttpResponse(org.jboss.netty.handler.codec.http.DefaultHttpResponse) HttpMethod(org.jboss.netty.handler.codec.http.HttpMethod)

Example 2 with TokenState

use of co.cask.cdap.security.auth.TokenState in project cdap by caskdata.

the class AuthenticationHandler method validateAccessToken.

/**
 * Validates the access token in authorization header.
 *
 * @param request the http request. The request headers will be modified if the validation succeeded to carry
 *                user information extracted from the token.
 * @return the {@link TokenState} indicating the result of the validation.
 */
private TokenState validateAccessToken(HttpRequest request, Channel channel) {
    String auth = request.headers().get(HttpHeaderNames.AUTHORIZATION);
    /*
     * Parse the access token from authorization header.  The header will be in the form:
     *     Authorization: Bearer TOKEN
     *
     * where TOKEN is the base64 encoded serialized AccessToken instance.
     */
    String accessToken = null;
    if (auth != null) {
        int idx = auth.trim().indexOf(' ');
        if (idx < 0) {
            return TokenState.MISSING;
        }
        accessToken = auth.substring(idx + 1).trim();
    }
    TokenState state = tokenValidator.validate(accessToken);
    if (state.isValid()) {
        try {
            AccessTokenTransformer.AccessTokenIdentifierPair tokenPair = tokenTransformer.transform(accessToken);
            // Update message header
            request.headers().set(HttpHeaderNames.AUTHORIZATION, "CDAP-verified " + tokenPair.getAccessTokenIdentifierStr());
            request.headers().set(Constants.Security.Headers.USER_ID, tokenPair.getAccessTokenIdentifierObj().getUsername());
            String clientIP = Networks.getIP(channel.remoteAddress());
            if (clientIP != null) {
                request.headers().set(Constants.Security.Headers.USER_IP, clientIP);
            }
        } catch (Exception e) {
            // This shouldn't happen in normal case, since the token is already validated
            LOG.debug("Exception raised when getting token information from a validate token", e);
            return TokenState.INVALID;
        }
    }
    return state;
}
Also used : AccessTokenTransformer(co.cask.cdap.security.auth.AccessTokenTransformer) TokenState(co.cask.cdap.security.auth.TokenState) TimeoutException(java.util.concurrent.TimeoutException) PatternSyntaxException(java.util.regex.PatternSyntaxException)

Example 3 with TokenState

use of co.cask.cdap.security.auth.TokenState in project cdap by caskdata.

the class AuthenticationHandler method channelRead.

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    if (!(msg instanceof HttpRequest)) {
        ctx.fireChannelRead(msg);
        return;
    }
    HttpRequest request = (HttpRequest) msg;
    // Pass if security is bypassed or it has valid access token, process to the next handler
    if (isBypassed(request)) {
        ctx.fireChannelRead(msg);
        return;
    }
    TokenState tokenState = validateAccessToken(request, ctx.channel());
    if (tokenState.isValid()) {
        ctx.fireChannelRead(msg);
        return;
    }
    // Response with failure, plus optionally audit log
    try {
        HttpHeaders headers = new DefaultHttpHeaders();
        JsonObject jsonObject = new JsonObject();
        if (tokenState == TokenState.MISSING) {
            headers.add(HttpHeaderNames.WWW_AUTHENTICATE, String.format("Bearer realm=\"%s\"", realm));
            LOG.debug("Authentication failed due to missing token");
        } else {
            headers.add(HttpHeaderNames.WWW_AUTHENTICATE, String.format("Bearer realm=\"%s\" error=\"invalid_token\"" + " error_description=\"%s\"", realm, tokenState.getMsg()));
            jsonObject.addProperty("error", "invalid_token");
            jsonObject.addProperty("error_description", tokenState.getMsg());
            LOG.debug("Authentication failed due to invalid token, reason={};", tokenState);
        }
        jsonObject.add("auth_uri", getAuthenticationURLs());
        ByteBuf content = Unpooled.copiedBuffer(jsonObject.toString(), StandardCharsets.UTF_8);
        HttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.UNAUTHORIZED, content);
        HttpUtil.setContentLength(response, content.readableBytes());
        HttpUtil.setKeepAlive(response, false);
        response.headers().setAll(headers);
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json;charset=UTF-8");
        auditLogIfNeeded(request, response, ctx.channel());
        ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
    } finally {
        ReferenceCountUtil.release(msg);
    }
}
Also used : HttpRequest(io.netty.handler.codec.http.HttpRequest) HttpHeaders(io.netty.handler.codec.http.HttpHeaders) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) DefaultHttpHeaders(io.netty.handler.codec.http.DefaultHttpHeaders) JsonObject(com.google.gson.JsonObject) DefaultFullHttpResponse(io.netty.handler.codec.http.DefaultFullHttpResponse) HttpResponse(io.netty.handler.codec.http.HttpResponse) ByteBuf(io.netty.buffer.ByteBuf) TokenState(co.cask.cdap.security.auth.TokenState)

Aggregations

TokenState (co.cask.cdap.security.auth.TokenState)3 AccessTokenTransformer (co.cask.cdap.security.auth.AccessTokenTransformer)2 JsonObject (com.google.gson.JsonObject)2 AuditLogContent (co.cask.cdap.common.logging.AuditLogContent)1 JsonArray (com.google.gson.JsonArray)1 ByteBuf (io.netty.buffer.ByteBuf)1 DefaultFullHttpResponse (io.netty.handler.codec.http.DefaultFullHttpResponse)1 DefaultHttpHeaders (io.netty.handler.codec.http.DefaultHttpHeaders)1 HttpHeaders (io.netty.handler.codec.http.HttpHeaders)1 HttpRequest (io.netty.handler.codec.http.HttpRequest)1 HttpResponse (io.netty.handler.codec.http.HttpResponse)1 InetSocketAddress (java.net.InetSocketAddress)1 HashMap (java.util.HashMap)1 TimeoutException (java.util.concurrent.TimeoutException)1 PatternSyntaxException (java.util.regex.PatternSyntaxException)1 ChannelBuffer (org.jboss.netty.buffer.ChannelBuffer)1 ChannelFuture (org.jboss.netty.channel.ChannelFuture)1 DefaultHttpResponse (org.jboss.netty.handler.codec.http.DefaultHttpResponse)1 HttpMethod (org.jboss.netty.handler.codec.http.HttpMethod)1 HttpResponse (org.jboss.netty.handler.codec.http.HttpResponse)1