You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
287 lines
8.7 KiB
287 lines
8.7 KiB
package de.srsoftware.widerhall.data; |
|
|
|
import de.srsoftware.widerhall.Util; |
|
|
|
import java.security.InvalidKeyException; |
|
import java.sql.ResultSet; |
|
import java.sql.SQLException; |
|
import java.time.LocalDate; |
|
import java.util.*; |
|
|
|
import static de.srsoftware.widerhall.Constants.*; |
|
import static de.srsoftware.widerhall.Util.t; |
|
|
|
/** |
|
* @author Stephan Richter |
|
* This class represents User objects of the widerhall db. |
|
*/ |
|
public class User { |
|
public static final String TABLE_NAME = "Users"; |
|
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 SALT = "salt"; |
|
private static final HashMap<String,User> users = new HashMap<>(); |
|
|
|
private String email, salt, hashedPass, name; |
|
private int permissions; |
|
|
|
/** |
|
* create a new user object |
|
* @param email |
|
* @param name |
|
* @param salt |
|
* @param hashedPass |
|
* @param permissions |
|
*/ |
|
public User(String email, String name, String salt, String hashedPass, int permissions) { |
|
this.email = email.toLowerCase(); |
|
this.name = name; |
|
this.salt = salt; |
|
this.hashedPass = hashedPass; |
|
this.permissions = permissions; |
|
} |
|
|
|
/*********** field accessors ***************/ |
|
public String email() { |
|
return email; |
|
} |
|
|
|
public String hashedPassword() { |
|
return hashedPass; |
|
} |
|
|
|
public String name() { |
|
return name; |
|
} |
|
|
|
public int permissions(){ |
|
return permissions; |
|
} |
|
|
|
public String salt(){ |
|
return salt; |
|
} |
|
|
|
/************** end of field accessors ****************/ |
|
|
|
/** |
|
* Add a new permission to the current user object. |
|
* Also updates the corresponding db entry |
|
* @param newPermission |
|
* @throws SQLException |
|
*/ |
|
public void addPermission(int newPermission) throws SQLException { |
|
permissions |= newPermission; |
|
Database.open() |
|
.update(TABLE_NAME) |
|
.set(PERMISSIONS,permissions) |
|
.where(EMAIL,email()) |
|
.compile() |
|
.run(); |
|
} |
|
|
|
|
|
/** |
|
* Create a new user object by hashing it's password and storing user data, salt and hashed password to the db. |
|
* Initially, the user is created without any permissions. |
|
* @param email |
|
* @param name |
|
* @param password |
|
* @return |
|
* @throws SQLException |
|
*/ |
|
public static User create(String email, String name, String password) throws SQLException { |
|
email = email.toLowerCase(); |
|
String salt = null; |
|
String hashedPass = null; |
|
if (password != null) { |
|
salt = Util.sha256(email + name + LocalDate.now()); |
|
hashedPass = Util.sha256(password + salt); |
|
} |
|
return new User(email,name,salt,hashedPass,0).save(); |
|
} |
|
|
|
/** |
|
* create user table |
|
* @throws SQLException |
|
*/ |
|
public static void createTable() throws SQLException { |
|
var sql = new StringBuilder() |
|
.append("CREATE TABLE ").append(TABLE_NAME) |
|
.append(" (") |
|
.append(EMAIL).append(" ").append(VARCHAR).append(" NOT NULL PRIMARY KEY, ") |
|
.append(NAME).append(" ").append(VARCHAR).append(", ") |
|
.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(); |
|
} |
|
|
|
/** |
|
* Withdraw a specific permission from the user object. |
|
* Updated permission flag will be written to db. |
|
* @param newPermission |
|
* @throws SQLException |
|
*/ |
|
public void dropPermission(int newPermission) throws SQLException { |
|
permissions ^= (permissions & newPermission); |
|
Database.open().update(TABLE_NAME).set(PERMISSIONS,permissions).compile().run(); |
|
} |
|
|
|
/** |
|
* check, if User object has requested permission(s). |
|
* @param permission |
|
* @return |
|
*/ |
|
public boolean hashPermission(int permission){ |
|
return (permissions & permission) > 0; |
|
} |
|
|
|
public static User load(String email) throws SQLException { |
|
var rs = Database.open().select(TABLE_NAME).where(EMAIL,email.toLowerCase()).compile().exec(); |
|
try { |
|
if (rs.next()) { |
|
return User.from(rs); |
|
} |
|
return null; |
|
} finally { |
|
rs.close(); |
|
} |
|
} |
|
|
|
/** |
|
* Load the list of all users. Internally calls loadAll(null) |
|
* @return |
|
* @throws SQLException |
|
*/ |
|
public static List<User> loadAll() throws SQLException { |
|
return loadAll(null); |
|
} |
|
|
|
/** |
|
* Load the list of all users identified by the provided email list. |
|
* If emails is null, all users are loaded. |
|
* If emails is empty, an empty list well be returned. |
|
* @param emails |
|
* @return |
|
* @throws SQLException |
|
*/ |
|
public static List<User> loadAll(Collection<String> emails) throws SQLException { |
|
if (emails != null && emails.isEmpty()) return List.of(); |
|
var userList = new ArrayList<User>(); |
|
var query = Database.open().select(TABLE_NAME); |
|
if (emails != null) query.where(EMAIL,emails); |
|
var rs = query.compile().exec(); |
|
while (rs.next()) userList.add(User.from(rs)); |
|
Collections.sort(userList,(u1,u2)->u1.name.compareTo(u2.name)); |
|
return userList; |
|
} |
|
|
|
/** |
|
* Create a new User object from a ResultSet. |
|
* This method is cached: If a User object with an identifying email has been loaded before, the already-loaded object will be returned. |
|
* @param rs |
|
* @return |
|
* @throws SQLException |
|
*/ |
|
private static User from(ResultSet rs) throws SQLException { |
|
var email = rs.getString(EMAIL); |
|
var user = users.get(email); |
|
if (user == null) users.put(email,user = new User( |
|
rs.getString(EMAIL), |
|
rs.getString(NAME), |
|
rs.getString(SALT), |
|
rs.getString(HASHED_PASS), |
|
rs.getInt(PERMISSIONS))); |
|
return user; |
|
} |
|
|
|
|
|
/** |
|
* Loads the user identified by it's email, but only if the provided password matches. |
|
* @param email |
|
* @param password |
|
* @return |
|
* @throws InvalidKeyException |
|
* @throws SQLException |
|
*/ |
|
public static User loadUser(String email, String password) throws InvalidKeyException, SQLException { |
|
ResultSet rs = Database.open() |
|
.select(TABLE_NAME) |
|
.where(EMAIL,email.toLowerCase()) |
|
.compile() |
|
.exec(); |
|
try { |
|
if (rs.next()) { |
|
var loadedUser = User.from(rs); |
|
if (loadedUser.matching(password)) return loadedUser; |
|
} |
|
} finally { |
|
rs.close(); |
|
} |
|
|
|
throw new InvalidKeyException(); |
|
} |
|
|
|
|
|
/** |
|
* checks, if the provided password matches the User obejcts's original password by comparing hashes. |
|
* @param password |
|
* @return |
|
*/ |
|
private boolean matching(String password) { |
|
if (hashedPass == null) return password == null; |
|
return hashedPass.equals(Util.sha256(password+salt)); |
|
} |
|
|
|
|
|
/** |
|
* Checks, whether the user table is empty |
|
* @return |
|
* @throws SQLException |
|
*/ |
|
public static boolean noUsers() throws SQLException { |
|
var rs = Database.open().select(TABLE_NAME,"count(*)").compile().exec(); |
|
try { |
|
if (rs.next()) return rs.getInt(1) < 1; |
|
} finally { |
|
rs.close(); |
|
} |
|
return false; |
|
} |
|
|
|
/** |
|
* creates a readable permission list from the permission flag. |
|
* @return |
|
*/ |
|
public String permissionList(){ |
|
var list = new ArrayList<String>(); |
|
if (hashPermission(PERMISSION_ADMIN)) list.add("admin"); |
|
if (hashPermission(PERMISSION_CREATE_LISTS)) list.add("create lists"); |
|
return String.join(", ",list); |
|
} |
|
|
|
/** |
|
* creates a map containing all of the Users data but the password. |
|
* @return |
|
*/ |
|
public Map<String,String> safeMap(){ |
|
return Map.of(NAME,name,EMAIL,email,PERMISSIONS,permissionList(),PASSWORD,t(hashedPassword() == null ? "no" : "yes")); |
|
} |
|
|
|
/** |
|
* sae the current User object to the database |
|
* @return |
|
* @throws SQLException |
|
*/ |
|
private User save() throws SQLException { |
|
var req = Database.open().insertInto(TABLE_NAME).set(EMAIL,email).set(NAME,name); |
|
if (salt != null) req.set(SALT,salt); |
|
if (hashedPass != null) req.set(HASHED_PASS,hashedPass); |
|
req.compile().run(); |
|
return this; |
|
} |
|
}
|
|
|