implemented resetting passwords
Signed-off-by: Stephan Richter <s.richter@srsoftware.de>
This commit is contained in:
@@ -10,6 +10,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
|
||||
import static de.srsoftware.widerhall.Constants.*;
|
||||
import static de.srsoftware.widerhall.Util.t;
|
||||
import static de.srsoftware.widerhall.data.MailingList.HOLD_TIME;
|
||||
|
||||
@@ -19,6 +20,7 @@ import static de.srsoftware.widerhall.data.MailingList.HOLD_TIME;
|
||||
*/
|
||||
public class Database {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Database.class);
|
||||
private static final String DB_VERSION = "db_version";
|
||||
private static Database singleton = null; // we only need one db handle ever. This will be it.
|
||||
private final Connection conn; // the actual db connection handle within the singleton
|
||||
|
||||
@@ -295,6 +297,14 @@ public class Database {
|
||||
return this;
|
||||
}
|
||||
|
||||
private void createVersionTable() throws SQLException {
|
||||
var sql = "CREATE TABLE %s (%s %s NOT NULL PRIMARY KEY);".formatted(DB_VERSION,DB_VERSION,INT);
|
||||
var db = Database.open();
|
||||
db.query(sql).compile().run();
|
||||
sql = "INSERT INTO %s VALUES (1)".formatted(DB_VERSION);
|
||||
db.query(sql).compile().run();
|
||||
}
|
||||
|
||||
private boolean columnExists(String tableName, String columnName) throws SQLException {
|
||||
var rs = Database.open().select("pragma_table_info('"+tableName+"')","COUNT(*) AS num").where("name",columnName).compile().exec();
|
||||
try {
|
||||
@@ -357,6 +367,7 @@ public class Database {
|
||||
try {
|
||||
singleton = new Database(DriverManager.getConnection(url));
|
||||
singleton.assertTables(); // must not be concatenated to exception above (assertTables accesses singleton)!
|
||||
singleton.update202405();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
@@ -373,6 +384,10 @@ public class Database {
|
||||
return new Request(sql);
|
||||
}
|
||||
|
||||
public Request query(String sql) {
|
||||
return new Request(new StringBuilder(sql));
|
||||
}
|
||||
|
||||
/**
|
||||
* create a SELECT [flields] FROM [table] request.
|
||||
* If no fields are supplied, a request in the form SELECT * FROM [table] will be generated.
|
||||
@@ -390,7 +405,6 @@ public class Database {
|
||||
return new Request(sql.append(" FROM ").append(tableName));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* check, whether a table with the provided name exists
|
||||
* @param tbName
|
||||
@@ -420,4 +434,12 @@ public class Database {
|
||||
public Request update(String tableName) {
|
||||
return new Request(new StringBuilder("UPDATE ").append(tableName));
|
||||
}
|
||||
|
||||
private Database update202405() throws SQLException {
|
||||
if (!tableExists(Database.DB_VERSION)) {
|
||||
createVersionTable();
|
||||
User.addTokenColumn();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,8 +49,8 @@ public class MailingList implements MessageHandler, ProblemListener {
|
||||
private static final String SMTP_PASS = "smtp_pass";
|
||||
public static final String TABLE_NAME = "Lists";
|
||||
private static final int STATE_PENDING = 0;
|
||||
private static final int STATE_ENABLED = 1; // do we process incoming messages?
|
||||
private static final int STATE_PUBLIC = 2; // can guests see this ML?
|
||||
public static final int STATE_ENABLED = 1; // do we process incoming messages?
|
||||
public static final int STATE_PUBLIC = 2; // can guests see this ML?
|
||||
public static final int STATE_FORWARD_FROM = 4; // set original sender as FROM when forwarding?
|
||||
public static final int STATE_FORWARD_ATTACHED = 8; // forward messages as attachment?
|
||||
public static final int STATE_HIDE_RECEIVERS = 16; // send using BCC receivers
|
||||
@@ -283,6 +283,18 @@ public class MailingList implements MessageHandler, ProblemListener {
|
||||
return hasState(STATE_OPEN_FOR_GUESTS|STATE_OPEN_FOR_SUBSCRIBERS);
|
||||
}
|
||||
|
||||
public static List<MailingList> listActive() {
|
||||
try {
|
||||
var list = new ArrayList<MailingList>();
|
||||
var rs = Database.open().select(TABLE_NAME).where(STATE+" % 2",1).compile().exec();
|
||||
while (rs.next()) list.add(MailingList.from(rs));
|
||||
return list;
|
||||
} catch (SQLException e){
|
||||
LOG.debug("Failed to load active MailingLists");
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a ML object by it's identifying email address.
|
||||
* This is a cached method: if the ML has been loaded before, the already-loaded object will be returned.
|
||||
@@ -308,6 +320,7 @@ public class MailingList implements MessageHandler, ProblemListener {
|
||||
return ml;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a ML object by it's identifying email address.
|
||||
* This is a cached method: if the ML has been loaded before, the already-loaded object will be returned.
|
||||
@@ -679,6 +692,10 @@ public class MailingList implements MessageHandler, ProblemListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void sendPasswordReset(String email, String subject,String text) throws MessagingException, UnsupportedEncodingException {
|
||||
smtp.send(email(),name(),email,subject,text);
|
||||
}
|
||||
|
||||
protected SmtpClient smtp(){
|
||||
return smtp;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import java.security.InvalidKeyException;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static de.srsoftware.widerhall.Constants.*;
|
||||
@@ -20,10 +21,11 @@ public class User {
|
||||
public static final int PERMISSION_ADMIN = 1;
|
||||
public static final int PERMISSION_CREATE_LISTS = 2;
|
||||
public static final String HASHED_PASS = "hashedPassword";
|
||||
public static final String RESET_TOKEN = "resetToken";
|
||||
public static final String SALT = "salt";
|
||||
private static final HashMap<String,User> users = new HashMap<>();
|
||||
|
||||
private String email, salt, hashedPass, name;
|
||||
private String email, salt, hashedPass, name, token;
|
||||
private int permissions;
|
||||
|
||||
/**
|
||||
@@ -34,11 +36,12 @@ public class User {
|
||||
* @param hashedPass
|
||||
* @param permissions
|
||||
*/
|
||||
public User(String email, String name, String salt, String hashedPass, int permissions) {
|
||||
public User(String email, String name, String salt, String hashedPass, String token, int permissions) {
|
||||
this.email = email.toLowerCase();
|
||||
this.name = name;
|
||||
this.salt = salt;
|
||||
this.hashedPass = hashedPass;
|
||||
this.token = token;
|
||||
this.permissions = permissions;
|
||||
}
|
||||
|
||||
@@ -81,6 +84,21 @@ public class User {
|
||||
.run();
|
||||
}
|
||||
|
||||
public static void addTokenColumn() throws SQLException {
|
||||
String sql = "ALTER TABLE %s ADD COLUMN %s %s;".formatted(TABLE_NAME,RESET_TOKEN,VARCHAR);
|
||||
Database.open().query(sql).compile().run();
|
||||
}
|
||||
|
||||
public static User byToken(String token) throws SQLException {
|
||||
if (token == null || token.isBlank()) return null;
|
||||
var rs = Database.open().select(TABLE_NAME).where(RESET_TOKEN,token).compile().exec();
|
||||
try {
|
||||
if (rs.next()) return User.from(rs);
|
||||
return null;
|
||||
} finally {
|
||||
rs.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user object by hashing it's password and storing user data, salt and hashed password to the db.
|
||||
@@ -96,10 +114,10 @@ public class User {
|
||||
String salt = null;
|
||||
String hashedPass = null;
|
||||
if (password != null) {
|
||||
salt = Util.sha256(email + name + LocalDate.now());
|
||||
salt = Util.sha256(email + LocalDateTime.now() + name);
|
||||
hashedPass = Util.sha256(password + salt);
|
||||
}
|
||||
return new User(email,name,salt,hashedPass,0).save();
|
||||
return new User(email,name,salt,hashedPass,null,0).save();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -115,7 +133,6 @@ public class User {
|
||||
.append(PERMISSIONS).append(" ").append(INT).append(", ")
|
||||
.append(SALT).append(" ").append(VARCHAR).append(", ")
|
||||
.append(HASHED_PASS).append(" ").append(VARCHAR)
|
||||
|
||||
.append(");");
|
||||
Database.open().query(sql).compile().run();
|
||||
}
|
||||
@@ -195,10 +212,16 @@ public class User {
|
||||
rs.getString(NAME),
|
||||
rs.getString(SALT),
|
||||
rs.getString(HASHED_PASS),
|
||||
rs.getString(RESET_TOKEN),
|
||||
rs.getInt(PERMISSIONS)));
|
||||
return user;
|
||||
}
|
||||
|
||||
public String generateToken() throws SQLException {
|
||||
token = Util.randomString(64);
|
||||
Database.open().update(TABLE_NAME).set(RESET_TOKEN,token).where(EMAIL,this.email).compile().run();
|
||||
return token;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the user identified by it's email, but only if the provided password matches.
|
||||
@@ -284,4 +307,18 @@ public class User {
|
||||
req.compile().run();
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setPassword(String newPassword) throws SQLException {
|
||||
if (newPassword != null) {
|
||||
String newSalt = Util.sha256(email + LocalDateTime.now() + name);
|
||||
String newHashedPass = Util.sha256(newPassword + newSalt);
|
||||
Database.open().update(TABLE_NAME).set(HASHED_PASS,newHashedPass).set(SALT,newSalt).where(EMAIL,email).compile().run();
|
||||
hashedPass = newHashedPass;
|
||||
salt = newSalt;
|
||||
}
|
||||
}
|
||||
|
||||
public String token() {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user