diff --git a/de.srsoftware.oidc.light/src/main/java/de/srsoftware/LightOICD.java b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/LightOICD.java deleted file mode 100644 index a1e4c68..0000000 --- a/de.srsoftware.oidc.light/src/main/java/de/srsoftware/LightOICD.java +++ /dev/null @@ -1,64 +0,0 @@ -/* © SRSoftware 2024 */ -package de.srsoftware; - -import de.srsoftware.oidc.api.User; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.List; -import java.util.Map; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebServlet; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -@WebServlet(urlPatterns = "/") -public class LightOICD extends HttpServlet { - private static final Logger LOG = LoggerFactory.getLogger(LightOICD.class); - private static final Templates templates; - static { - try { - templates = Templates.singleton(); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Override - public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { - var path = Arrays.stream(req.getRequestURI().split("/")).skip(1).toList(); - User user = null; - if (path.isEmpty()) { - landingPage(req, resp, user); - return; - } - switch (path.remove(0)) { - default: - helloWorld(req, resp, path); - } - } - - private void helloWorld(HttpServletRequest req, HttpServletResponse resp, List path) throws IOException { - LOG.debug("helloWorld(…), path = {}", path); - resp.setContentType("text/html"); - PrintWriter out = resp.getWriter(); - out.println("Hello World Servlet"); - out.println(""); - out.println("

Hello Guys!

"); - out.println(""); - out.println(""); - out.close(); - } - - private void landingPage(HttpServletRequest req, HttpServletResponse resp, User user) throws IOException { - LOG.debug("landingPage(…)"); - var index = templates.get("index.html", Map.of("user","Darling")); - resp.setContentType("text/html"); - resp.getWriter().println(index.get()); - } -} diff --git a/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/Constants.java b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/Constants.java new file mode 100644 index 0000000..84b2f03 --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/Constants.java @@ -0,0 +1,20 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.light; + +public class Constants { + public static final String BODY = "body"; + public static final String EMAIL = "email"; + public static final String ERR_PAGE_NOT_FOUND = "page_not_found"; + public static final String ERR_REDIRECT_FAILED = "redirect_failed"; + public static final String HEAD = "head"; + public static final String MESSAGES = "messages.txt"; + public static final String PAGE_LOGIN = "login"; + public static final String PAGE_START = "start"; + public static final String PAGE_WELCOME = "welcome"; + public static final String PASSWORD = "password"; + public static final String TARGET = "target"; + public static final String TITLE = "title"; + public static final String TITLE_LOGIN = "title_login"; + public static final String TITLE_WELCOME = "title_welcom"; + public static final String USER = "user"; +} diff --git a/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/LightOICD.java b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/LightOICD.java new file mode 100644 index 0000000..2955f58 --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/LightOICD.java @@ -0,0 +1,181 @@ +/* © SRSoftware 2024 */ +package de.srsoftware.oidc.light; + +import static de.srsoftware.oidc.light.Constants.*; +import static de.srsoftware.oidc.light.Templates.braced; + +import de.srsoftware.oidc.api.User; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@WebServlet(urlPatterns = "/") +public class LightOICD extends HttpServlet { + private static final Logger LOG = LoggerFactory.getLogger(LightOICD.class); + private static final Templates templates; + static { + try { + templates = Templates.singleton(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Override + public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { + var uri = req.getRequestURI(); + var path = Arrays.stream(uri.split("/")).skip(1).collect(Collectors.toList()); + if (path.isEmpty()) { + path.add(PAGE_START); + } + + var optUser = loadUser(req); + handleGet(path, optUser, req, resp).ifPresent(resp.getWriter()::println); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + var uri = req.getRequestURI(); + var path = Arrays.stream(uri.split("/")).skip(1).collect(Collectors.toList()); + if (path.isEmpty()) { + path.add(PAGE_START); + } + + var optUser = loadUser(req); + handlePost(path, optUser, req, resp).ifPresent(resp.getWriter()::println); + } + + private Optional handleGet(List path, Optional optUser, HttpServletRequest req, HttpServletResponse resp) { + String token = path.remove(0); + if (optUser.isPresent()) { + var user = optUser.get(); + switch (token) { + case PAGE_START: + return pageStart(user, req, resp); + case PAGE_WELCOME: + return pageWelcome(user,req, resp); + } + } + + switch (token) { + case PAGE_LOGIN: + return pageLogin(req, resp); + case PAGE_START: + case PAGE_WELCOME: + return redirect(resp, PAGE_LOGIN); + } + return templates.message(ERR_PAGE_NOT_FOUND); + } + + + + private Optional handlePost(List path, Optional optUser, HttpServletRequest req, HttpServletResponse resp) { + String token = path.remove(0); + if (optUser.isPresent()) { + var user = optUser.get(); + switch (token) { + case PAGE_START: + return pageStart(user, req, resp); + } + } + + switch (token) { + case PAGE_LOGIN: + return postLogin(req, resp); + case PAGE_START: + return redirect(resp, PAGE_LOGIN); + } + return templates.message(ERR_PAGE_NOT_FOUND); + } + + private Optional loadUser(HttpServletRequest req) { + HttpSession session = req.getSession(); + if (session.getAttribute(USER) instanceof User user) { + return Optional.of(user); + } + return Optional.empty(); + } + + + + private Optional pageLogin(HttpServletRequest req, HttpServletResponse resp) { + LOG.debug("pageLogin(…)"); + try { + var title = templates.message(TITLE_LOGIN).orElse(TITLE_LOGIN); + var head = templates.get("head.snippet", Map.of(TITLE, title)).get(); + var login = templates.get("login.snippet", Map.of(USER, "Darling", EMAIL, "", PASSWORD, "")).get(); + var page = templates.get("scaffold.html", Map.of(BODY, login, HEAD, head)).get(); + resp.setContentType("text/html"); + resp.getWriter().println(page); + return Optional.empty(); + } catch (Exception e) { + return Optional.of(e.getMessage()); + } + } + + private Optional pageStart(User user, HttpServletRequest req, HttpServletResponse resp) { + LOG.debug("pageStart(…)"); + return Optional.empty(); + } + + private Optional pageWelcome(User user, HttpServletRequest req, HttpServletResponse resp) { + LOG.debug("pageWelcome(…)"); + try { + var title = templates.message(TITLE_WELCOME).orElse(TITLE_WELCOME); + var head = templates.get("head.snippet", Map.of(TITLE, title)).get(); + var login = templates.get("welcome.snippet", Map.of(USER, "Darling", EMAIL, "", PASSWORD, "")).get(); + var page = templates.get("scaffold.html", Map.of(BODY, login, HEAD, head)).get(); + resp.setContentType("text/html"); + resp.getWriter().println(page); + return Optional.empty(); + } catch (Exception e) { + return Optional.of(e.getMessage()); + } + } + + private Optional postLogin(HttpServletRequest req, HttpServletResponse resp) { + LOG.debug("postLogin(…)"); + var email = req.getParameter(EMAIL); + if (braced(EMAIL).equals(email)) email = ""; + var pass = req.getParameter(PASSWORD); + var user = tryLogin(email, pass); + if (user.isPresent()) { + req.getSession().setAttribute(USER, user.get()); + return redirect(resp, PAGE_WELCOME); + } + try { + var title = templates.message(TITLE_LOGIN).orElse(TITLE_LOGIN); + var head = templates.get("head.snippet", Map.of(TITLE, title)).get(); + var login = templates.get("login.snippet", Map.of(USER, "Darling", EMAIL, email, PASSWORD, "")).get(); + var page = templates.get("scaffold.html", Map.of(BODY, login, HEAD, head)).get(); + resp.setContentType("text/html"); + resp.getWriter().println(page); + return Optional.empty(); + } catch (Exception e) { + return Optional.of(e.getMessage()); + } + } + + private Optional tryLogin(String email, String pass) { + if (email == null || pass == null) return Optional.empty(); + if (email.equals(pass)) return Optional.of(new User()); + return Optional.empty(); + } + + private Optional redirect(HttpServletResponse resp, String path) { + try { + resp.sendRedirect(path); + } catch (IOException e) { + return templates.message(ERR_REDIRECT_FAILED, Map.of(TARGET, path)); + } + return Optional.empty(); + } +} diff --git a/de.srsoftware.oidc.light/src/main/java/de/srsoftware/Templates.java b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/Templates.java similarity index 60% rename from de.srsoftware.oidc.light/src/main/java/de/srsoftware/Templates.java rename to de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/Templates.java index eea8dc9..e6515c2 100644 --- a/de.srsoftware.oidc.light/src/main/java/de/srsoftware/Templates.java +++ b/de.srsoftware.oidc.light/src/main/java/de/srsoftware/oidc/light/Templates.java @@ -1,11 +1,14 @@ /* © SRSoftware 2024 */ -package de.srsoftware; +package de.srsoftware.oidc.light; + +import static de.srsoftware.oidc.light.Constants.MESSAGES; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.HashMap; import java.util.Map; import java.util.Optional; import org.slf4j.Logger; @@ -15,6 +18,7 @@ public class Templates { private static Templates singleton = null; private static Logger LOG = LoggerFactory.getLogger(Templates.class); private Path dir = searchTemplates(); + private Map messages = null; public Templates() throws FileNotFoundException {} @@ -41,11 +45,14 @@ public class Templates { return singleton; } + public Optional get(String path) { + return get(path, Map.of()); + } + public Optional get(String path, Map replacements) { var file = dir.resolve(path); try { - return Optional.of(Files.readString(file)).map(s -> replaceKeys(s,replacements)); - // TODO: replacements + return Optional.of(Files.readString(file)).map(s -> replaceKeys(s, replacements)); } catch (IOException e) { LOG.warn("Failed to read {}", path, e); return Optional.empty(); @@ -54,7 +61,35 @@ public class Templates { } private String replaceKeys(String text, Map replacements) { - for (Map.Entry replacement : replacements.entrySet()) text = text.replace("{"+replacement.getKey()+"}",replacement.getValue()); + for (Map.Entry replacement : replacements.entrySet()) + text = text.replace(braced(replacement.getKey()), replacement.getValue()); return text; } + + public Optional message(String code) { + return message(code, Map.of()); + } + + public Optional message(String code, Map replacements) { + if (this.messages == null) { + get(MESSAGES).map(s -> s.split("\n")).ifPresent(this::setMessages); + } + return Optional.ofNullable(messages.get(code)).map(text -> replaceKeys(text, replacements)); + } + + private void setMessages(String[] lines) { + this.messages = new HashMap<>(); + for (String line : lines) { + var parts = line.split(" ", 2); + if (parts.length < 2) { + LOG.warn("Invalid format in {} file, skipped {}", MESSAGES, line); + continue; + } + messages.put(parts[0], parts[1]); + } + } + + public static String braced(String key) { + return String.join(key, "{", "}"); + } } diff --git a/de.srsoftware.oidc.light/src/main/resources/templates/head.snippet b/de.srsoftware.oidc.light/src/main/resources/templates/head.snippet new file mode 100644 index 0000000..ba8bce2 --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/resources/templates/head.snippet @@ -0,0 +1,16 @@ + +{title} + \ No newline at end of file diff --git a/de.srsoftware.oidc.light/src/main/resources/templates/login.snippet b/de.srsoftware.oidc.light/src/main/resources/templates/login.snippet new file mode 100644 index 0000000..4af605e --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/resources/templates/login.snippet @@ -0,0 +1,16 @@ +

Login

+ +
+
+ Light OIDC Login + + + +
+
\ No newline at end of file diff --git a/de.srsoftware.oidc.light/src/main/resources/templates/messages.txt b/de.srsoftware.oidc.light/src/main/resources/templates/messages.txt new file mode 100644 index 0000000..4dfd798 --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/resources/templates/messages.txt @@ -0,0 +1,2 @@ +title_login LightOIDC Login +title_welcome Willkommen bei LightOIDC! \ No newline at end of file diff --git a/de.srsoftware.oidc.light/src/main/resources/templates/scaffold.html b/de.srsoftware.oidc.light/src/main/resources/templates/scaffold.html new file mode 100644 index 0000000..c18243a --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/resources/templates/scaffold.html @@ -0,0 +1,6 @@ + + +{head} + +{body} + \ No newline at end of file diff --git a/de.srsoftware.oidc.light/src/main/resources/templates/welcome.snippet b/de.srsoftware.oidc.light/src/main/resources/templates/welcome.snippet new file mode 100644 index 0000000..45c6c97 --- /dev/null +++ b/de.srsoftware.oidc.light/src/main/resources/templates/welcome.snippet @@ -0,0 +1 @@ +Willkommen! \ No newline at end of file