use of org.apache.nifi.authorization.AccessDeniedException in project nifi by apache.
the class LuceneEventIndex method retrieveQuerySubmission.
@Override
public QuerySubmission retrieveQuerySubmission(final String queryIdentifier, final NiFiUser user) {
final QuerySubmission submission = querySubmissionMap.get(queryIdentifier);
final String userId = submission.getSubmitterIdentity();
if (user == null && userId == null) {
return submission;
}
if (user == null) {
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided");
}
if (userId == null || userId.equals(user.getIdentity())) {
return submission;
}
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request");
}
use of org.apache.nifi.authorization.AccessDeniedException in project nifi by apache.
the class VolatileProvenanceRepository method retrieveQuerySubmission.
@Override
public QuerySubmission retrieveQuerySubmission(final String queryIdentifier, final NiFiUser user) {
final QuerySubmission submission = querySubmissionMap.get(queryIdentifier);
final String userId = submission.getSubmitterIdentity();
if (user == null && userId == null) {
return submission;
}
if (user == null) {
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because no user id was provided in the provenance request.");
}
if (userId == null || userId.equals(user.getIdentity())) {
return submission;
}
throw new AccessDeniedException("Cannot retrieve Provenance Query Submission because " + user.getIdentity() + " is not the user who submitted the request.");
}
use of org.apache.nifi.authorization.AccessDeniedException in project nifi by apache.
the class Authorizable method authorize.
/**
* Authorizes the current user for the specified action on the specified resource. This method does imply the user is
* directly accessing the specified resource.
*
* @param authorizer authorizer
* @param action action
* @param user user
* @param resourceContext resource context
*/
default void authorize(Authorizer authorizer, RequestAction action, NiFiUser user, Map<String, String> resourceContext) throws AccessDeniedException {
if (user == null) {
throw new AccessDeniedException("Unknown user.");
}
final Map<String, String> userContext;
if (user.getClientAddress() != null && !user.getClientAddress().trim().isEmpty()) {
userContext = new HashMap<>();
userContext.put(UserContextKeys.CLIENT_ADDRESS.name(), user.getClientAddress());
} else {
userContext = null;
}
final Resource resource = getResource();
final Resource requestedResource = getRequestedResource();
final AuthorizationRequest request = new AuthorizationRequest.Builder().identity(user.getIdentity()).groups(user.getGroups()).anonymous(user.isAnonymous()).accessAttempt(true).action(action).resource(resource).requestedResource(requestedResource).resourceContext(resourceContext).userContext(userContext).explanationSupplier(() -> {
// build the safe explanation
final StringBuilder safeDescription = new StringBuilder("Unable to ");
if (RequestAction.READ.equals(action)) {
safeDescription.append("view ");
} else {
safeDescription.append("modify ");
}
safeDescription.append(resource.getSafeDescription()).append(".");
return safeDescription.toString();
}).build();
final AuthorizationResult result = authorizer.authorize(request);
if (Result.ResourceNotFound.equals(result.getResult())) {
final Authorizable parent = getParentAuthorizable();
if (parent == null) {
final AuthorizationResult failure = AuthorizationResult.denied("No applicable policies could be found.");
// audit authorization request
if (authorizer instanceof AuthorizationAuditor) {
((AuthorizationAuditor) authorizer).auditAccessAttempt(request, failure);
}
// denied
throw new AccessDeniedException(failure.getExplanation());
} else {
// create a custom authorizable to override the safe description but still defer to the parent authorizable
final Authorizable parentProxy = new Authorizable() {
@Override
public Authorizable getParentAuthorizable() {
return parent.getParentAuthorizable();
}
@Override
public Resource getRequestedResource() {
return requestedResource;
}
@Override
public Resource getResource() {
final Resource parentResource = parent.getResource();
return new Resource() {
@Override
public String getIdentifier() {
return parentResource.getIdentifier();
}
@Override
public String getName() {
return parentResource.getName();
}
@Override
public String getSafeDescription() {
return resource.getSafeDescription();
}
};
}
};
parentProxy.authorize(authorizer, action, user, resourceContext);
}
} else if (Result.Denied.equals(result.getResult())) {
throw new AccessDeniedException(result.getExplanation());
}
}
use of org.apache.nifi.authorization.AccessDeniedException in project nifi by apache.
the class ContentViewerController method doGet.
/**
* Gets the content and defers to registered viewers to generate the markup.
*
* @param request servlet request
* @param response servlet response
* @throws ServletException if a servlet-specific error occurs
* @throws IOException if an I/O error occurs
*/
@Override
protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {
// specify the charset in a response header
response.addHeader("Content-Type", "text/html; charset=UTF-8");
// get the content
final ServletContext servletContext = request.getServletContext();
final ContentAccess contentAccess = (ContentAccess) servletContext.getAttribute("nifi-content-access");
final ContentRequestContext contentRequest;
try {
contentRequest = getContentRequest(request);
} catch (final Exception e) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "Unable to interpret content request.");
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
}
if (contentRequest.getDataUri() == null) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "The data reference must be specified.");
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
}
// get the content
final DownloadableContent downloadableContent;
try {
downloadableContent = contentAccess.getContent(contentRequest);
} catch (final ResourceNotFoundException rnfe) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "Unable to find the specified content");
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
} catch (final AccessDeniedException ade) {
request.setAttribute("title", "Access Denied");
request.setAttribute("messages", "Unable to approve access to the specified content: " + ade.getMessage());
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
} catch (final Exception e) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "An unexpected error has occurred: " + e.getMessage());
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
}
// determine how we want to view the data
String mode = request.getParameter("mode");
// if the name isn't set, use original
if (mode == null) {
mode = DisplayMode.Original.name();
}
// determine the display mode
final DisplayMode displayMode;
try {
displayMode = DisplayMode.valueOf(mode);
} catch (final IllegalArgumentException iae) {
request.setAttribute("title", "Error");
request.setAttribute("messages", "Invalid display mode: " + mode);
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
}
// buffer the content to support resetting in case we need to detect the content type or char encoding
try (final BufferedInputStream bis = new BufferedInputStream(downloadableContent.getContent())) {
final String mimeType;
final String normalizedMimeType;
// when clustered and we don't know the type set to octet stream since the content was retrieved from the node's rest endpoint
if (downloadableContent.getType() == null || StringUtils.startsWithIgnoreCase(downloadableContent.getType(), MediaType.OCTET_STREAM.toString())) {
// attempt to detect the content stream if we don't know what it is ()
final DefaultDetector detector = new DefaultDetector();
// create the stream for tika to process, buffered to support reseting
final TikaInputStream tikaStream = TikaInputStream.get(bis);
// provide a hint based on the filename
final Metadata metadata = new Metadata();
metadata.set(Metadata.RESOURCE_NAME_KEY, downloadableContent.getFilename());
// Get mime type
final MediaType mediatype = detector.detect(tikaStream, metadata);
mimeType = mediatype.toString();
} else {
mimeType = downloadableContent.getType();
}
// Extract only mime type and subtype from content type (anything after the first ; are parameters)
// Lowercase so subsequent code does not need to implement case insensitivity
normalizedMimeType = mimeType.split(";", 2)[0].toLowerCase();
// add attributes needed for the header
request.setAttribute("filename", downloadableContent.getFilename());
request.setAttribute("contentType", mimeType);
// generate the header
request.getRequestDispatcher("/WEB-INF/jsp/header.jsp").include(request, response);
// remove the attributes needed for the header
request.removeAttribute("filename");
request.removeAttribute("contentType");
// generate the markup for the content based on the display mode
if (DisplayMode.Hex.equals(displayMode)) {
final byte[] buffer = new byte[BUFFER_LENGTH];
final int read = StreamUtils.fillBuffer(bis, buffer, false);
// trim the byte array if necessary
byte[] bytes = buffer;
if (read != buffer.length) {
bytes = new byte[read];
System.arraycopy(buffer, 0, bytes, 0, read);
}
// convert bytes into the base 64 bytes
final String base64 = Base64.encodeBase64String(bytes);
// defer to the jsp
request.setAttribute("content", base64);
request.getRequestDispatcher("/WEB-INF/jsp/hexview.jsp").include(request, response);
} else {
// lookup a viewer for the content
final String contentViewerUri = servletContext.getInitParameter(normalizedMimeType);
// handle no viewer for content type
if (contentViewerUri == null) {
request.getRequestDispatcher("/WEB-INF/jsp/no-viewer.jsp").include(request, response);
} else {
// create a request attribute for accessing the content
request.setAttribute(ViewableContent.CONTENT_REQUEST_ATTRIBUTE, new ViewableContent() {
@Override
public InputStream getContentStream() {
return bis;
}
@Override
public String getContent() throws IOException {
// detect the charset
final CharsetDetector detector = new CharsetDetector();
detector.setText(bis);
detector.enableInputFilter(true);
final CharsetMatch match = detector.detect();
// ensure we were able to detect the charset
if (match == null) {
throw new IOException("Unable to detect character encoding.");
}
// convert the stream using the detected charset
return IOUtils.toString(bis, match.getName());
}
@Override
public ViewableContent.DisplayMode getDisplayMode() {
return displayMode;
}
@Override
public String getFileName() {
return downloadableContent.getFilename();
}
@Override
public String getContentType() {
return normalizedMimeType;
}
@Override
public String getRawContentType() {
return mimeType;
}
});
try {
// generate the content
final ServletContext viewerContext = servletContext.getContext(contentViewerUri);
viewerContext.getRequestDispatcher("/view-content").include(request, response);
} catch (final Exception e) {
String message = e.getMessage() != null ? e.getMessage() : e.toString();
message = "Unable to generate view of data: " + message;
// log the error
logger.error(message);
if (logger.isDebugEnabled()) {
logger.error(StringUtils.EMPTY, e);
}
// populate the request attributes
request.setAttribute("title", "Error");
request.setAttribute("messages", message);
// forward to the error page
final ServletContext viewerContext = servletContext.getContext("/nifi");
viewerContext.getRequestDispatcher("/message").forward(request, response);
return;
}
// remove the request attribute
request.removeAttribute(ViewableContent.CONTENT_REQUEST_ATTRIBUTE);
}
}
// generate footer
request.getRequestDispatcher("/WEB-INF/jsp/footer.jsp").include(request, response);
}
}
use of org.apache.nifi.authorization.AccessDeniedException in project nifi by apache.
the class ThreadPoolRequestReplicator method performVerification.
private void performVerification(final Set<NodeIdentifier> nodeIds, final String method, final URI uri, final Object entity, final Map<String, String> headers, final StandardAsyncClusterResponse clusterResponse, final boolean merge, final Object monitor) {
logger.debug("Verifying that mutable request {} {} can be made", method, uri.getPath());
final Map<String, String> validationHeaders = new HashMap<>(headers);
validationHeaders.put(REQUEST_VALIDATION_HTTP_HEADER, NODE_CONTINUE);
final long startNanos = System.nanoTime();
final int numNodes = nodeIds.size();
final NodeRequestCompletionCallback completionCallback = new NodeRequestCompletionCallback() {
final Set<NodeResponse> nodeResponses = Collections.synchronizedSet(new HashSet<>());
@Override
public void onCompletion(final NodeResponse nodeResponse) {
// Add the node response to our collection. We later need to know whether or
// not this is the last node response, so we add the response and then check
// the size within a synchronized block to ensure that those two things happen
// atomically. Otherwise, we could have multiple threads checking the sizes of
// the sets at the same time, which could result in multiple threads performing
// the 'all nodes are complete' logic.
final boolean allNodesResponded;
synchronized (nodeResponses) {
nodeResponses.add(nodeResponse);
allNodesResponded = nodeResponses.size() == numNodes;
}
try {
final long nanos = System.nanoTime() - startNanos;
clusterResponse.addTiming("Completed Verification", nodeResponse.getNodeId().toString(), nanos);
// and if good replicate the original request to all of the nodes.
if (allNodesResponded) {
clusterResponse.addTiming("Verification Completed", "All Nodes", nanos);
// Check if we have any requests that do not have a 150-Continue status code.
final long dissentingCount = nodeResponses.stream().filter(p -> p.getStatus() != NODE_CONTINUE_STATUS_CODE).count();
// to all nodes and we are finished.
if (dissentingCount == 0) {
logger.debug("Received verification from all {} nodes that mutable request {} {} can be made", numNodes, method, uri.getPath());
replicate(nodeIds, method, uri, entity, headers, false, clusterResponse, true, merge, monitor);
return;
}
try {
final Map<String, String> cancelLockHeaders = new HashMap<>(headers);
cancelLockHeaders.put(REQUEST_TRANSACTION_CANCELATION_HTTP_HEADER, "true");
final Thread cancelLockThread = new Thread(new Runnable() {
@Override
public void run() {
logger.debug("Found {} dissenting nodes for {} {}; canceling claim request", dissentingCount, method, uri.getPath());
final Function<NodeIdentifier, NodeHttpRequest> requestFactory = nodeId -> new NodeHttpRequest(nodeId, method, createURI(uri, nodeId), entity, cancelLockHeaders, null, clusterResponse);
submitAsyncRequest(nodeIds, uri.getScheme(), uri.getPath(), requestFactory, cancelLockHeaders);
}
});
cancelLockThread.setName("Cancel Flow Locks");
cancelLockThread.start();
// Check that all nodes responded successfully.
for (final NodeResponse response : nodeResponses) {
if (response.getStatus() != NODE_CONTINUE_STATUS_CODE) {
final Response clientResponse = response.getClientResponse();
final String message;
if (clientResponse == null) {
message = "Node " + response.getNodeId() + " is unable to fulfill this request due to: Unexpected Response Code " + response.getStatus();
logger.info("Received a status of {} from {} for request {} {} when performing first stage of two-stage commit. The action will not occur", response.getStatus(), response.getNodeId(), method, uri.getPath());
} else {
final String nodeExplanation = clientResponse.readEntity(String.class);
message = "Node " + response.getNodeId() + " is unable to fulfill this request due to: " + nodeExplanation;
logger.info("Received a status of {} from {} for request {} {} when performing first stage of two-stage commit. " + "The action will not occur. Node explanation: {}", response.getStatus(), response.getNodeId(), method, uri.getPath(), nodeExplanation);
}
// if a node reports forbidden, use that as the response failure
final RuntimeException failure;
if (response.getStatus() == Status.FORBIDDEN.getStatusCode()) {
if (response.hasThrowable()) {
failure = new AccessDeniedException(message, response.getThrowable());
} else {
failure = new AccessDeniedException(message);
}
} else {
if (response.hasThrowable()) {
failure = new IllegalClusterStateException(message, response.getThrowable());
} else {
failure = new IllegalClusterStateException(message);
}
}
clusterResponse.setFailure(failure, response.getNodeId());
}
}
} finally {
if (monitor != null) {
synchronized (monitor) {
monitor.notify();
}
logger.debug("Notified monitor {} because request {} {} has failed due to at least 1 dissenting node", monitor, method, uri);
}
}
}
} catch (final Exception e) {
clusterResponse.add(new NodeResponse(nodeResponse.getNodeId(), method, uri, e));
// to the Cluster Response so that the Cluster Response is complete.
for (final NodeResponse otherResponse : nodeResponses) {
if (otherResponse.getNodeId().equals(nodeResponse.getNodeId())) {
continue;
}
clusterResponse.add(otherResponse);
}
}
}
};
// Callback function for generating a NodeHttpRequestCallable that can be used to perform the work
final Function<NodeIdentifier, NodeHttpRequest> requestFactory = nodeId -> new NodeHttpRequest(nodeId, method, createURI(uri, nodeId), entity, validationHeaders, completionCallback, clusterResponse);
// replicate the 'verification request' to all nodes
submitAsyncRequest(nodeIds, uri.getScheme(), uri.getPath(), requestFactory, validationHeaders);
}
Aggregations