This is an automated email from the ASF dual-hosted git repository. olli pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-commons-crypto.git
commit 1cc4e03bfea4a24898093a35cc3d72eaa15958d0 Author: Oliver Lietz <[email protected]> AuthorDate: Sun Dec 8 09:40:56 2019 +0100 SLING-8886 Provide a Web Console plugin to encrypt messages --- bnd.bnd | 4 + pom.xml | 6 + .../crypto/internal/EncryptWebConsolePlugin.java | 190 +++++++++++++++++++++ 3 files changed, 200 insertions(+) diff --git a/bnd.bnd b/bnd.bnd index ba06157..a714ebc 100644 --- a/bnd.bnd +++ b/bnd.bnd @@ -1,10 +1,14 @@ DynamicImport-Package:\ + javax.servlet,\ + javax.servlet.http,\ org.jasypt.encryption.pbe,\ org.jasypt.iv,\ org.jasypt.registry,\ org.jasypt.salt Import-Package:\ + javax.servlet;resolution:=optional,\ + javax.servlet.http;resolution:=optional,\ org.jasypt.encryption.pbe;resolution:=optional,\ org.jasypt.iv;resolution:=optional,\ org.jasypt.registry;resolution:=optional,\ diff --git a/pom.xml b/pom.xml index 95b97c0..daa9cdb 100644 --- a/pom.xml +++ b/pom.xml @@ -111,6 +111,12 @@ <artifactId>javax.inject</artifactId> <scope>test</scope> </dependency> + <dependency> + <groupId>javax.servlet</groupId> + <artifactId>javax.servlet-api</artifactId> + <scope>provided</scope> + <optional>true</optional> + </dependency> <!-- OSGi --> <dependency> <groupId>org.osgi</groupId> diff --git a/src/main/java/org/apache/sling/commons/crypto/internal/EncryptWebConsolePlugin.java b/src/main/java/org/apache/sling/commons/crypto/internal/EncryptWebConsolePlugin.java new file mode 100644 index 0000000..cff31ae --- /dev/null +++ b/src/main/java/org/apache/sling/commons/crypto/internal/EncryptWebConsolePlugin.java @@ -0,0 +1,190 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.commons.crypto.internal; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Arrays; +import java.util.Objects; + +import javax.servlet.RequestDispatcher; +import javax.servlet.Servlet; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; + +import org.apache.sling.commons.crypto.CryptoService; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.annotations.Activate; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; +import org.osgi.util.tracker.ServiceTracker; + +import static org.osgi.service.component.ComponentConstants.COMPONENT_ID; + +@Component( + service = Servlet.class, + property = { + "felix.webconsole.label=sling-commons-crypto-encrypt", + "felix.webconsole.title=Sling Commons Crypto Encrypt", + "felix.webconsole.category=Crypto" + } +) +public class EncryptWebConsolePlugin extends HttpServlet { + + private BundleContext bundleContext; + + private ServiceTracker<CryptoService, CryptoService> tracker; + + private static final String PARAMETER_ID = "id"; + + private static final String PARAMETER_MESSAGE = "message"; + + private static final String ATTRIBUTE_CIPHERTEXT = "org.apache.sling.commons.crypto.internal.EncryptWebConsolePlugin.ciphertext"; + + public EncryptWebConsolePlugin() { + } + + @Activate + private void activate(final BundleContext bundleContext) { + this.bundleContext = bundleContext; + tracker = new ServiceTracker<>(bundleContext, CryptoService.class, null); + tracker.open(); + } + + @Deactivate + private void deactivate() { + this.bundleContext = null; + if (Objects.nonNull(tracker)) { + tracker.close(); + tracker = null; + } + } + + @Override + protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { + final ServiceReference<CryptoService>[] references = tracker.getServiceReferences(); + final PrintWriter writer = response.getWriter(); + if (Objects.nonNull(references) && references.length > 0) { + final String form = buildForm(references); + writer.println(form); + } else { + writer.println("<p>No crypto service available</p>"); + } + + final String forwardRequestUri = (String) request.getAttribute(RequestDispatcher.FORWARD_REQUEST_URI); + if (Objects.nonNull(forwardRequestUri) && forwardRequestUri.equals(request.getRequestURI())) { + final String ciphertext = (String) request.getAttribute(ATTRIBUTE_CIPHERTEXT); + if (Objects.nonNull(ciphertext)) { + final String html = String.format("<p>Encrypted message: %s</p>", ciphertext); + writer.println(html); + } + } + } + + @Override + protected void doPost(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { + request.removeAttribute(ATTRIBUTE_CIPHERTEXT); + final String id = request.getParameter(PARAMETER_ID); + final String message = request.getParameter(PARAMETER_MESSAGE); // do NOT log SECRET message + if (Objects.isNull(id)) { + handleParameterMissing(response, PARAMETER_ID); + return; + } + if (Objects.isNull(message)) { + handleParameterMissing(response, PARAMETER_MESSAGE); + return; + } + final CryptoService cryptoService = findCryptoService(id); + if (Objects.isNull(cryptoService)) { + handleCryptoServiceNotFound(response, id); + return; + } + final String ciphertext = cryptoService.encrypt(message); + request.setAttribute(ATTRIBUTE_CIPHERTEXT, ciphertext); + final GetHttpServletRequestWrapper wrapper = new GetHttpServletRequestWrapper(request); + request.getRequestDispatcher(request.getRequestURI()).forward(wrapper, response); + } + + private void handleParameterMissing(final HttpServletResponse response, final String parameter) throws IOException { + final String message = String.format("Parameter %s is missing", parameter); + response.sendError(400, message); + } + + private void handleCryptoServiceNotFound(final HttpServletResponse response, final String id) throws IOException { + final String message = String.format("Crypto service with component id %s not found", id); + response.sendError(404, message); + } + + private @NotNull String buildForm(final ServiceReference<CryptoService>[] references) { + final StringBuilder builder = new StringBuilder(); + builder.append("<form method=\"POST\">"); + builder.append("<label for=\"message\">Message to encrypt</label>"); + builder.append("<br>"); + builder.append("<input type=\"text\" name=\"message\" id=\"message\">"); + builder.append("<br>"); + builder.append("<label>Available crypto services"); + builder.append("<br>"); + builder.append("<select name=\"id\">"); + for (final ServiceReference<CryptoService> reference : references) { + final String id = reference.getProperty(COMPONENT_ID).toString(); + final String[] names = (String[]) reference.getProperty("names"); + final String algorithm = reference.getProperty("algorithm").toString(); + final String label = String.format("Component id %s, names: %s, algorithm: %s", id, Arrays.toString(names), algorithm); + builder.append("<option value=\"").append(id).append("\">"); + builder.append(label); + builder.append("</option>"); + } + builder.append("</label>"); + builder.append("</select>"); + builder.append("<br>"); + builder.append("<button type=\"submit\">encrypt</button>"); + builder.append("</form>"); + return builder.toString(); + } + + private @Nullable CryptoService findCryptoService(@NotNull final String id) { + final ServiceReference<CryptoService>[] references = tracker.getServiceReferences(); + for (final ServiceReference<CryptoService> reference : references) { + if (id.equals(reference.getProperty(COMPONENT_ID).toString())) { + return bundleContext.getService(reference); + } + } + return null; + } + + private static class GetHttpServletRequestWrapper extends HttpServletRequestWrapper { + + GetHttpServletRequestWrapper(final HttpServletRequest request) { + super(request); + } + + @Override + public String getMethod() { + return "GET"; + } + + } + +}
