Skip to content

Commit

Permalink
Issue #113: Allow to log-in with code sent to e-mail address.
Browse files Browse the repository at this point in the history
  • Loading branch information
haumacher committed Jan 7, 2025
1 parent cc8b283 commit 0fa2038
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,29 @@
import de.haumacher.phoneblock.db.DBService;
import de.haumacher.phoneblock.mail.MailService;
import de.haumacher.phoneblock.mail.MailServiceStarter;
import de.haumacher.phoneblock.util.ServletUtil;

/**
* {@link HttpServlet} that is invoked from the <code>signup.jsp</code> form.
*/
@WebServlet(urlPatterns = {
EMailVerificationServlet.VERIFY_WEB,
EMailVerificationServlet.LOGIN_WEB,
})
public class EMailVerificationServlet extends HttpServlet {

/**
* Request attribute holding the page to re-start login/signup.
*/
public static final String RESTART_PAGE_ATTR = "restartPage";

/**
* Request attribute set, if e-mail verification failed.
*/
public static final String VERIFY_ERROR_ATTR = "message";

public static final String VERIFY_WEB = "/verify-web";
public static final String LOGIN_WEB = "/login-web";

private static final Logger LOG = LoggerFactory.getLogger(EMailVerificationServlet.class);

Expand Down Expand Up @@ -66,17 +79,23 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S
req.getSession().setAttribute("email", email);
req.getSession().setAttribute("code", code);
req.setAttribute("email", email);
req.setAttribute(RESTART_PAGE_ATTR, failurePage(req));
req.getRequestDispatcher(successPage(req)).forward(req, resp);
}

private void sendFailure(HttpServletRequest req, HttpServletResponse resp, String message)
throws ServletException, IOException {
req.setAttribute("message", message);
req.setAttribute(VERIFY_ERROR_ATTR, message);
req.getRequestDispatcher(failurePage(req)).forward(req, resp);
}

private String failurePage(HttpServletRequest req) {
/**
* The page to redirect, if something went wrong.
*/
private static String failurePage(HttpServletRequest req) {
switch (req.getServletPath()) {
case LOGIN_WEB:
return "/login.jsp";
case VERIFY_WEB:
default:
return "/signup.jsp";
Expand All @@ -85,6 +104,7 @@ private String failurePage(HttpServletRequest req) {

private String successPage(HttpServletRequest req) {
switch (req.getServletPath()) {
case LOGIN_WEB:
case VERIFY_WEB:
default:
return "/signup-code.jsp";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
@WebServlet(urlPatterns = LoginServlet.PATH)
public class LoginServlet extends HttpServlet {

/**
* Request attribute set, if a login was not successful.
*/
public static final String LOGIN_ERROR_ATTR = "loginError";

public static final String USER_NAME_PARAM = "userName";

public static final String PASSWORD_PARAM = "password";
Expand Down Expand Up @@ -120,7 +125,7 @@ public static void processRememberMe(HttpServletRequest req, HttpServletResponse
* Redirects the client to the login page.
*/
public static void sendFailure(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("error", "Anmeldung fehlgeschlagen.");
req.setAttribute(LOGIN_ERROR_ATTR, "Anmeldung fehlgeschlagen.");
req.getRequestDispatcher("/login.jsp").forward(req, resp);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@
})
public class RegistrationServlet extends HttpServlet {

/**
* Request attribute set, if registration fails.
*/
public static final String REGISTER_ERROR_ATTR = "message";

public static final String REGISTER_WEB = "/register-web";

private static final String PASSWORD_ATTR = "passwd";
Expand All @@ -53,16 +58,23 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S

String login;
try {
String passwd;

DB db = DBService.getInstance();
login = db.getEmailLogin(email);
if (login == null) {
login = UUID.randomUUID().toString();
String passwd = db.createUser(login, email);
passwd = db.createUser(login, email);
db.setEmail(login, email);
startSetup(req, resp, login, passwd);
} else {
startSetup(req, resp, login, null);
// No longer known.
passwd = null;
}

String rememberValue = req.getParameter(LoginServlet.REMEMBER_PARAM);
LoginServlet.processRememberMe(req, resp, db, rememberValue, login);

startSetup(req, resp, login, passwd);
} catch (Exception ex) {
LOG.error("Failed to create user: " + email, ex);

Expand Down Expand Up @@ -103,7 +115,7 @@ private static String successPage(HttpServletRequest req) {
}

private void sendError(HttpServletRequest req, HttpServletResponse resp, String message) throws ServletException, IOException {
req.setAttribute("message", message);
req.setAttribute(REGISTER_ERROR_ATTR, message);
req.getRequestDispatcher(errorPage(req)).forward(req, resp);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
}

if (login == null) {
// Create new account.
login = UUID.randomUUID().toString();

if (displayName == null) {
Expand All @@ -112,7 +113,7 @@ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws Se
}

String passwd = db.createUser(login, displayName);
db.setGoogleId(login, googleId, displayName);
db.setGoogleId(login, googleId, null);
if (email != null) {
try {
db.setEmail(login, email);
Expand Down
89 changes: 81 additions & 8 deletions phoneblock/src/main/webapp/login.jsp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!DOCTYPE html>
<%@page import="de.haumacher.phoneblock.app.EMailVerificationServlet"%>
<%@page import="de.haumacher.phoneblock.app.SettingsServlet"%>
<%@page import="java.net.URLEncoder"%>
<%@page import="java.net.URL"%>
Expand All @@ -15,10 +16,6 @@
<jsp:include page="head-content.jspf"></jsp:include>
</head>

<%
boolean error = request.getAttribute("error") != null;
String userActive = error ? "is-active" : "";
%>
<body>
<jsp:include page="header.jspf"></jsp:include>

Expand Down Expand Up @@ -82,15 +79,91 @@
</div>
</div>
</nav>

<%
Object emailMessage = request.getAttribute(EMailVerificationServlet.VERIFY_ERROR_ATTR);
boolean emailError = emailMessage != null;
String emailActive = emailError ? "is-active" : "";
String inputClass = emailError ? "input is-danger" : "input";
%>
<nav class="panel">
<p class="panel-heading"><a href="#emailLogin" data-action="collapse"><i class="fas fa-envelope"></i> <span>Mit E-Mail anmelden</span></a></p>
<div id="emailLogin" class="is-collapsible <%=emailActive%>">
<form action="<%= request.getContextPath() %><%= EMailVerificationServlet.LOGIN_WEB%>" method="post" enctype="application/x-www-form-urlencoded">
<div class="panel-block">
<div class="content">
<% if (location != null) { %>
<input type="hidden" name="<%=LoginServlet.LOCATION_ATTRIBUTE%>" value="<%= JspUtil.quote(location) %>">
<% } %>
<div class="field">
<label class="label">E-Mail</label>
<div class="control has-icons-left has-icons-right">
<input name="email"
class="<%= inputClass %>"
type="email"
placeholder="Deine E-Mail-Adresse"
value="<%= JspUtil.quote(request.getAttribute("email")) %>"
/>
<span class="icon is-small is-left">
<i class="fas fa-envelope"></i>
</span>
<% if (emailError) { %>
<span class="icon is-small is-right">
<i class="fas fa-exclamation-triangle"></i>
</span>
<% } %>
</div>
<% if (emailError) { %>
<p class="help is-danger">
<%= JspUtil.quote(request.getAttribute("message")) %>
</p>
<% } %>
</div>

<div class="field">
<div class="control">
<label class="checkbox">
<input type="checkbox" name="<%=LoginServlet.REMEMBER_PARAM%>" value="true"/>
<span>Auf diesem Gerät angemeldet bleiben (setzt ein <a href="<%=request.getContextPath()%>/datenschutz.jsp">Cookie</a>)</span>
</label>
</div>
</div>

<div class="field is-grouped">
<p class="control">
<button class="button is-primary" type="submit">
<span class="icon">
<i class="fa-solid fa-right-to-bracket"></i>
</span>
<span>Code anfordern</span>
</button>
</p>
</div>

<p>
Du erhälst einen Code an die angegebene E-Mail-Adresse, mit dem Du Dich anmelden kannst.
Wenn Du schon einen PhoneBlock-Nutzernamen und ein Passwort hast, dann kannst Du Dich auch
mit diesem anmelden, siehe unten.
</p>
</div>
</div>
</form>
</div>
</nav>

<%
boolean loginError = request.getAttribute(LoginServlet.LOGIN_ERROR_ATTR) != null;
String userActive = loginError ? "is-active" : "";
%>

<nav class="panel">
<p class="panel-heading">
<a href="#loginForm" data-action="collapse"><i class="fas fa-user"></i> <span>Mit PhoneBlock-Nutzernamen anmelden</span></a>
</p>
<div id="loginForm" class="is-collapsible <%=userActive%>">
<div class="panel-block">
<div class="content">
<form action="<%=request.getContextPath()%>/login" method="post">
<form action="<%=request.getContextPath()%><%=LoginServlet.PATH %>" method="post">
<%
if (location != null) {
%>
Expand All @@ -117,11 +190,11 @@
<i class="fas fa-lock"></i>
</span>
</p>
<% if (error) {%>
<% if (loginError) {%>
<p class="help is-danger">Die Anmeldedaten stimmen nicht überein, bitte überprüfe die Eingaben und versuche es noch einmal.</p>
<% } else { %>
<% } else { %>
<p class="help">Das Passwort wurde Dir nach der <a href="<%=request.getContextPath()%>/signup.jsp<%=locationParamFirst%>">Registrierung</a> angezeigt.</p>
<% } %>
<% } %>
</div>

<div class="field">
Expand Down
64 changes: 25 additions & 39 deletions phoneblock/src/main/webapp/signup-code.jsp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<!DOCTYPE html>
<%@page import="de.haumacher.phoneblock.app.EMailVerificationServlet"%>
<%@page import="de.haumacher.phoneblock.app.RegistrationServlet"%>
<%@page import="de.haumacher.phoneblock.app.LoginServlet"%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" session="false"%>
Expand All @@ -17,12 +18,13 @@
<section class="section">
<div class="content">

<h1>E-Mail bestätigen</h1>

<p>
Eine E-Mail wurde an die Adresse <code><%= JspUtil.quote(request.getAttribute("email")) %></code> gesendet.
Bitte schau in Deiner Mailbox nach und kopiere den Code.
</p>
<%
String location = LoginServlet.location(request);
Object errorMessage = request.getAttribute(RegistrationServlet.REGISTER_ERROR_ATTR);
boolean hasError = errorMessage != null;
String inputClass = hasError ? "input is-danger" : "input";
Object rememberMe = request.getParameter(LoginServlet.REMEMBER_PARAM);
%>

<div class="columns">
<div class="column">
Expand All @@ -33,61 +35,45 @@
<p class="subtitle">E-Mail-Adresse bestätigen.</p>
<div class="content">
<p>
Bitte bestätige Deine E-Mail-Adresse, indem Du den Code aus der Anmelde-E-Mail hier eingibst.
Eine E-Mail wurde an die Adresse <code><%= JspUtil.quote(request.getAttribute("email")) %></code> gesendet.
Bitte schau in Deiner Mailbox nach und gib den Code hier ein.
</p>
<%
String location = LoginServlet.location(request);
%>
<form action="<%= request.getContextPath() %><%=RegistrationServlet.REGISTER_WEB%>" method="post" enctype="application/x-www-form-urlencoded">
<div class="field">
<%
Object message = request.getAttribute("message");
%>
<label class="label">Code</label>
<div class="control has-icons-left has-icons-right">
<%
if (location != null) {
%>
<% if (location != null) { %>
<input type="hidden" name="<%=LoginServlet.LOCATION_ATTRIBUTE%>" value="<%= JspUtil.quote(location) %>">
<%
}
%>
<% } %>
<% if (rememberMe != null) { %>
<input type="hidden" name="<%=LoginServlet.REMEMBER_PARAM%>" value="<%= JspUtil.quote(rememberMe) %>">
<% } %>

<input name="code" class="input<%= message != null ? " is-danger" : "" %>" type="text" placeholder="Bestätigungscode" value="">
<input name="code" class="<%= inputClass %>" type="text" placeholder="Bestätigungscode" value="">
<span class="icon is-small is-left">
<i class="fa-solid fa-barcode"></i>
</span>
<%
if (message != null) {
%>
<% if (hasError) { %>
<span class="icon is-small is-right">
<i class="fas fa-exclamation-triangle"></i>
</span>
<%
}
%>
<% } %>
</div>
<%
if (message != null) {
%>
<% if (hasError) { %>
<p class="help is-danger">
<%= JspUtil.quote(request.getAttribute("message")) %>
<a href="<%=request.getContextPath()%>/signup.jsp<%= LoginServlet.locationParamFirst(request) %>">Nochmal probieren</a>.
<%= JspUtil.quote(errorMessage) %>
<a href="<%=request.getContextPath()%><%=request.getAttribute(EMailVerificationServlet.RESTART_PAGE_ATTR)%><%= LoginServlet.locationParamFirst(request) %>">Nochmal probieren</a>.
</p>
<%
} else {
%>
<% } else { %>
<p class="help is-info">
Keine E-Mail erhalten? Prüfe bitte Deinen Spam-Ordner!
<a href="<%=request.getContextPath()%>/signup.jsp<%= LoginServlet.locationParamFirst(request) %>">Nochmal probieren</a>.
<a href="<%=request.getContextPath()%><%=request.getAttribute(EMailVerificationServlet.RESTART_PAGE_ATTR)%><%= LoginServlet.locationParamFirst(request) %>">Nochmal probieren</a>.
</p>
<%
}
%>
<% } %>
</div>

<div class="buttons is-right">
<button class="button is-link" type="submit">Account erstellen</button>
<button class="button is-link" type="submit">Anmelden</button>
</div>
</form>
</div>
Expand Down

0 comments on commit 0fa2038

Please sign in to comment.