Skip to content
This repository has been archived by the owner on Jun 7, 2024. It is now read-only.

Commit

Permalink
Merge pull request #702 from zalando/aruha-929-authorization-refactoring
Browse files Browse the repository at this point in the history
Aruha 929 authorization refactoring
  • Loading branch information
lmontrieux authored Jul 25, 2017
2 parents 519e8e9 + 1f86c27 commit 29d8360
Show file tree
Hide file tree
Showing 41 changed files with 365 additions and 545 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/).

## [Unreleased]

## [1.1.0] - 2017-07-25

### Added
- The Nakadi manual merged to the project docs.
- The template added to generate Nakadi website using github pages.
- Addition of a new authentication mode, 'REALM'

### Changed
- The metrics endpoint documentation key "summary" changed to "description" in Open API file.
- Event type authorization refactoring

### Removed
- Removed unused feature toggle CHECK_APPLICATION_LEVEL_PERMISSIONS
for authorization based on owning_application.

### Fixed
- Fixed formatting of CursorDistanceResult in Open API file.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ dependencies {
compile "io.dropwizard.metrics:metrics-servlets:$dropwizardVersion"
compile "io.dropwizard.metrics:metrics-jvm:$dropwizardVersion"
compile 'org.apache.commons:commons-lang3:3.5'
compile 'org.zalando:nakadi-plugin-api:1.1.0'
compile 'org.zalando:nakadi-plugin-api:2.0.0'
compile 'org.echocat.jomon:runtime:1.6.3'

// kafka & zookeeper
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package org.zalando.nakadi.controller;

import com.google.common.base.Charsets;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.ws.rs.core.Response;
import org.json.JSONException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import static org.springframework.http.ResponseEntity.status;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.NativeWebRequest;
import org.zalando.nakadi.domain.EventPublishResult;
import org.zalando.nakadi.domain.EventPublishingStatus;
import org.zalando.nakadi.exceptions.NakadiException;
import org.zalando.nakadi.exceptions.NoSuchEventTypeException;
import org.zalando.nakadi.exceptions.ResourceAccessNotAuthorizedException;
import org.zalando.nakadi.exceptions.runtime.AccessDeniedException;
import org.zalando.nakadi.exceptions.runtime.ServiceTemporarilyUnavailableException;
import org.zalando.nakadi.metrics.EventTypeMetricRegistry;
import org.zalando.nakadi.metrics.EventTypeMetrics;
Expand All @@ -32,6 +26,13 @@
import org.zalando.problem.Problem;
import org.zalando.problem.ThrowableProblem;
import org.zalando.problem.spring.web.advice.Responses;

import javax.ws.rs.core.Response;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static org.springframework.http.ResponseEntity.status;
import static org.springframework.web.bind.annotation.RequestMethod.POST;
import static org.zalando.problem.spring.web.advice.Responses.create;

@RestController
Expand All @@ -56,7 +57,7 @@ public EventPublishingController(final EventPublisher publisher,
public ResponseEntity postEvent(@PathVariable final String eventTypeName,
@RequestBody final String eventsAsString,
final NativeWebRequest request,
final Client client) throws ResourceAccessNotAuthorizedException {
final Client client) throws AccessDeniedException {
LOG.trace("Received event {} for event type {}", eventsAsString, eventTypeName);
final EventTypeMetrics eventTypeMetrics = eventTypeMetricRegistry.metricsFor(eventTypeName);

Expand All @@ -76,23 +77,12 @@ public ResponseEntity postEvent(@PathVariable final String eventTypeName,
}
}

@ExceptionHandler(ResourceAccessNotAuthorizedException.class)
public ResponseEntity<Problem> processUnauthorizedAccess(final ResourceAccessNotAuthorizedException e,
final NativeWebRequest request) {
return Responses.create(
Problem.valueOf(
Response.Status.FORBIDDEN,
"Operation " + e.getOperation() + " is not allowed for resource " + e.getResource()),
request);
}


private ResponseEntity postEventInternal(final String eventTypeName,
final String eventsAsString,
final NativeWebRequest nativeWebRequest,
final EventTypeMetrics eventTypeMetrics,
final Client client)
throws ResourceAccessNotAuthorizedException, ServiceTemporarilyUnavailableException {
throws AccessDeniedException, ServiceTemporarilyUnavailableException {
final long startingNanos = System.nanoTime();
try {
final EventPublishResult result = publisher.publish(eventsAsString, eventTypeName, client);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import org.zalando.nakadi.service.EventStreamFactory;
import org.zalando.nakadi.service.EventTypeChangeListener;
import org.zalando.nakadi.service.timeline.TimelineService;
import org.zalando.nakadi.util.AuthorizationUtils;
import org.zalando.nakadi.util.FeatureToggleService;
import org.zalando.nakadi.view.Cursor;
import org.zalando.problem.Problem;
Expand Down Expand Up @@ -287,7 +286,7 @@ public StreamingResponseBody streamEvents(
// TODO: deprecate and remove previous authorization strategy
writeProblemResponse(response, outputStream, FORBIDDEN, e.getMessage());
} catch (final AccessDeniedException e) {
writeProblemResponse(response, outputStream, FORBIDDEN, AuthorizationUtils.errorMessage(e));
writeProblemResponse(response, outputStream, FORBIDDEN, e.explain());
} catch (final Exception e) {
LOG.error("Error while trying to stream events. Respond with INTERNAL_SERVER_ERROR.", e);
writeProblemResponse(response, outputStream, INTERNAL_SERVER_ERROR, e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@
import org.zalando.nakadi.domain.EventTypeBase;
import org.zalando.nakadi.domain.EventTypeOptions;
import org.zalando.nakadi.exceptions.ConflictException;
import org.zalando.nakadi.exceptions.ForbiddenAccessException;
import org.zalando.nakadi.exceptions.NakadiRuntimeException;
import org.zalando.nakadi.exceptions.UnableProcessException;
import org.zalando.nakadi.exceptions.runtime.AccessDeniedException;
import org.zalando.nakadi.exceptions.runtime.EventTypeDeletionException;
import org.zalando.nakadi.exceptions.runtime.EventTypeUnavailableException;
import org.zalando.nakadi.exceptions.runtime.InconsistentStateException;
Expand Down Expand Up @@ -128,15 +128,15 @@ public ResponseEntity<?> delete(@PathVariable("name") final String eventTypeName
final NativeWebRequest request,
final Client client)
throws EventTypeDeletionException,
ForbiddenAccessException,
AccessDeniedException,
NoEventTypeException,
ConflictException,
ServiceTemporarilyUnavailableException {
if (featureToggleService.isFeatureEnabled(DISABLE_EVENT_TYPE_DELETION) && !isAdmin(client)) {
return new ResponseEntity<>(HttpStatus.FORBIDDEN);
}

eventTypeService.delete(eventTypeName, client);
eventTypeService.delete(eventTypeName);

return status(HttpStatus.OK).build();
}
Expand Down Expand Up @@ -191,13 +191,6 @@ public ResponseEntity<Problem> conflict(final ConflictException exception, final
return Responses.create(Response.Status.CONFLICT, exception.getMessage(), request);
}

@ExceptionHandler(ForbiddenAccessException.class)
public ResponseEntity<Problem> forbiddenAccess(final ForbiddenAccessException exception,
final NativeWebRequest request) {
LOG.debug(exception.getMessage(), exception);
return Responses.create(Response.Status.FORBIDDEN, exception.getMessage(), request);
}

@ExceptionHandler(NoEventTypeException.class)
public ResponseEntity<Problem> noEventType(final NoEventTypeException exception,
final NativeWebRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@

import javax.ws.rs.core.Response;

import static org.zalando.nakadi.util.AuthorizationUtils.errorMessage;
import static org.zalando.problem.MoreStatus.UNPROCESSABLE_ENTITY;


Expand Down Expand Up @@ -82,7 +81,7 @@ public ResponseEntity<Problem> noEventTypeException(final NoEventTypeException e
@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<Problem> accessDeniedException(final AccessDeniedException exception,
final NativeWebRequest request) {
return Responses.create(Response.Status.FORBIDDEN, errorMessage(exception), request);
return Responses.create(Response.Status.FORBIDDEN, exception.explain(), request);
}

@ExceptionHandler(IllegalScopeException.class)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
package org.zalando.nakadi.controller;

import java.util.Set;
import javax.annotation.Nullable;
import static javax.ws.rs.core.Response.Status.NOT_IMPLEMENTED;
import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -22,14 +18,19 @@
import org.zalando.nakadi.exceptions.runtime.FeatureNotAvailableException;
import org.zalando.nakadi.exceptions.runtime.InconsistentStateException;
import org.zalando.nakadi.exceptions.runtime.ServiceTemporarilyUnavailableException;
import org.zalando.nakadi.security.Client;
import org.zalando.nakadi.service.WebResult;
import org.zalando.nakadi.service.subscription.SubscriptionService;
import org.zalando.nakadi.util.FeatureToggleService;
import static org.zalando.nakadi.util.FeatureToggleService.Feature.HIGH_LEVEL_API;
import org.zalando.problem.Problem;
import org.zalando.problem.spring.web.advice.Responses;

import javax.annotation.Nullable;
import java.util.Set;

import static javax.ws.rs.core.Response.Status.NOT_IMPLEMENTED;
import static javax.ws.rs.core.Response.Status.SERVICE_UNAVAILABLE;
import static org.zalando.nakadi.util.FeatureToggleService.Feature.HIGH_LEVEL_API;


@RestController
@RequestMapping(value = "/subscriptions")
Expand Down Expand Up @@ -70,10 +71,10 @@ public ResponseEntity<?> getSubscription(@PathVariable("id") final String subscr

@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> deleteSubscription(@PathVariable("id") final String subscriptionId,
final NativeWebRequest request, final Client client) {
final NativeWebRequest request) {
featureToggleService.checkFeatureOn(HIGH_LEVEL_API);

return WebResult.wrap(() -> subscriptionService.deleteSubscription(subscriptionId, client), request,
return WebResult.wrap(() -> subscriptionService.deleteSubscription(subscriptionId), request,
HttpStatus.NO_CONTENT);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
import com.codahale.metrics.Counter;
import com.codahale.metrics.MetricRegistry;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
Expand All @@ -25,7 +18,6 @@
import org.zalando.nakadi.domain.Subscription;
import org.zalando.nakadi.exceptions.NakadiException;
import org.zalando.nakadi.exceptions.runtime.AccessDeniedException;
import static org.zalando.nakadi.metrics.MetricUtils.metricNameForSubscription;
import org.zalando.nakadi.repository.db.SubscriptionDbRepository;
import org.zalando.nakadi.security.Client;
import org.zalando.nakadi.service.BlacklistService;
Expand All @@ -34,11 +26,20 @@
import org.zalando.nakadi.service.subscription.SubscriptionOutput;
import org.zalando.nakadi.service.subscription.SubscriptionStreamer;
import org.zalando.nakadi.service.subscription.SubscriptionStreamerFactory;
import static org.zalando.nakadi.util.AuthorizationUtils.errorMessage;
import org.zalando.nakadi.util.FeatureToggleService;
import static org.zalando.nakadi.util.FeatureToggleService.Feature.HIGH_LEVEL_API;
import org.zalando.problem.Problem;

import javax.annotation.Nullable;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicBoolean;

import static org.zalando.nakadi.metrics.MetricUtils.metricNameForSubscription;
import static org.zalando.nakadi.util.FeatureToggleService.Feature.HIGH_LEVEL_API;

@RestController
public class SubscriptionStreamController {
public static final String CONSUMERS_COUNT_METRIC_NAME = "consumers";
Expand Down Expand Up @@ -102,7 +103,7 @@ public void onException(final Exception ex) {
try {
if (ex instanceof AccessDeniedException) {
writeProblemResponse(response, out, Problem.valueOf(Response.Status.FORBIDDEN,
errorMessage((AccessDeniedException) ex)));
((AccessDeniedException) ex).explain()));
}
if (ex instanceof NakadiException) {
writeProblemResponse(response, out, ((NakadiException) ex).asProblem());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.NativeWebRequest;
import org.zalando.nakadi.exceptions.ConflictException;
import org.zalando.nakadi.exceptions.ForbiddenAccessException;
import org.zalando.nakadi.exceptions.NotFoundException;
import org.zalando.nakadi.exceptions.TimelineException;
import org.zalando.nakadi.exceptions.runtime.TopicRepositoryException;
import org.zalando.nakadi.exceptions.UnableProcessException;
import org.zalando.nakadi.exceptions.runtime.AccessDeniedException;
import org.zalando.nakadi.exceptions.runtime.InconsistentStateException;
import org.zalando.nakadi.exceptions.runtime.RepositoryProblemException;
import org.zalando.nakadi.exceptions.runtime.TopicRepositoryException;
import org.zalando.nakadi.security.Client;
import org.zalando.nakadi.service.timeline.TimelineService;
import org.zalando.nakadi.view.TimelineRequest;
Expand Down Expand Up @@ -48,7 +48,7 @@ public TimelinesController(final TimelineService timelineService) {
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> createTimeline(@PathVariable("name") final String eventTypeName,
@RequestBody final TimelineRequest timelineRequest, final Client client)
throws ForbiddenAccessException, TimelineException, TopicRepositoryException, InconsistentStateException,
throws AccessDeniedException, TimelineException, TopicRepositoryException, InconsistentStateException,
RepositoryProblemException {
timelineService.createTimeline(eventTypeName, timelineRequest.getStorageId(), client);
return ResponseEntity.status(HttpStatus.CREATED).build();
Expand All @@ -68,12 +68,6 @@ public ResponseEntity<?> getTimelines(@PathVariable("name") final String eventTy
.collect(Collectors.toList()));
}

@ExceptionHandler(ForbiddenAccessException.class)
public ResponseEntity<Problem> forbidden(final ForbiddenAccessException ex, final NativeWebRequest request) {
LOG.error(ex.getMessage(), ex);
return Responses.create(Response.Status.FORBIDDEN, ex.getMessage(), request);
}

@ExceptionHandler(UnableProcessException.class)
public ResponseEntity<Problem> unprocessable(final UnableProcessException ex, final NativeWebRequest request) {
LOG.error(ex.getMessage(), ex);
Expand Down
32 changes: 19 additions & 13 deletions src/main/java/org/zalando/nakadi/domain/EventTypeResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,47 @@
import org.zalando.nakadi.plugin.api.authz.AuthorizationService;
import org.zalando.nakadi.plugin.api.authz.Resource;

import javax.annotation.Nullable;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class EventTypeResource implements Resource {

private final String name;
private final String type;
private final Map<AuthorizationService.Operation, List<AuthorizationAttribute>> attributes;
private final EventTypeAuthorization etAuthorization;

public EventTypeResource(final String name,
final String type,
final Map<AuthorizationService.Operation, List<AuthorizationAttribute>> attributes) {
public EventTypeResource(final String name, final EventTypeAuthorization etAuthorization) {
this.name = name;
this.type = type;
this.attributes = attributes;
this.etAuthorization = etAuthorization;
}

@Override
@Nullable
public String getName() {
return name;
}

@Override
@Nullable
public String getType() {
return type;
return "event-type";
}

@Override
public Optional<List<AuthorizationAttribute>> getAttributesForOperation(
final AuthorizationService.Operation operation) {
return Optional.ofNullable(attributes.get(operation));
switch (operation) {
case READ:
return Optional.of(etAuthorization.getReaders());
case WRITE:
return Optional.of(etAuthorization.getWriters());
case ADMIN:
return Optional.of(etAuthorization.getAdmins());
default:
throw new IllegalArgumentException("Operation " + operation + " is not supported");
}
}

@Override
public String toString() {
return "AuthorizedResource{event-type='" + name + "'}";
}

}

This file was deleted.

Loading

0 comments on commit 29d8360

Please sign in to comment.