@ -15,8 +15,7 @@ import java.io.IOException;
@@ -15,8 +15,7 @@ import java.io.IOException;
import java.nio.file.Files ;
import java.security.InvalidKeyException ;
import java.sql.SQLException ;
import java.util.HashMap ;
import java.util.Map ;
import java.util.* ;
import static de.srsoftware.widerhall.Constants.* ;
import static de.srsoftware.widerhall.Util.t ;
@ -37,6 +36,7 @@ public class Web extends TemplateServlet {
@@ -37,6 +36,7 @@ public class Web extends TemplateServlet {
private static final String POST = "post" ;
private static final String REGISTER = "register" ;
private static final String RELOAD = "reload" ;
private static final String RESET_PASSWORD = "reset-pw" ;
private static final String SUBSCRIBE = "subscribe" ;
private static final String UNSUBSCRIBE = "unsubscribe" ;
private static final String IMAP_HOST = "imap_host" ;
@ -49,7 +49,7 @@ public class Web extends TemplateServlet {
@@ -49,7 +49,7 @@ public class Web extends TemplateServlet {
private static final String SMTP_PASS = "smtp_pass" ;
private static final int PRIMARY_KEY_CONSTRAINT = 19 ;
private String addList ( HttpServletRequest req , HttpServletResponse resp ) {
private String addList ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
var user = Util . getUser ( req ) ;
if ( user = = null ) return redirectTo ( LOGIN , resp ) ;
var data = new HashMap < String , Object > ( ) ;
@ -139,15 +139,16 @@ public class Web extends TemplateServlet {
@@ -139,15 +139,16 @@ public class Web extends TemplateServlet {
var allowed = list . hasPublicArchive ( ) | | list . mayBeAlteredBy ( user ) ;
if ( ! allowed ) return t ( "You are not allowed to access the archive of this list" ) ;
var map = new HashMap < String , Object > ( ) ;
map . put ( LIST , list . email ( ) ) ;
var data = new HashMap < String , Object > ( ) ;
if ( user ! = null ) data . put ( USER , user ) ;
data . put ( LIST , list . email ( ) ) ;
var month = req . getParameter ( MONTH ) ;
if ( month ! = null & & ! month . isBlank ( ) ) {
map . put ( MONTH , month ) ;
map . put ( MODERATOR , list . mayBeAlteredBy ( user ) ) ;
data . put ( MONTH , month ) ;
data . put ( MODERATOR , list . mayBeAlteredBy ( user ) ) ;
}
return loadTemplate ( ARCHIVE , map , resp ) ;
return loadTemplate ( ARCHIVE , data , resp ) ;
}
private String confirm ( HttpServletRequest req , HttpServletResponse resp ) {
@ -169,17 +170,17 @@ public class Web extends TemplateServlet {
@@ -169,17 +170,17 @@ public class Web extends TemplateServlet {
@Override
protected void doGet ( HttpServletRequest req , HttpServletResponse resp ) throws IOException {
String error = handleGe t ( req , resp ) ;
String error = handleReques t ( req , resp , true ) ;
if ( error ! = null ) resp . sendError ( 400 , error ) ;
}
@Override
protected void doPost ( HttpServletRequest req , HttpServletResponse resp ) throws IOException {
String error = handlePo st ( req , resp ) ;
String error = handleReque st ( req , resp , false ) ;
if ( error ! = null ) resp . sendError ( 400 , error ) ;
}
public String editList ( HttpServletRequest req , HttpServletResponse resp ) {
public String editList ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
var user = Util . getUser ( req ) ;
if ( user = = null ) return redirectTo ( LOGIN , resp ) ;
@ -188,6 +189,7 @@ public class Web extends TemplateServlet {
@@ -188,6 +189,7 @@ public class Web extends TemplateServlet {
var list = Util . getMailingList ( req ) ;
data . put ( LIST , list . safeMap ( ) ) ;
if ( isGet ) return loadTemplate ( EDIT_LIST , data , resp ) ;
try {
var allowed = user . hashPermission ( PERMISSION_ADMIN ) | | ListMember . load ( list , user ) . isOwner ( ) ;
if ( ! allowed ) return loadTemplate ( ADMIN , data , resp ) ;
@ -273,11 +275,56 @@ public class Web extends TemplateServlet {
@@ -273,11 +275,56 @@ public class Web extends TemplateServlet {
return sqle ;
}
private User getSessionUser ( HttpServletRequest req ) {
return req . getSession ( ) . getAttribute ( USER ) instanceof User user ? user : null ;
private String handleLogin ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
if ( isGet ) return loadTemplate ( LOGIN , null , resp ) ;
var email = req . getParameter ( "email" ) ;
var pass = req . getParameter ( "pass" ) ;
if ( email = = null | | pass = = null ) return loadTemplate ( LOGIN , Map . of ( "error" , t ( "Missing username or password!" ) ) , resp ) ;
if ( ! Util . isEmail ( email ) ) return loadTemplate ( LOGIN , Map . of ( "error" , t ( "'{}' is not a valid email address!" , email ) ) , resp ) ;
try {
var user = User . loadUser ( email , pass ) ;
req . getSession ( ) . setAttribute ( "user" , user ) ;
// loading user successfull: goto index
resp . sendRedirect ( String . join ( "/" , WEB_ROOT , "admin" ) ) ;
} catch ( Exception e ) {
try {
LOG . warn ( "Static.handleLogin failed:" , e ) ;
Thread . sleep ( 10000 ) ;
} finally {
return loadTemplate ( "login" , Map . of ( ERROR , t ( "Invalid username/password" ) , EMAIL , email ) , resp ) ;
}
}
return null ;
}
private String handleGet ( HttpServletRequest req , HttpServletResponse resp ) {
private String handleNewPassword ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
var user = Util . getUser ( req ) ;
if ( user = = null ) return redirectTo ( LOGIN , resp ) ;
var data = new HashMap < String , Object > ( ) ;
data . put ( USER , user . safeMap ( ) ) ;
if ( ! isGet ) {
var pass = req . getParameter ( PASSWORD ) ;
var repeat = req . getParameter ( PASSWORD_REPEAT ) ;
if ( pass = = null | | pass . isBlank ( ) ) {
data . put ( ERROR , "Please set a password!" ) ;
} else if ( ! pass . equals ( repeat ) ) {
data . put ( ERROR , "Passwords do not match" ) ;
} else if ( Util . simplePassword ( pass ) ) {
data . put ( ERROR , "Your password is to simple" ) ;
} else {
try {
user . setPassword ( pass ) ;
data . put ( NOTES , "Your password has been updated!" ) ;
return loadTemplate ( ADMIN , data , resp ) ;
} catch ( SQLException e ) {
data . put ( ERROR , t ( "Failed to update password in database: {}" , e . getMessage ( ) ) ) ;
}
}
}
return loadTemplate ( NEW_PASSWORD_FORM , data , resp ) ;
}
private String handleRequest ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
var path = Util . getPath ( req ) ;
var user = Util . getUser ( req ) ;
var data = new HashMap < String , Object > ( ) ;
@ -285,39 +332,75 @@ public class Web extends TemplateServlet {
@@ -285,39 +332,75 @@ public class Web extends TemplateServlet {
if ( user ! = null ) data . put ( USER , user . safeMap ( ) ) ;
if ( list ! = null ) data . put ( LIST , list . minimalMap ( ) ) ;
var interesting = ! Set . of ( "js" , "jquery" , "css" , "OpenSans-Regular.woff" , "Bhineka.ttf" ) . contains ( path ) ;
LOG . debug ( "{}" , interesting ? "interesting" : "boring" ) ;
if ( user ! = null ) {
switch ( path ) {
case ADD_LIST :
return addList ( req , resp , isGet ) ;
case ADMIN :
return loadTemplate ( path , data , resp ) ;
case EDIT_LIST :
return editList ( req , resp , isGet ) ;
case INSPECT :
return inspect ( req , resp , isGet ) ;
case NEW_PASSWORD_FORM :
return handleNewPassword ( req , resp , isGet ) ;
}
}
switch ( path ) {
case ARCHIVE :
return archive ( list , user , req , resp ) ;
case CSS :
case INDEX :
return loadTemplate ( path , data , resp ) ;
case CONFIRM :
return confirm ( req , resp ) ;
case LOGIN :
return handleLogin ( req , resp , isGet ) ;
case LOGOUT :
req . getSession ( ) . invalidate ( ) ;
return redirectTo ( INDEX , resp ) ;
case POST :
return post ( req , resp ) ;
case REGISTER :
return registerUser ( req , resp , isGet ) ;
case RELOAD :
loadTemplates ( ) ;
data . put ( NOTES , t ( "Templates have been reloaded!" ) ) ;
path = INDEX ;
case CSS :
case INDEX :
return loadTemplate ( path , data , resp ) ;
return loadTemplate ( INDEX , data , resp ) ;
case RESET_PASSWORD :
if ( ! isGet ) return resetPassword ( req , resp ) ;
// TODO: move following code into resetPassword method
try {
user = User . byToken ( req . getParameter ( TOKEN ) ) ;
if ( user ! = null ) {
req . getSession ( ) . setAttribute ( "user" , user ) ;
return redirectTo ( NEW_PASSWORD_FORM , resp ) ;
}
} catch ( SQLException sqle ) {
return loadTemplate ( path , Map . of ( ERROR , "Failed to add user for token!" ) , resp ) ;
}
var email = req . getParameter ( EMAIL ) ;
return loadTemplate ( path , email = = null ? null : Map . of ( EMAIL , email ) , resp ) ;
case SUBSCRIBE :
if ( ! isGet ) {
return subscribe ( req , resp ) ;
} // TODO: Code für GET-Request mit in subscribe-Methode verschieben
if ( list . isOpenFor ( user ) ) {
data . put ( LIST , list . email ( ) ) ;
return loadTemplate ( path , data , resp ) ;
}
return t ( "You are not allowed to subscribe to '{}'!" , list . email ( ) ) ;
case UNSUBSCRIBE :
return unsubscribe ( req , resp , isGet ) ;
}
/* uninteresting paths */
switch ( path ) {
case "js" :
resp . setContentType ( "text/javascript" ) ;
return loadTemplate ( path , data , resp ) ;
case LOGIN :
try {
if ( User . noUsers ( ) ) return loadTemplate ( REGISTER , Map . of ( NOTES , t ( "User database is empty. Create admin user first:" ) ) , resp ) ;
return loadTemplate ( path , null , resp ) ;
} catch ( SQLException e ) {
return "Error reading user database!" ;
}
case LOGOUT :
req . getSession ( ) . invalidate ( ) ;
return redirectTo ( INDEX , resp ) ;
case "jquery" :
resp . setContentType ( "text/javascript" ) ;
return loadFile ( "jquery-3.6.0.min.js" , resp ) ;
@ -329,69 +412,16 @@ public class Web extends TemplateServlet {
@@ -329,69 +412,16 @@ public class Web extends TemplateServlet {
return loadFile ( "Bhineka.ttf" , resp ) ; case UNSUBSCRIBE :
data . put ( LIST , list . email ( ) ) ;
return loadTemplate ( path , data , resp ) ;
}
if ( user ! = null ) {
if ( list ! = null ) data . put ( LIST , req . getParameter ( LIST ) ) ;
switch ( path ) {
case EDIT_LIST :
return editList ( req , resp ) ;
}
return loadTemplate ( path , data , resp ) ;
}
return redirectTo ( LOGIN , resp ) ;
}
private String handleLogin ( HttpServletRequest req , HttpServletResponse resp ) {
var email = req . getParameter ( "email" ) ;
var pass = req . getParameter ( "pass" ) ;
if ( email = = null | | pass = = null ) return loadTemplate ( "login" , Map . of ( "error" , t ( "Missing username or password!" ) ) , resp ) ;
if ( ! Util . isEmail ( email ) ) return loadTemplate ( "login" , Map . of ( "error" , t ( "'{}' is not a valid email address!" , email ) ) , resp ) ;
try {
var user = User . loadUser ( email , pass ) ;
req . getSession ( ) . setAttribute ( "user" , user ) ;
// loading user successfull: goto index
resp . sendRedirect ( String . join ( "/" , WEB_ROOT , "admin" ) ) ;
} catch ( Exception e ) {
try {
LOG . warn ( "Static.handleLogin failed:" , e ) ;
Thread . sleep ( 10000 ) ;
} finally {
return loadTemplate ( "login" , Map . of ( ERROR , t ( "Invalid username/password" ) , EMAIL , email ) , resp ) ;
}
}
return null ;
}
private String handlePost ( HttpServletRequest req , HttpServletResponse resp ) {
final var path = Util . getPath ( req ) ;
switch ( path ) {
case ADD_LIST :
return addList ( req , resp ) ;
case EDIT_LIST :
return editList ( req , resp ) ;
case INSPECT :
return inspect ( req , resp ) ;
case LOGIN :
return handleLogin ( req , resp ) ;
case REGISTER :
return registerUser ( req , resp ) ;
case SUBSCRIBE :
return subscribe ( req , resp ) ;
case UNSUBSCRIBE :
return unsubscribe ( req , resp ) ;
}
return t ( "No handler for path {}!" , path ) ;
}
private String inspect ( HttpServletRequest req , HttpServletResponse resp ) {
private String inspect ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
var user = Util . getUser ( req ) ;
if ( user = = null ) return redirectTo ( LOGIN , resp ) ;
var data = new HashMap < String , Object > ( ) ;
data . put ( USER , user ) ;
var error = false ;
var list = Util . getMailingList ( req ) ;
@ -399,6 +429,7 @@ public class Web extends TemplateServlet {
@@ -399,6 +429,7 @@ public class Web extends TemplateServlet {
error = true ;
data . put ( ERROR , t ( "No valid mailing list provided!" ) ) ;
} else data . put ( LIST , list . email ( ) ) ;
if ( isGet ) return loadTemplate ( INSPECT , data , resp ) ;
if ( ! error & & ! list . mayBeAlteredBy ( user ) ) {
error = true ;
@ -451,10 +482,8 @@ public class Web extends TemplateServlet {
@@ -451,10 +482,8 @@ public class Web extends TemplateServlet {
}
}
private String registerUser ( HttpServletRequest req , HttpServletResponse resp ) {
private String registerUser ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
if ( isGet ) return loadTemplate ( REGISTER , null , resp ) ;
var email = req . getParameter ( "email" ) ;
var pass = req . getParameter ( "pass" ) ;
var pass_repeat = req . getParameter ( "pass_repeat" ) ;
@ -463,9 +492,9 @@ public class Web extends TemplateServlet {
@@ -463,9 +492,9 @@ public class Web extends TemplateServlet {
if ( email = = null | | email . isBlank ( ) | |
name = = null | | name . isBlank ( ) | |
pass = = null | | pass . isBlank ( ) | |
pass_repeat = = null | | pass_repeat . isBlank ( ) ) return loadTemplate ( REGISTER , Map . of ( ERROR , t ( "Fill all fields, please!" ) , NAME , name , EMAIL , email ) , resp ) ;
if ( ! pass . equals ( pass_repeat ) ) return loadTemplate ( REGISTER , Map . of ( ERROR , t ( "Passwords do not match!" ) , NAME , name , EMAIL , email ) , resp ) ;
if ( Util . simplePassword ( pass ) ) return loadTemplate ( REGISTER , Map . of ( ERROR , t ( "Password to short or to simple!" ) , NAME , name , EMAIL , email ) , resp ) ;
pass_repeat = = null | | pass_repeat . isBlank ( ) ) return loadTemplate ( REGISTER , Map . of ( ERROR , t ( "Fill all fields, please!" ) , NAME , name , EMAIL , email ) , resp ) ;
if ( ! pass . equals ( pass_repeat ) ) return loadTemplate ( REGISTER , Map . of ( ERROR , t ( "Passwords do not match!" ) , NAME , name , EMAIL , email ) , resp ) ;
if ( Util . simplePassword ( pass ) ) return loadTemplate ( REGISTER , Map . of ( ERROR , t ( "Password to short or to simple!" ) , NAME , name , EMAIL , email ) , resp ) ;
var firstUser = false ;
try {
@ -474,7 +503,6 @@ public class Web extends TemplateServlet {
@@ -474,7 +503,6 @@ public class Web extends TemplateServlet {
return t ( "Failed to access user database: {}" , e . getMessage ( ) ) ;
}
try {
var user = User . create ( email , name , pass ) ;
if ( firstUser ) user . addPermission ( PERMISSION_ADMIN | User . PERMISSION_CREATE_LISTS ) ;
@ -486,6 +514,33 @@ public class Web extends TemplateServlet {
@@ -486,6 +514,33 @@ public class Web extends TemplateServlet {
}
}
private String resetPassword ( HttpServletRequest req , HttpServletResponse resp ) {
var email = req . getParameter ( "email" ) ;
if ( email = = null ) return loadTemplate ( "login" , Map . of ( "error" , t ( "Missing email address!" ) ) , resp ) ;
if ( ! Util . isEmail ( email ) ) return loadTemplate ( "login" , Map . of ( "error" , t ( "'{}' is not a valid email address!" , email ) ) , resp ) ;
try {
var user = User . load ( email ) ;
if ( user ! = null ) {
MailingList list = ListMember . listsOf ( user ) . stream ( ) . map ( ListMember : : list ) . filter ( ml - > ml . hasState ( STATE_ENABLED ) ) . findAny ( ) . orElse ( null ) ;
if ( list = = null ) list = MailingList . listActive ( ) . stream ( ) . findAny ( ) . orElse ( null ) ;
if ( list = = null ) throw new NullPointerException ( "no active List found!" ) ;
String token = user . generateToken ( ) ;
var host = Arrays . stream ( req . getRequestURL ( ) . toString ( ) . split ( "/" ) ) . filter ( s - > ! s . isEmpty ( ) & & ! s . startsWith ( "http" ) ) . findFirst ( ) . orElse ( "unknwon host" ) ;
var link = req . getRequestURL ( ) . toString ( ) + "?token=" + token ;
var subject = t ( "Reset your password at {}" , host ) ;
var text = t ( "Somebody asked to reset your account password at {}.\n\nIf that was you, please open the following link in your web browser:\n\n{}\n\nIf you did not expect this email, please ignore it." , host , link ) ;
list . sendPasswordReset ( email , subject , text ) ;
return loadTemplate ( "reset_link_sent" , null , resp ) ;
}
} catch ( Exception e ) {
return loadTemplate ( Util . getPath ( req ) , Map . of ( EMAIL , email , ERROR , "Failed to send reset email: " + e . getMessage ( ) ) , resp ) ;
}
return null ;
}
private String subscribe ( HttpServletRequest req , HttpServletResponse resp ) {
var name = req . getParameter ( NAME ) ;
var email = req . getParameter ( EMAIL ) ;
@ -517,6 +572,7 @@ public class Web extends TemplateServlet {
@@ -517,6 +572,7 @@ public class Web extends TemplateServlet {
int code = cause . getErrorCode ( ) ;
if ( code = = PRIMARY_KEY_CONSTRAINT ) try { // user already exists
user = User . loadUser ( email , pass ) ;
req . getSession ( ) . setAttribute ( "user" , user ) ;
skipConfirmation = pass ! = null ; // subscription with email address already known to database
// success → subscribe
} catch ( InvalidKeyException | SQLException e ) {
@ -555,11 +611,9 @@ public class Web extends TemplateServlet {
@@ -555,11 +611,9 @@ public class Web extends TemplateServlet {
}
}
private String unsubscribe ( HttpServletRequest req , HttpServletResponse resp ) {
private String unsubscribe ( HttpServletRequest req , HttpServletResponse resp , boolean isGet ) {
var data = new HashMap < String , Object > ( ) ;
var user = getSession User( req ) ;
var user = Util . getUser ( req ) ;
var email = req . getParameter ( EMAIL ) ;
var list = Util . getMailingList ( req ) ;
data . put ( EMAIL , email ) ;
@ -568,6 +622,7 @@ public class Web extends TemplateServlet {
@@ -568,6 +622,7 @@ public class Web extends TemplateServlet {
data . put ( ERROR , t ( "No list provided by form data!" ) ) ;
return loadTemplate ( UNSUBSCRIBE , data , resp ) ;
} else data . put ( LIST , list . email ( ) ) ;
if ( isGet ) return loadTemplate ( UNSUBSCRIBE , data , resp ) ;
if ( user = = null ) {
if ( email = = null | | email . isBlank ( ) ) {
data . put ( ERROR , t ( "Email is required for list un-subscription!" ) ) ;
@ -607,7 +662,6 @@ public class Web extends TemplateServlet {
@@ -607,7 +662,6 @@ public class Web extends TemplateServlet {
LOG . warn ( "Problem during unscubsription of {} from {}:" , user . email ( ) , list . email ( ) , e ) ;
data . put ( ERROR , "Failed to unsubscribe!" ) ;
return loadTemplate ( UNSUBSCRIBE , data , resp ) ;
}
}
}