@Override public void handleRequest(final HttpServerExchange exchange) throws Exception { final FormDataParser parser = formParserFactory.createParser(exchange); if (parser == null) { Common.UNSUPPORTED_MEDIA_TYPE.handleRequest(exchange); } // Prevent CSRF which can occur from standard a multipart/form-data submission from a standard // HTML form. // If the browser sends an Origin header (Chrome / Webkit) then the earlier origin check will // have passed // to reach this point. If the browser doesn't (FireFox), then only requests which came from // Javascript, // which enforces same-origin policy when no Origin header is present, should be allowed. The // presence of // a custom header indicates usage of XHR since simple forms can not set them. HeaderMap headers = exchange.getRequestHeaders(); if (!headers.contains(Headers.ORIGIN) && !headers.contains(CLIENT_NAME)) { ROOT_LOGGER.debug( "HTTP Origin or X-Management-Client-Name header is required for all multipart form data posts."); Common.UNAUTHORIZED.handleRequest(exchange); return; } // Parse the form data final FormData data = parser.parseBlocking(); final OperationParameter.Builder operationParameterBuilder = new OperationParameter.Builder(false); // Process the operation final FormData.FormValue op = data.getFirst(OPERATION); final ModelNode operation; try { String type = op.getHeaders().getFirst(Headers.CONTENT_TYPE); if (Common.APPLICATION_DMR_ENCODED.equals(type)) { try (InputStream stream = convertToStream(op)) { operation = ModelNode.fromBase64(stream); } operationParameterBuilder.encode(true); } else if (Common.APPLICATION_JSON.equals(stripSuffix(type))) { try (InputStream stream = convertToStream(op)) { operation = ModelNode.fromJSONStream(stream); } } else { ROOT_LOGGER.debug("Content-type must be application/dmr-encoded or application/json"); Common.UNAUTHORIZED.handleRequest(exchange); return; } } catch (Exception e) { ROOT_LOGGER.errorf("Unable to construct ModelNode '%s'", e.getMessage()); Common.sendError(exchange, false, e.getLocalizedMessage()); return; } // Process the input streams final OperationBuilder builder = OperationBuilder.create(operation, true); final Iterator<String> i = data.iterator(); while (i.hasNext()) { final String name = i.next(); final Deque<FormData.FormValue> contents = data.get(name); if (contents != null && !contents.isEmpty()) { for (final FormData.FormValue value : contents) { if (value.isFile()) { builder.addFileAsAttachment(value.getPath().toFile()); } } } } operationParameterBuilder.pretty( operation.hasDefined("json.pretty") && operation.get("json.pretty").asBoolean()); // Response callback to handle the :reload operation final OperationParameter opParam = operationParameterBuilder.build(); final ResponseCallback callback = new ResponseCallback() { @Override void doSendResponse(final ModelNode response) { if (response.hasDefined(OUTCOME) && FAILED.equals(response.get(OUTCOME).asString())) { Common.sendError(exchange, opParam.isEncode(), response); return; } writeResponse(exchange, 200, response, opParam); } }; final boolean sendPreparedResponse = sendPreparedResponse(operation); final ModelController.OperationTransactionControl control = sendPreparedResponse ? new ModelController.OperationTransactionControl() { @Override public void operationPrepared( final ModelController.OperationTransaction transaction, final ModelNode result) { transaction.commit(); // Fix prepared result result.get(OUTCOME).set(SUCCESS); result.get(RESULT); callback.sendResponse(result); } } : ModelController.OperationTransactionControl.COMMIT; ModelNode response; final Operation builtOp = builder.build(); try { ModelNode opheaders = operation.get(OPERATION_HEADERS); opheaders.get(ACCESS_MECHANISM).set(AccessMechanism.HTTP.toString()); opheaders.get(CALLER_TYPE).set(USER); // Don't allow a domain-uuid operation header from a user call if (opheaders.hasDefined(DOMAIN_UUID)) { opheaders.remove(DOMAIN_UUID); } response = modelController.execute(operation, OperationMessageHandler.DISCARD, control, builtOp); } catch (Throwable t) { ROOT_LOGGER.modelRequestError(t); Common.sendError(exchange, opParam.isEncode(), t.getLocalizedMessage()); return; } finally { // Close any input streams that were open if (builtOp.isAutoCloseStreams()) { for (InputStream in : builtOp.getInputStreams()) { IoUtils.safeClose(in); } } } callback.sendResponse(response); }
private HttpHandler setupSecurityHandlers(HttpHandler initialHandler) { final DeploymentInfo deploymentInfo = super.getDeployment().getDeploymentInfo(); final LoginConfig loginConfig = deploymentInfo.getLoginConfig(); final Map<String, AuthenticationMechanismFactory> factoryMap = new HashMap<>(deploymentInfo.getAuthenticationMechanisms()); if (!factoryMap.containsKey(BASIC_AUTH)) { factoryMap.put(BASIC_AUTH, BasicAuthenticationMechanism.FACTORY); } if (!factoryMap.containsKey(FORM_AUTH)) { factoryMap.put(FORM_AUTH, ServletFormAuthenticationMechanism.FACTORY); } if (!factoryMap.containsKey(DIGEST_AUTH)) { factoryMap.put(DIGEST_AUTH, DigestAuthenticationMechanism.FACTORY); } if (!factoryMap.containsKey(CLIENT_CERT_AUTH)) { factoryMap.put(CLIENT_CERT_AUTH, ClientCertAuthenticationMechanism.FACTORY); } if (!factoryMap.containsKey(ExternalAuthenticationMechanism.NAME)) { factoryMap.put(ExternalAuthenticationMechanism.NAME, ExternalAuthenticationMechanism.FACTORY); } HttpHandler current = initialHandler; current = new SSLInformationAssociationHandler(current); final SecurityPathMatches securityPathMatches = buildSecurityConstraints(); current = new ServletAuthenticationCallHandler(current); if (deploymentInfo.isDisableCachingForSecuredPages()) { current = Handlers.predicate(Predicates.authRequired(), Handlers.disableCache(current), current); } if (!securityPathMatches.isEmpty()) { current = new ServletAuthenticationConstraintHandler(current); } current = new ServletConfidentialityConstraintHandler( deploymentInfo.getConfidentialPortManager(), current); if (!securityPathMatches.isEmpty()) { current = new ServletSecurityConstraintHandler(securityPathMatches, current); } List<AuthenticationMechanism> authenticationMechanisms = new LinkedList<>(); authenticationMechanisms.add( new CachedAuthenticatedSessionMechanism()); // TODO: does this really need to be hard coded? String mechName = null; if (loginConfig != null || deploymentInfo.getJaspiAuthenticationMechanism() != null) { // we don't allow multipart requests, and always use the default encoding FormParserFactory parser = FormParserFactory.builder(false) .addParser( new FormEncodedDataDefinition() .setDefaultEncoding(deploymentInfo.getDefaultEncoding())) .build(); List<AuthMethodConfig> authMethods = Collections.<AuthMethodConfig>emptyList(); if (loginConfig != null) { authMethods = loginConfig.getAuthMethods(); } for (AuthMethodConfig method : authMethods) { AuthenticationMechanismFactory factory = factoryMap.get(method.getName()); if (factory == null) { throw UndertowServletMessages.MESSAGES.unknownAuthenticationMechanism(method.getName()); } if (mechName == null) { mechName = method.getName(); } final Map<String, String> properties = new HashMap<>(); properties.put( AuthenticationMechanismFactory.CONTEXT_PATH, deploymentInfo.getContextPath()); properties.put(AuthenticationMechanismFactory.REALM, loginConfig.getRealmName()); properties.put(AuthenticationMechanismFactory.ERROR_PAGE, loginConfig.getErrorPage()); properties.put(AuthenticationMechanismFactory.LOGIN_PAGE, loginConfig.getLoginPage()); properties.putAll(method.getProperties()); String name = method.getName().toUpperCase(Locale.US); // The mechanism name is passed in from the HttpServletRequest interface as the name // reported needs to be // comparable using '==' name = name.equals(FORM_AUTH) ? FORM_AUTH : name; name = name.equals(BASIC_AUTH) ? BASIC_AUTH : name; name = name.equals(DIGEST_AUTH) ? DIGEST_AUTH : name; name = name.equals(CLIENT_CERT_AUTH) ? CLIENT_CERT_AUTH : name; authenticationMechanisms.add(factory.create(name, parser, properties)); } } ((DeploymentImpl) super.getDeployment()).setAuthenticationMechanisms(authenticationMechanisms); // if the JASPI auth mechanism is set then it takes over if (deploymentInfo.getJaspiAuthenticationMechanism() == null) { current = new AuthenticationMechanismsHandler(current, authenticationMechanisms); } else { current = new AuthenticationMechanismsHandler( current, Collections.<AuthenticationMechanism>singletonList( deploymentInfo.getJaspiAuthenticationMechanism())); } current = new CachedAuthenticatedSessionHandler(current, super.getDeployment().getServletContext()); List<NotificationReceiver> notificationReceivers = deploymentInfo.getNotificationReceivers(); if (!notificationReceivers.isEmpty()) { current = new NotificationReceiverHandler(current, notificationReceivers); } // TODO - A switch to constraint driven could be configurable, however before we can support // that with servlets we would // need additional tracking within sessions if a servlet has specifically requested that // authentication occurs. SecurityContextFactory contextFactory = deploymentInfo.getSecurityContextFactory(); if (contextFactory == null) { contextFactory = SecurityContextFactoryImpl.INSTANCE; } current = new SecurityInitialHandler( AuthenticationMode.PRO_ACTIVE, deploymentInfo.getIdentityManager(), mechName, contextFactory, current); return current; }
public DomainApiGenericOperationHandler(ModelController modelController) { this.modelController = modelController; this.formParserFactory = FormParserFactory.builder().build(); }