Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,12 @@ public void postFilters(List<Filter> postFilters)
// Emit nothing by default.
}

@Override
public void identity(String identity)
{
// Emit nothing by default.
}

@Override
public BitmapResultFactory<?> makeBitmapResultFactory(BitmapFactory factory)
{
Expand Down
5 changes: 5 additions & 0 deletions processing/src/main/java/io/druid/query/QueryMetrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,11 @@ public interface QueryMetrics<QueryType extends Query<?>>

void postFilters(List<Filter> postFilters);

/**
* Sets identity of the requester for a query. See {@code AuthenticationResult}.
*/
void identity(String identity);

/**
* Creates a {@link BitmapResultFactory} which may record some information along bitmap construction from {@link
* #preFilters(List)}. The returned BitmapResultFactory may add some dimensions to this QueryMetrics from it's {@link
Expand Down
87 changes: 47 additions & 40 deletions server/src/main/java/io/druid/server/QueryLifecycle.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@
import io.druid.server.log.RequestLogger;
import io.druid.server.security.Access;
import io.druid.server.security.AuthenticationResult;
import io.druid.server.security.AuthorizerMapper;
import io.druid.server.security.AuthorizationUtils;
import io.druid.server.security.AuthorizerMapper;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -80,6 +80,7 @@ public class QueryLifecycle
private final long startNs;

private State state = State.NEW;
private AuthenticationResult authenticationResult;
private QueryToolChest toolChest;
private QueryPlus queryPlus;

Expand Down Expand Up @@ -111,9 +112,9 @@ public QueryLifecycle(
* is unauthorized, an IllegalStateException will be thrown. Logs and metrics are emitted when the Sequence is
* either fully iterated or throws an exception.
*
* @param query the query
* @param authenticationResult authentication result indicating identity of the requester
* @param remoteAddress remote address, for logging; or null if unknown
* @param query the query
* @param authenticationResult authentication result indicating identity of the requester
* @param remoteAddress remote address, for logging; or null if unknown
*
* @return results
*/
Expand Down Expand Up @@ -185,63 +186,65 @@ public void initialize(final Query baseQuery)
* @param authenticationResult authentication result indicating the identity of the requester
*
* @return authorization result
*
* */
public Access authorize(
final AuthenticationResult authenticationResult
)
*/
public Access authorize(final AuthenticationResult authenticationResult)
{
transition(State.INITIALIZED, State.AUTHORIZING);
Access authResult = AuthorizationUtils.authorizeAllResourceActions(
return doAuthorize(
authenticationResult,
Iterables.transform(
queryPlus.getQuery().getDataSource().getNames(),
AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR
),
authorizerMapper
AuthorizationUtils.authorizeAllResourceActions(
authenticationResult,
Iterables.transform(
queryPlus.getQuery().getDataSource().getNames(),
AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR
),
authorizerMapper
)
);

if (!authResult.isAllowed()) {
// Not authorized; go straight to Jail, do not pass Go.
transition(State.AUTHORIZING, State.DONE);
} else {
transition(State.AUTHORIZING, State.AUTHORIZED);
}
return authResult;
}

/**
* Authorize the query. Will return an Access object denoting whether the query is authorized or not.
*
* @param token authentication token from the request
* @param namespace namespace of the authentication token
* @param req HTTP request object of the request. If provided, the auth-related fields in the HTTP request
* will be automatically set.
*
* @return authorization result
*
* */
public Access authorize(
@Nullable HttpServletRequest req
)
*/
public Access authorize(HttpServletRequest req)
{
transition(State.INITIALIZED, State.AUTHORIZING);
Access authResult = AuthorizationUtils.authorizeAllResourceActions(
req,
Iterables.transform(
queryPlus.getQuery().getDataSource().getNames(),
AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR
),
authorizerMapper
return doAuthorize(
AuthorizationUtils.authenticationResultFromRequest(req),
AuthorizationUtils.authorizeAllResourceActions(
req,
Iterables.transform(
queryPlus.getQuery().getDataSource().getNames(),
AuthorizationUtils.DATASOURCE_READ_RA_GENERATOR
),
authorizerMapper
)
);
}

if (!authResult.isAllowed()) {
private Access doAuthorize(final AuthenticationResult authenticationResult, final Access authorizationResult)
{
if (!authorizationResult.isAllowed()) {
// Not authorized; go straight to Jail, do not pass Go.
transition(State.AUTHORIZING, State.DONE);
transition(State.AUTHORIZING, State.UNAUTHORIZED);
} else {
transition(State.AUTHORIZING, State.AUTHORIZED);
}
return authResult;

this.authenticationResult = authenticationResult;

final QueryMetrics queryMetrics = queryPlus.getQueryMetrics();

if (queryMetrics != null) {
queryMetrics.identity(authenticationResult.getIdentity());
}

return authorizationResult;
}

/**
Expand Down Expand Up @@ -314,6 +317,9 @@ public void emitLogsAndMetrics(
statsMap.put("query/time", TimeUnit.NANOSECONDS.toMillis(queryTimeNs));
statsMap.put("query/bytes", bytesWritten);
statsMap.put("success", success);
if (authenticationResult != null) {
statsMap.put("identity", authenticationResult.getIdentity());
}
if (e != null) {
statsMap.put("exception", e.toString());

Expand Down Expand Up @@ -360,6 +366,7 @@ enum State
AUTHORIZING,
AUTHORIZED,
EXECUTING,
UNAUTHORIZED,
DONE
}

Expand Down
2 changes: 1 addition & 1 deletion server/src/main/java/io/druid/server/QueryResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public QueryResource(
@DELETE
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getServer(@PathParam("id") String queryId, @Context final HttpServletRequest req)
public Response cancelQuery(@PathParam("id") String queryId, @Context final HttpServletRequest req)
{
if (log.isDebugEnabled()) {
log.debug("Received cancel request for query [%s]", queryId);
Expand Down
26 changes: 26 additions & 0 deletions server/src/main/java/io/druid/server/QueryStats.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.fasterxml.jackson.annotation.JsonValue;

import java.util.Map;
import java.util.Objects;

/**
*/
Expand All @@ -39,4 +40,29 @@ public Map<String, Object> getStats()
{
return stats;
}

@Override
public boolean equals(final Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final QueryStats that = (QueryStats) o;
return Objects.equals(stats, that.stats);
}

@Override
public int hashCode()
{
return Objects.hash(stats);
}

@Override
public String toString()
{
return String.valueOf(stats);
}
}
34 changes: 34 additions & 0 deletions server/src/main/java/io/druid/server/RequestLogLine.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.joda.time.DateTime;

import java.util.Arrays;
import java.util.Objects;

public class RequestLogLine
{
Expand Down Expand Up @@ -80,4 +81,37 @@ public QueryStats getQueryStats()
{
return queryStats;
}

@Override
public boolean equals(final Object o)
{
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
final RequestLogLine that = (RequestLogLine) o;
return Objects.equals(timestamp, that.timestamp) &&
Objects.equals(remoteAddr, that.remoteAddr) &&
Objects.equals(query, that.query) &&
Objects.equals(queryStats, that.queryStats);
}

@Override
public int hashCode()
{
return Objects.hash(timestamp, remoteAddr, query, queryStats);
}

@Override
public String toString()
{
return "RequestLogLine{" +
"timestamp=" + timestamp +
", remoteAddr='" + remoteAddr + '\'' +
", query=" + query +
", queryStats=" + queryStats +
'}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ public class AuthorizationUtils
*
* If this attribute is already set when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param resourceAction A resource identifier and the action to be taken the resource.
* @param request HTTP request to be authorized
* @param resourceAction A resource identifier and the action to be taken the resource.
* @param authorizerMapper The singleton AuthorizerMapper instance
*
* @return ACCESS_OK or the failed Access object returned by the Authorizer that checked the request.
*/
public static Access authorizeResourceAction(
Expand All @@ -62,6 +63,28 @@ public static Access authorizeResourceAction(
);
}

/**
* Returns the authentication information for a request.
*
* @param request http request
*
* @return authentication result
*
* @throws IllegalStateException if the request was not authenticated
*/
public static AuthenticationResult authenticationResultFromRequest(final HttpServletRequest request)
{
final AuthenticationResult authenticationResult = (AuthenticationResult) request.getAttribute(
AuthConfig.DRUID_AUTHENTICATION_RESULT
);

if (authenticationResult == null) {
throw new ISE("Null authentication result");
}

return authenticationResult;
}

/**
* Check a list of resource-actions to be performed by the identity represented by authenticationResult.
*
Expand All @@ -71,7 +94,8 @@ public static Access authorizeResourceAction(
* Otherwise, return ACCESS_OK if all resource-actions were successfully authorized.
*
* @param authenticationResult Authentication result representing identity of requester
* @param resourceActions An Iterable of resource-actions to authorize
* @param resourceActions An Iterable of resource-actions to authorize
*
* @return ACCESS_OK or the Access object from the first failed check
*/
public static Access authorizeAllResourceActions(
Expand Down Expand Up @@ -119,8 +143,9 @@ public static Access authorizeAllResourceActions(
*
* If this attribute is already set when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param request HTTP request to be authorized
* @param resourceActions An Iterable of resource-actions to authorize
*
* @return ACCESS_OK or the Access object from the first failed check
*/
public static Access authorizeAllResourceActions(
Expand All @@ -133,15 +158,8 @@ public static Access authorizeAllResourceActions(
throw new ISE("Request already had authorization check.");
}

final AuthenticationResult authenticationResult = (AuthenticationResult) request.getAttribute(
AuthConfig.DRUID_AUTHENTICATION_RESULT
);
if (authenticationResult == null) {
throw new ISE("Null authentication result");
}

Access access = authorizeAllResourceActions(
authenticationResult,
authenticationResultFromRequest(request),
resourceActions,
authorizerMapper
);
Expand All @@ -168,12 +186,12 @@ public static Access authorizeAllResourceActions(
*
* If this attribute is already set when this function is called, an exception is thrown.
*
* @param request HTTP request to be authorized
* @param resources resources to be processed into resource-actions
* @param request HTTP request to be authorized
* @param resources resources to be processed into resource-actions
* @param resourceActionGenerator Function that creates an iterable of resource-actions from a resource
* @param authorizerMapper authorizer mapper
* @return Iterable containing resources that were authorized
* @param authorizerMapper authorizer mapper
*
* @return Iterable containing resources that were authorized
*/
public static <ResType> Iterable<ResType> filterAuthorizedResources(
final HttpServletRequest request,
Expand All @@ -186,12 +204,7 @@ public static <ResType> Iterable<ResType> filterAuthorizedResources(
throw new ISE("Request already had authorization check.");
}

final AuthenticationResult authenticationResult = (AuthenticationResult) request.getAttribute(
AuthConfig.DRUID_AUTHENTICATION_RESULT
);
if (authenticationResult == null) {
throw new ISE("Null authentication result");
}
final AuthenticationResult authenticationResult = authenticationResultFromRequest(request);

final Authorizer authorizer = authorizerMapper.getAuthorizer(authenticationResult.getAuthorizerName());
if (authorizer == null) {
Expand Down Expand Up @@ -231,7 +244,6 @@ public static <ResType> Iterable<ResType> filterAuthorizedResources(
}



/**
* Function for the common pattern of generating a resource-action for reading from a datasource, using the
* datasource name.
Expand Down
Loading