Search in sources :

Example 11 with AuthorizationInfo

use of io.druid.server.security.AuthorizationInfo in project druid by druid-io.

the class DatasourcesResourceTest method testSecuredGetFullQueryableDataSources.

@Test
public void testSecuredGetFullQueryableDataSources() throws Exception {
    EasyMock.expect(server.getDataSources()).andReturn(ImmutableList.of(listDataSources.get(0), listDataSources.get(1))).atLeastOnce();
    EasyMock.expect(inventoryView.getInventory()).andReturn(ImmutableList.of(server)).atLeastOnce();
    EasyMock.expect(request.getAttribute(AuthConfig.DRUID_AUTH_TOKEN)).andReturn(new AuthorizationInfo() {

        @Override
        public Access isAuthorized(Resource resource, Action action) {
            if (resource.getName().equals("datasource1")) {
                return new Access(true);
            } else {
                return new Access(false);
            }
        }
    }).times(2);
    EasyMock.replay(inventoryView, server, request);
    DatasourcesResource datasourcesResource = new DatasourcesResource(inventoryView, null, null, new AuthConfig(true));
    Response response = datasourcesResource.getQueryableDataSources("full", null, request);
    Set<DruidDataSource> result = (Set<DruidDataSource>) response.getEntity();
    DruidDataSource[] resultantDruidDataSources = new DruidDataSource[result.size()];
    result.toArray(resultantDruidDataSources);
    Assert.assertEquals(200, response.getStatus());
    Assert.assertEquals(1, resultantDruidDataSources.length);
    Assert.assertArrayEquals(listDataSources.subList(0, 1).toArray(), resultantDruidDataSources);
    response = datasourcesResource.getQueryableDataSources(null, null, request);
    List<String> result1 = (List<String>) response.getEntity();
    Assert.assertEquals(200, response.getStatus());
    Assert.assertEquals(1, result1.size());
    Assert.assertTrue(result1.contains("datasource1"));
    EasyMock.verify(inventoryView, server, request);
}
Also used : Action(io.druid.server.security.Action) TreeSet(java.util.TreeSet) ImmutableSet(com.google.common.collect.ImmutableSet) Set(java.util.Set) Resource(io.druid.server.security.Resource) Access(io.druid.server.security.Access) AuthConfig(io.druid.server.security.AuthConfig) AuthorizationInfo(io.druid.server.security.AuthorizationInfo) DruidDataSource(io.druid.client.DruidDataSource) Response(javax.ws.rs.core.Response) ArrayList(java.util.ArrayList) ImmutableList(com.google.common.collect.ImmutableList) List(java.util.List) Test(org.junit.Test)

Example 12 with AuthorizationInfo

use of io.druid.server.security.AuthorizationInfo in project druid by druid-io.

the class QueryResource method getServer.

@DELETE
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response getServer(@PathParam("id") String queryId, @Context final HttpServletRequest req) {
    if (log.isDebugEnabled()) {
        log.debug("Received cancel request for query [%s]", queryId);
    }
    if (authConfig.isEnabled()) {
        // This is an experimental feature, see - https://github.com/druid-io/druid/pull/2424
        final AuthorizationInfo authorizationInfo = (AuthorizationInfo) req.getAttribute(AuthConfig.DRUID_AUTH_TOKEN);
        Preconditions.checkNotNull(authorizationInfo, "Security is enabled but no authorization info found in the request");
        Set<String> datasources = queryManager.getQueryDatasources(queryId);
        if (datasources == null) {
            log.warn("QueryId [%s] not registered with QueryManager, cannot cancel", queryId);
        } else {
            for (String dataSource : datasources) {
                Access authResult = authorizationInfo.isAuthorized(new Resource(dataSource, ResourceType.DATASOURCE), Action.WRITE);
                if (!authResult.isAllowed()) {
                    return Response.status(Response.Status.FORBIDDEN).header("Access-Check-Result", authResult).build();
                }
            }
        }
    }
    queryManager.cancelQuery(queryId);
    return Response.status(Response.Status.ACCEPTED).build();
}
Also used : Access(io.druid.server.security.Access) Resource(io.druid.server.security.Resource) AuthorizationInfo(io.druid.server.security.AuthorizationInfo) Path(javax.ws.rs.Path) DELETE(javax.ws.rs.DELETE) Produces(javax.ws.rs.Produces)

Example 13 with AuthorizationInfo

use of io.druid.server.security.AuthorizationInfo in project druid by druid-io.

the class QueryResource method doPost.

@POST
@Produces({ MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE })
@Consumes({ MediaType.APPLICATION_JSON, SmileMediaTypes.APPLICATION_JACKSON_SMILE, APPLICATION_SMILE })
public Response doPost(InputStream in, @QueryParam("pretty") String pretty, // used to get request content-type, remote address and AuthorizationInfo
@Context final HttpServletRequest req) throws IOException {
    final long start = System.currentTimeMillis();
    Query query = null;
    QueryToolChest toolChest = null;
    String queryId = null;
    final ResponseContext context = createContext(req.getContentType(), pretty != null);
    final String currThreadName = Thread.currentThread().getName();
    try {
        query = context.getObjectMapper().readValue(in, Query.class);
        queryId = query.getId();
        if (queryId == null) {
            queryId = UUID.randomUUID().toString();
            query = query.withId(queryId);
        }
        if (query.getContextValue(QueryContextKeys.TIMEOUT) == null) {
            query = query.withOverriddenContext(ImmutableMap.of(QueryContextKeys.TIMEOUT, config.getMaxIdleTime().toStandardDuration().getMillis()));
        }
        toolChest = warehouse.getToolChest(query);
        Thread.currentThread().setName(String.format("%s[%s_%s_%s]", currThreadName, query.getType(), query.getDataSource().getNames(), queryId));
        if (log.isDebugEnabled()) {
            log.debug("Got query [%s]", query);
        }
        if (authConfig.isEnabled()) {
            // This is an experimental feature, see - https://github.com/druid-io/druid/pull/2424
            AuthorizationInfo authorizationInfo = (AuthorizationInfo) req.getAttribute(AuthConfig.DRUID_AUTH_TOKEN);
            if (authorizationInfo != null) {
                for (String dataSource : query.getDataSource().getNames()) {
                    Access authResult = authorizationInfo.isAuthorized(new Resource(dataSource, ResourceType.DATASOURCE), Action.READ);
                    if (!authResult.isAllowed()) {
                        return Response.status(Response.Status.FORBIDDEN).header("Access-Check-Result", authResult).build();
                    }
                }
            } else {
                throw new ISE("WTF?! Security is enabled but no authorization info found in the request");
            }
        }
        String prevEtag = req.getHeader(HDR_IF_NONE_MATCH);
        if (prevEtag != null) {
            query = query.withOverriddenContext(ImmutableMap.of(HDR_IF_NONE_MATCH, prevEtag));
        }
        final Map<String, Object> responseContext = new MapMaker().makeMap();
        final Sequence res = query.run(texasRanger, responseContext);
        if (prevEtag != null && prevEtag.equals(responseContext.get(HDR_ETAG))) {
            return Response.notModified().build();
        }
        final Sequence results;
        if (res == null) {
            results = Sequences.empty();
        } else {
            results = res;
        }
        final Yielder yielder = Yielders.each(results);
        try {
            final Query theQuery = query;
            final QueryToolChest theToolChest = toolChest;
            final ObjectWriter jsonWriter = context.newOutputWriter();
            Response.ResponseBuilder builder = Response.ok(new StreamingOutput() {

                @Override
                public void write(OutputStream outputStream) throws IOException, WebApplicationException {
                    try {
                        // json serializer will always close the yielder
                        CountingOutputStream os = new CountingOutputStream(outputStream);
                        jsonWriter.writeValue(os, yielder);
                        // Some types of OutputStream suppress flush errors in the .close() method.
                        os.flush();
                        os.close();
                        successfulQueryCount.incrementAndGet();
                        final long queryTime = System.currentTimeMillis() - start;
                        emitter.emit(DruidMetrics.makeQueryTimeMetric(theToolChest, jsonMapper, theQuery, req.getRemoteAddr()).setDimension("success", "true").build("query/time", queryTime));
                        emitter.emit(DruidMetrics.makeQueryTimeMetric(theToolChest, jsonMapper, theQuery, req.getRemoteAddr()).build("query/bytes", os.getCount()));
                        requestLogger.log(new RequestLogLine(new DateTime(start), req.getRemoteAddr(), theQuery, new QueryStats(ImmutableMap.<String, Object>of("query/time", queryTime, "query/bytes", os.getCount(), "success", true))));
                    } finally {
                        Thread.currentThread().setName(currThreadName);
                    }
                }
            }, context.getContentType()).header("X-Druid-Query-Id", queryId);
            if (responseContext.get(HDR_ETAG) != null) {
                builder.header(HDR_ETAG, responseContext.get(HDR_ETAG));
                responseContext.remove(HDR_ETAG);
            }
            //Limit the response-context header, see https://github.com/druid-io/druid/issues/2331
            //Note that Response.ResponseBuilder.header(String key,Object value).build() calls value.toString()
            //and encodes the string using ASCII, so 1 char is = 1 byte
            String responseCtxString = jsonMapper.writeValueAsString(responseContext);
            if (responseCtxString.length() > RESPONSE_CTX_HEADER_LEN_LIMIT) {
                log.warn("Response Context truncated for id [%s] . Full context is [%s].", queryId, responseCtxString);
                responseCtxString = responseCtxString.substring(0, RESPONSE_CTX_HEADER_LEN_LIMIT);
            }
            return builder.header("X-Druid-Response-Context", responseCtxString).build();
        } catch (Exception e) {
            // make sure to close yielder if anything happened before starting to serialize the response.
            yielder.close();
            throw Throwables.propagate(e);
        } finally {
        // do not close yielder here, since we do not want to close the yielder prior to
        // StreamingOutput having iterated over all the results
        }
    } catch (QueryInterruptedException e) {
        try {
            log.warn(e, "Exception while processing queryId [%s]", queryId);
            interruptedQueryCount.incrementAndGet();
            final long queryTime = System.currentTimeMillis() - start;
            emitter.emit(DruidMetrics.makeQueryTimeMetric(toolChest, jsonMapper, query, req.getRemoteAddr()).setDimension("success", "false").build("query/time", queryTime));
            requestLogger.log(new RequestLogLine(new DateTime(start), req.getRemoteAddr(), query, new QueryStats(ImmutableMap.<String, Object>of("query/time", queryTime, "success", false, "interrupted", true, "reason", e.toString()))));
        } catch (Exception e2) {
            log.error(e2, "Unable to log query [%s]!", query);
        }
        return context.gotError(e);
    } catch (Exception e) {
        // Input stream has already been consumed by the json object mapper if query == null
        final String queryString = query == null ? "unparsable query" : query.toString();
        log.warn(e, "Exception occurred on request [%s]", queryString);
        failedQueryCount.incrementAndGet();
        try {
            final long queryTime = System.currentTimeMillis() - start;
            emitter.emit(DruidMetrics.makeQueryTimeMetric(toolChest, jsonMapper, query, req.getRemoteAddr()).setDimension("success", "false").build("query/time", queryTime));
            requestLogger.log(new RequestLogLine(new DateTime(start), req.getRemoteAddr(), query, new QueryStats(ImmutableMap.<String, Object>of("query/time", queryTime, "success", false, "exception", e.toString()))));
        } catch (Exception e2) {
            log.error(e2, "Unable to log query [%s]!", queryString);
        }
        log.makeAlert(e, "Exception handling request").addData("exception", e.toString()).addData("query", queryString).addData("peer", req.getRemoteAddr()).emit();
        return context.gotError(e);
    } finally {
        Thread.currentThread().setName(currThreadName);
    }
}
Also used : Query(io.druid.query.Query) CountingOutputStream(com.google.common.io.CountingOutputStream) OutputStream(java.io.OutputStream) Access(io.druid.server.security.Access) StreamingOutput(javax.ws.rs.core.StreamingOutput) QueryToolChest(io.druid.query.QueryToolChest) DateTime(org.joda.time.DateTime) CountingOutputStream(com.google.common.io.CountingOutputStream) ISE(io.druid.java.util.common.ISE) QueryInterruptedException(io.druid.query.QueryInterruptedException) Yielder(io.druid.java.util.common.guava.Yielder) Resource(io.druid.server.security.Resource) MapMaker(com.google.common.collect.MapMaker) ObjectWriter(com.fasterxml.jackson.databind.ObjectWriter) Sequence(io.druid.java.util.common.guava.Sequence) AuthorizationInfo(io.druid.server.security.AuthorizationInfo) WebApplicationException(javax.ws.rs.WebApplicationException) QueryInterruptedException(io.druid.query.QueryInterruptedException) IOException(java.io.IOException) Response(javax.ws.rs.core.Response) POST(javax.ws.rs.POST) Produces(javax.ws.rs.Produces) Consumes(javax.ws.rs.Consumes)

Example 14 with AuthorizationInfo

use of io.druid.server.security.AuthorizationInfo in project druid by druid-io.

the class IntervalsResource method getIntervals.

@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getIntervals(@Context final HttpServletRequest req) {
    final Comparator<Interval> comparator = Comparators.inverse(Comparators.intervalsByStartThenEnd());
    final Set<DruidDataSource> datasources = authConfig.isEnabled() ? InventoryViewUtils.getSecuredDataSources(serverInventoryView, (AuthorizationInfo) req.getAttribute(AuthConfig.DRUID_AUTH_TOKEN)) : InventoryViewUtils.getDataSources(serverInventoryView);
    final Map<Interval, Map<String, Map<String, Object>>> retVal = Maps.newTreeMap(comparator);
    for (DruidDataSource dataSource : datasources) {
        for (DataSegment dataSegment : dataSource.getSegments()) {
            Map<String, Map<String, Object>> interval = retVal.get(dataSegment.getInterval());
            if (interval == null) {
                Map<String, Map<String, Object>> tmp = Maps.newHashMap();
                retVal.put(dataSegment.getInterval(), tmp);
            }
            setProperties(retVal, dataSource, dataSegment);
        }
    }
    return Response.ok(retVal).build();
}
Also used : AuthorizationInfo(io.druid.server.security.AuthorizationInfo) DruidDataSource(io.druid.client.DruidDataSource) Map(java.util.Map) DataSegment(io.druid.timeline.DataSegment) Interval(org.joda.time.Interval) Produces(javax.ws.rs.Produces) GET(javax.ws.rs.GET)

Example 15 with AuthorizationInfo

use of io.druid.server.security.AuthorizationInfo in project druid by druid-io.

the class MetadataResource method getDatabaseDataSources.

@GET
@Path("/datasources")
@Produces(MediaType.APPLICATION_JSON)
public Response getDatabaseDataSources(@QueryParam("full") final String full, @QueryParam("includeDisabled") final String includeDisabled, @Context final HttpServletRequest req) {
    final Set<String> dataSourceNamesPreAuth;
    if (includeDisabled != null) {
        dataSourceNamesPreAuth = Sets.newTreeSet(metadataSegmentManager.getAllDatasourceNames());
    } else {
        dataSourceNamesPreAuth = Sets.newTreeSet(Iterables.transform(metadataSegmentManager.getInventory(), new Function<DruidDataSource, String>() {

            @Override
            public String apply(DruidDataSource input) {
                return input.getName();
            }
        }));
    }
    final Set<String> dataSourceNamesPostAuth;
    if (authConfig.isEnabled()) {
        // This is an experimental feature, see - https://github.com/druid-io/druid/pull/2424
        final Map<Pair<Resource, Action>, Access> resourceAccessMap = new HashMap<>();
        final AuthorizationInfo authorizationInfo = (AuthorizationInfo) req.getAttribute(AuthConfig.DRUID_AUTH_TOKEN);
        dataSourceNamesPostAuth = ImmutableSet.copyOf(Sets.filter(dataSourceNamesPreAuth, new Predicate<String>() {

            @Override
            public boolean apply(String input) {
                Resource resource = new Resource(input, ResourceType.DATASOURCE);
                Action action = Action.READ;
                Pair<Resource, Action> key = new Pair<>(resource, action);
                if (resourceAccessMap.containsKey(key)) {
                    return resourceAccessMap.get(key).isAllowed();
                } else {
                    Access access = authorizationInfo.isAuthorized(key.lhs, key.rhs);
                    resourceAccessMap.put(key, access);
                    return access.isAllowed();
                }
            }
        }));
    } else {
        dataSourceNamesPostAuth = dataSourceNamesPreAuth;
    }
    // Always use dataSourceNamesPostAuth to determine the set of returned dataSources
    if (full != null && includeDisabled == null) {
        return Response.ok().entity(Collections2.filter(metadataSegmentManager.getInventory(), new Predicate<DruidDataSource>() {

            @Override
            public boolean apply(DruidDataSource input) {
                return dataSourceNamesPostAuth.contains(input.getName());
            }
        })).build();
    } else {
        return Response.ok().entity(dataSourceNamesPostAuth).build();
    }
}
Also used : Action(io.druid.server.security.Action) HashMap(java.util.HashMap) Access(io.druid.server.security.Access) Resource(io.druid.server.security.Resource) DruidDataSource(io.druid.client.DruidDataSource) AuthorizationInfo(io.druid.server.security.AuthorizationInfo) Pair(io.druid.java.util.common.Pair) Path(javax.ws.rs.Path) Produces(javax.ws.rs.Produces) GET(javax.ws.rs.GET)

Aggregations

AuthorizationInfo (io.druid.server.security.AuthorizationInfo)20 Access (io.druid.server.security.Access)18 Resource (io.druid.server.security.Resource)18 Action (io.druid.server.security.Action)10 Produces (javax.ws.rs.Produces)9 WebApplicationException (javax.ws.rs.WebApplicationException)8 GET (javax.ws.rs.GET)6 Path (javax.ws.rs.Path)6 Response (javax.ws.rs.core.Response)6 Pair (io.druid.java.util.common.Pair)5 AuthConfig (io.druid.server.security.AuthConfig)5 HashMap (java.util.HashMap)5 DruidDataSource (io.druid.client.DruidDataSource)4 Test (org.junit.Test)4 Predicate (com.google.common.base.Predicate)3 Query (io.druid.query.Query)3 NoopRequestLogger (io.druid.server.log.NoopRequestLogger)3 NoopServiceEmitter (io.druid.server.metrics.NoopServiceEmitter)3 ByteArrayInputStream (java.io.ByteArrayInputStream)3 IOException (java.io.IOException)3