gessnerfl / fake-smtp-server

A simple SMTP Server for Testing purposes. Emails are stored in an in-memory database and rendered in a Web UI
Apache License 2.0
422 stars 88 forks source link

Add authentication to login fakesmtp page #60

Open ilkin opened 2 years ago

ilkin commented 2 years ago

Please add login page to authenticate fakesmtp interface. Authentication could be optional and Username and password can canfigure in application.properties.

gessnerfl commented 2 years ago

Will consider it in a future release. Contribution would also be appreciated ;)

grig0ry commented 1 year ago

+1, this would be useful

gessnerfl commented 1 year ago

@ilkin @grig0ry Do have any specific authentication method in mind? LDAP, OAuth, SAML2, etc.

smuda commented 1 year ago

Please make this optional. Our use case would be crippled with forced authentication.

gessnerfl commented 1 year ago

@smuda yes in case this is getting implemented authentication will be optional.

@ilkin / @grig0ry can you please elaborate in more detail the need for authentication? The tool is intended for development purposes only.

grig0ry commented 1 year ago

@gessnerfl In our case, we run test builds of the application publicly, for QA and the development team. With a secure web UI, it would be calmer :)

ilkin commented 1 year ago

Also, we are using it for QA and UAT purposes and it is a public server in the company that many users can access, so simple authentication might be good at least.

Slartibartfast27 commented 1 year ago

Just a thought: We also use fakesmtp, which contains non-public data in testcases. Therefore we added traefik as a reverse proxy with basic authentication before and made fakesmtp website accessible only through traefik, see https://doc.traefik.io/traefik/middlewares/http/basicauth/ Maybe this could be a simple solution without the need to change a line in fakesmtp.

This is our docker-compose file (no exposed ports for web interface):

version: '3'
services:
  k-fakesmtp:
    # https://hub.docker.com/r/gessnerfl/fake-smtp-server
    # https://github.com/gessnerfl/fake-smtp-server
    image:          gessnerfl/fake-smtp-server
    container_name: k-fakesmtp
    environment:
      - TZ=Europe/Zurich
    volumes:
      - ./mountpoints/application.properties:/root/application.properties:ro
    entrypoint: ["java",
                 "-Djava.security.egd=file:/dev/./urandom",
                 "-Dfile.encoding=utf-8",
                 "-cp", "@/app/jib-classpath-file",
                 "de.gessnerfl.fakesmtp.Application",
                 "--spring.config.location=/root/application.properties"]
    labels:
      - traefik.enable=true

      - traefik.http.routers.fakesmtpHTTPS.rule=Host(`fakesmtp-dev01.company.com`)
      - traefik.http.routers.fakesmtpHTTPS.tls=true
      - traefik.http.routers.fakesmtpHTTPS.middlewares=basicAuthForUsers@file

      - traefik.http.services.fakesmtp.loadbalancer.server.port=5080

    networks:
      - traefik_shared
    ports:
      - 5025:5025
      # do NOT expose ports 5080 / 5081 as we want to have basicAuth from traefik

networks:
  traefik_shared:
    external:
      name: traefik_shared
jessec commented 1 year ago

Not sure if it is useful for anybody but you can implement a quick and dirty, but easy solution with this class. You could redirect to a login page if the right cookie is not found to set the right cookie.

package proxy;

import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer;

import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.InetSocketAddress; import java.net.URL; import java.util.List; import java.util.Map;

public class ReverseProxyServer {

private static final int PORT = 8000;
private static final String DESTINATION_URL = "http://localhost:8080";

public static void main(String[] args) throws IOException {
    HttpServer server = HttpServer.create(new InetSocketAddress(PORT), 0);
    server.createContext("/", new RequestHandler());
    server.start();
    System.out.println("Reverse Proxy Server started on port " + PORT);
}

static class RequestHandler implements HttpHandler {
    @Override
    public void handle(HttpExchange exchange) throws IOException {
        String requestMethod = exchange.getRequestMethod();
        String requestPath = exchange.getRequestURI().getPath();

        String requestQuery = exchange.getRequestURI().getQuery();

        System.out.println(requestQuery);

        String destinationUrl = DESTINATION_URL + requestPath + "?" + requestQuery;

        HttpURLConnection connection = (HttpURLConnection) new URL(destinationUrl).openConnection();
        connection.setRequestMethod(requestMethod);

        // Forward headers from the original request to the destination
        for (String header : exchange.getRequestHeaders().keySet()) {
            connection.setRequestProperty(header, exchange.getRequestHeaders().getFirst(header));
        }

        // Copy the cookies from the original request to the destination
        Map<String, List<String>> cookieHeaders = exchange.getRequestHeaders();
        if (cookieHeaders.containsKey("Cookie")) {
            String cookies = String.join("; ", cookieHeaders.get("Cookie"));
            System.out.println(cookies);
            connection.setRequestProperty("Cookie", cookies);
        }

        // Copy the response from the destination to the client
        try (OutputStream outputStream = exchange.getResponseBody();
             InputStream inputStream = connection.getInputStream()) {
            exchange.sendResponseHeaders(connection.getResponseCode(), 0);
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        } finally {
            connection.disconnect();
        }
    }
}

}