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;
}
}
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;
}
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);
}
}
Aggregations