Add documentation to server package
This commit is contained in:
parent
b5e8b96f65
commit
54b1eb7299
9 changed files with 325 additions and 22 deletions
|
@ -17,6 +17,9 @@ import java.net.Socket;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Server client connection management
|
||||
*/
|
||||
public class ConnectedClient implements Runnable {
|
||||
private static int idCounter = 0;
|
||||
private final int id = idCounter++;
|
||||
|
@ -26,6 +29,12 @@ public class ConnectedClient implements Runnable {
|
|||
private ObjectInputStream in;
|
||||
private UserModel user;
|
||||
|
||||
/**
|
||||
* Create a client connection management
|
||||
* @param server server socket
|
||||
* @param socket client socket
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
ConnectedClient(Server server, Socket socket) throws IOException {
|
||||
this.server = server;
|
||||
this.socket = socket;
|
||||
|
@ -33,12 +42,31 @@ public class ConnectedClient implements Runnable {
|
|||
this.in = new ObjectInputStream(socket.getInputStream());
|
||||
}
|
||||
|
||||
public Message sendMessage(Message message) throws IOException {
|
||||
out.writeObject(new Command(CommandType.message, List.of(message)));
|
||||
/**
|
||||
* Send command to the client
|
||||
* @param cmd the command
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
private void send(Command cmd) throws IOException {
|
||||
out.writeObject(cmd);
|
||||
out.flush();
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a message to the client
|
||||
* @param message the message
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
public void sendMessage(Message message) throws IOException {
|
||||
send(new Command(CommandType.message, List.of(message)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Client command handler
|
||||
* @param command the command
|
||||
* @throws IOException if a connection error occur with the client
|
||||
* @throws ChatException chat runtime error send to the user
|
||||
*/
|
||||
private void actionCommand(Command command) throws IOException, ChatException {
|
||||
CommandType type = command.getType();
|
||||
if (user == null && type != CommandType.login)
|
||||
|
@ -53,7 +81,14 @@ public class ConnectedClient implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private void commandLogin(Command cmd) throws IOException, ChatException {
|
||||
/**
|
||||
* Handel's user authentication
|
||||
* ToDo avoid re auth
|
||||
* @param cmd the login command
|
||||
* @throws IOException if a connection error occur with the client
|
||||
* @throws LoginInvalid if the user credentials are invalid
|
||||
*/
|
||||
private void commandLogin(Command cmd) throws IOException, LoginInvalid {
|
||||
List<Object> args = cmd.getArgs();
|
||||
|
||||
String username = (String) args.get(0);
|
||||
|
@ -71,13 +106,17 @@ public class ConnectedClient implements Runnable {
|
|||
else if (!user.checkPassword(password))
|
||||
throw new LoginInvalid("Password invalid");
|
||||
else {
|
||||
out.writeObject(new Command(CommandType.login, null));
|
||||
out.flush();
|
||||
send(new Command(CommandType.login, null));
|
||||
this.user = user;
|
||||
System.out.println("Client "+user.getUsername()+" is connected !");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Message receive handler
|
||||
* @param cmd the message command
|
||||
* @throws NotInChannel if the user is not in the channel
|
||||
*/
|
||||
private void commandMessage(Command cmd) throws NotInChannel {
|
||||
Message msg = (Message) cmd.getArgs().get(0);
|
||||
msg.setSender(this.user);
|
||||
|
@ -89,16 +128,27 @@ public class ConnectedClient implements Runnable {
|
|||
server.broadcastMessage(msg, id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Command user list handler
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
private void commandList() throws IOException {
|
||||
out.writeObject(new Command(CommandType.list, Collections.singletonList(server.getUsers())));
|
||||
out.flush();
|
||||
send(new Command(CommandType.list, Collections.singletonList(server.getUsers())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Command channel list handler
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
private void commandListChannels() throws IOException {
|
||||
out.writeObject(new Command(CommandType.listChannels, Collections.singletonList((List<Channel>)(List<?>) ChannelModel.getAll())));
|
||||
out.flush();
|
||||
send(new Command(CommandType.listChannels, Collections.singletonList((List<Channel>)(List<?>) ChannelModel.getAll())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Channel join command handler
|
||||
* @param cmd the join command
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
private void commandJoin(Command cmd) throws IOException {
|
||||
String name = (String) cmd.getArgs().get(0);
|
||||
ChannelModel chan = ChannelModel.get(name);
|
||||
|
@ -110,12 +160,14 @@ public class ConnectedClient implements Runnable {
|
|||
if (!chan.have(user))
|
||||
chan.addUser(user);
|
||||
|
||||
out.writeObject(new Command(CommandType.join, List.of((Channel) chan)));
|
||||
out.flush();
|
||||
send(new Command(CommandType.join, List.of((Channel) chan)));
|
||||
|
||||
server.broadcastMessage(new Message(chan, Server.getServerUser(), user.getUsername()+" joined the channel !"), -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Man thread of user connection
|
||||
*/
|
||||
public void run() {
|
||||
try {
|
||||
while (true) {
|
||||
|
@ -143,6 +195,10 @@ public class ConnectedClient implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close connection to client
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
public void closeClient() throws IOException {
|
||||
if (in != null)
|
||||
in.close();
|
||||
|
@ -150,10 +206,18 @@ public class ConnectedClient implements Runnable {
|
|||
socket.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the client connection id
|
||||
* @return connection id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user
|
||||
* @return the user
|
||||
*/
|
||||
public User getUser() {
|
||||
return user;
|
||||
}
|
||||
|
|
|
@ -7,15 +7,28 @@ import java.io.IOException;
|
|||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
|
||||
/**
|
||||
* Server connection manager
|
||||
*/
|
||||
public class Connection implements Runnable {
|
||||
private final Server server;
|
||||
private final ServerSocket serverSocket;
|
||||
|
||||
/**
|
||||
* Create a server connection manager
|
||||
* @param server the server
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
Connection(Server server) throws IOException {
|
||||
this.server = server;
|
||||
this.serverSocket = initSSL();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the SSL client WebSocket connection
|
||||
* @return the socket
|
||||
* @throws IOException if a connection error occur with the client
|
||||
*/
|
||||
private SSLServerSocket initSSL() throws IOException {
|
||||
|
||||
SSLContext ctx = ChatSSL.getSSLContext();
|
||||
|
@ -29,6 +42,9 @@ public class Connection implements Runnable {
|
|||
return sslListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main thread
|
||||
*/
|
||||
public void run() {
|
||||
while (true) {
|
||||
Socket clientSocket;
|
||||
|
|
|
@ -10,10 +10,16 @@ import java.sql.DriverManager;
|
|||
import java.sql.SQLException;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Server database management
|
||||
*/
|
||||
public class Database {
|
||||
private static Database database;
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
* Create database object and establish connection
|
||||
*/
|
||||
private Database() {
|
||||
Database.database = this;
|
||||
try {
|
||||
|
@ -26,7 +32,12 @@ public class Database {
|
|||
init();
|
||||
}
|
||||
|
||||
private String[] getCredentials() throws NullPointerException, IOException {
|
||||
/**
|
||||
* Get database credentials
|
||||
* @return credentials
|
||||
* @throws IOException when an error occur with the configuration file
|
||||
*/
|
||||
private String[] getCredentials() throws IOException {
|
||||
Properties props = new Properties();
|
||||
File f = new File("server.properties");
|
||||
|
||||
|
@ -43,6 +54,12 @@ public class Database {
|
|||
return new String[]{props.getProperty("db.url"), props.getProperty("db.user"), props.getProperty("db.password")};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database connection
|
||||
* @return the connection
|
||||
* @throws SQLException if a connection error occur with the database
|
||||
* @throws IOException when failed to get the credentials
|
||||
*/
|
||||
private Connection getConnexion() throws SQLException, IOException {
|
||||
String[] credentials = getCredentials();
|
||||
|
||||
|
@ -56,16 +73,27 @@ public class Database {
|
|||
return DriverManager.getConnection(credentials[0], credentials[1], credentials[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database connection
|
||||
* @return the connection
|
||||
*/
|
||||
public Connection getConnection() {
|
||||
return connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database instance
|
||||
* @return the database
|
||||
*/
|
||||
public static Database getDatabase() {
|
||||
if (Database.database == null)
|
||||
return new Database();
|
||||
return Database.database;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the database tables from models
|
||||
*/
|
||||
private void init() {
|
||||
UserModel.generateTable();
|
||||
ChannelModel.generateTable();
|
||||
|
|
|
@ -2,6 +2,9 @@ package fr.univ.lyon1.server;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Main server program
|
||||
*/
|
||||
public class MainServer {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
|
@ -16,6 +19,9 @@ public class MainServer {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Help usage for arguments
|
||||
*/
|
||||
private static void printUsage() {
|
||||
System.out.println("java server.Server <port>");
|
||||
System.out.println("\t<port>: server's port");
|
||||
|
|
|
@ -9,11 +9,19 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Main server management
|
||||
*/
|
||||
public class Server {
|
||||
private final int port;
|
||||
private List<ConnectedClient> clients = new ArrayList<>();
|
||||
private static User serverUser = new User(UUID.fromString("3539b6bf-5eb3-41d4-893f-cbf0caa9ca74"), "server");
|
||||
private final List<ConnectedClient> clients = new ArrayList<>();
|
||||
private static final User serverUser = new User(UUID.fromString("3539b6bf-5eb3-41d4-893f-cbf0caa9ca74"), "server");
|
||||
|
||||
/**
|
||||
* Create server
|
||||
* @param port the listening port
|
||||
* @throws IOException if a connection error occur
|
||||
*/
|
||||
Server(int port) throws IOException {
|
||||
this.port = port;
|
||||
Database.getDatabase();
|
||||
|
@ -21,12 +29,20 @@ public class Server {
|
|||
connection.start();
|
||||
}
|
||||
|
||||
public ConnectedClient addClient(ConnectedClient newClient) {
|
||||
/**
|
||||
* Add client handler
|
||||
* @param newClient the client
|
||||
*/
|
||||
public void addClient(ConnectedClient newClient) {
|
||||
clients.add(newClient);
|
||||
return newClient;
|
||||
}
|
||||
|
||||
public int broadcastMessage(Message message, int id) {
|
||||
/**
|
||||
* Send a message to all clients
|
||||
* @param message the message
|
||||
* @param id the sender id
|
||||
*/
|
||||
public void broadcastMessage(Message message, int id) {
|
||||
List<UUID> users = UserChannelModel.getUsers(message.getChannel()).stream().map(User::getUUID).toList();
|
||||
for (ConnectedClient client : clients.stream().filter(connectedClient -> users.contains(connectedClient.getUser().getUUID())).toList()) {
|
||||
if (id == -1 || client.getId() != id)
|
||||
|
@ -37,10 +53,13 @@ public class Server {
|
|||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
public ConnectedClient disconnectedClient(ConnectedClient client) {
|
||||
/**
|
||||
* Close client connection
|
||||
* @param client the client connection manager
|
||||
*/
|
||||
public void disconnectedClient(ConnectedClient client) {
|
||||
try {
|
||||
client.closeClient();
|
||||
} catch (IOException e) {
|
||||
|
@ -51,17 +70,28 @@ public class Server {
|
|||
clients.remove(client);
|
||||
|
||||
System.out.println("Client "+client.getId()+" disconnected");
|
||||
return client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server listening port
|
||||
* @return the server listening port
|
||||
*/
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the server user
|
||||
* @return the server user
|
||||
*/
|
||||
public static User getServerUser() {
|
||||
return serverUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of connection client to the server
|
||||
* @return list of connected client to the server
|
||||
*/
|
||||
public List<User> getUsers() {
|
||||
return clients.stream().map(ConnectedClient::getUser).toList();
|
||||
}
|
||||
|
|
|
@ -10,26 +10,53 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Database model of a channel
|
||||
*/
|
||||
public class ChannelModel extends Channel implements Model {
|
||||
private static final String TABLE_NAME = "Channel";
|
||||
|
||||
/**
|
||||
* Create a new channel from a name
|
||||
* @param name the name
|
||||
*/
|
||||
public ChannelModel(String name) {
|
||||
super(name);
|
||||
create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Model from existing channel
|
||||
* @param uuid
|
||||
* @param name
|
||||
*/
|
||||
private ChannelModel(UUID uuid, String name) {
|
||||
super(uuid, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a user to the channel
|
||||
* ToDo on user reconnection rejoin all connected channels
|
||||
* @param user the user
|
||||
*/
|
||||
public void addUser(User user) {
|
||||
new UserChannelModel(user, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user is in this channel
|
||||
* @param user the user
|
||||
* @return true if is else false
|
||||
*/
|
||||
public boolean have(User user) {
|
||||
return UserChannelModel.exist(user, this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel from a name
|
||||
* @param name the name
|
||||
* @return the channel or null if not found
|
||||
*/
|
||||
public static ChannelModel get(String name) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT UUID FROM "+TABLE_NAME+" WHERE name = ?");
|
||||
|
@ -46,6 +73,11 @@ public class ChannelModel extends Channel implements Model {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a channel from the unique id
|
||||
* @param uuid the unique id
|
||||
* @return the channel or null if not found
|
||||
*/
|
||||
public static ChannelModel get(UUID uuid) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT * FROM "+TABLE_NAME+" WHERE UUID = ?");
|
||||
|
@ -65,6 +97,10 @@ public class ChannelModel extends Channel implements Model {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all channels
|
||||
* @return a list of channels
|
||||
*/
|
||||
public static List<ChannelModel> getAll() {
|
||||
List<ChannelModel> channels = new ArrayList<>();
|
||||
try {
|
||||
|
@ -84,6 +120,10 @@ public class ChannelModel extends Channel implements Model {
|
|||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check of the channel exists in the database
|
||||
* @return true if the channel exists else false
|
||||
*/
|
||||
private boolean exist() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT UUID FROM "+TABLE_NAME+" WHERE UUID = ?");
|
||||
|
@ -96,6 +136,10 @@ public class ChannelModel extends Channel implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the channel in the database
|
||||
* @return true if the register is successful else false
|
||||
*/
|
||||
private boolean create() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("INSERT INTO "+TABLE_NAME+" (UUID, name) VALUES (?, ?)");
|
||||
|
@ -108,6 +152,10 @@ public class ChannelModel extends Channel implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the channel in the database
|
||||
* @return true if the update is successful else false
|
||||
*/
|
||||
public boolean save() {
|
||||
if (!exist())
|
||||
return create();
|
||||
|
@ -122,6 +170,9 @@ public class ChannelModel extends Channel implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the channel model table in the database
|
||||
*/
|
||||
public static void generateTable() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS "+TABLE_NAME+" ( UUID varchar(40) primary key, name varchar(16) unique )");
|
||||
|
|
|
@ -2,7 +2,9 @@ package fr.univ.lyon1.server.models;
|
|||
|
||||
import fr.univ.lyon1.server.Database;
|
||||
|
||||
|
||||
/**
|
||||
* Base model of a database type
|
||||
*/
|
||||
public interface Model {
|
||||
Database database = Database.getDatabase();
|
||||
}
|
||||
|
|
|
@ -10,12 +10,20 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Database model of the relation between user and channel
|
||||
*/
|
||||
public class UserChannelModel implements Model {
|
||||
private User user;
|
||||
private Channel channel;
|
||||
|
||||
private static final String TABLE_NAME = "UserChannel";
|
||||
|
||||
/**
|
||||
* Create a user channel relation and save it in database if necessary
|
||||
* @param user
|
||||
* @param channel
|
||||
*/
|
||||
public UserChannelModel(User user, Channel channel) {
|
||||
this.user = user;
|
||||
this.channel = channel;
|
||||
|
@ -24,6 +32,11 @@ public class UserChannelModel implements Model {
|
|||
create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of users in a specific channel
|
||||
* @param channel the channel
|
||||
* @return the list of users
|
||||
*/
|
||||
public static List<User> getUsers(Channel channel) {
|
||||
List<User> users = new ArrayList<>();
|
||||
|
||||
|
@ -42,6 +55,11 @@ public class UserChannelModel implements Model {
|
|||
return users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of channel where a user is in
|
||||
* @param user the user
|
||||
* @return the list of channels
|
||||
*/
|
||||
public static List<Channel> getChannels(User user) {
|
||||
List<Channel> channels = new ArrayList<>();
|
||||
|
||||
|
@ -60,6 +78,12 @@ public class UserChannelModel implements Model {
|
|||
return channels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the relation exists in the database
|
||||
* @param user the user
|
||||
* @param channel the channel
|
||||
* @return true if present else false
|
||||
*/
|
||||
public static boolean exist(User user, Channel channel) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT 1 FROM "+TABLE_NAME+" WHERE userUUID = ? AND channelUUID = ?");
|
||||
|
@ -73,6 +97,10 @@ public class UserChannelModel implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the ration in the database
|
||||
* @return true if succeed else false
|
||||
*/
|
||||
private boolean create() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("INSERT INTO "+TABLE_NAME+" (userUUID, channelUUID) VALUES (?, ?)");
|
||||
|
@ -85,6 +113,9 @@ public class UserChannelModel implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the user channel relation model table in the database
|
||||
*/
|
||||
public static void generateTable() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS "+TABLE_NAME+" (userUUID varchar(40) not null references User(UUID), channelUUID varchar(40) not null references Channel(UUID), PRIMARY KEY (userUUID, channelUUID))");
|
||||
|
|
|
@ -17,6 +17,9 @@ import java.util.UUID;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Database model of a user type
|
||||
*/
|
||||
public class UserModel extends User implements Model {
|
||||
private String passwordHash;
|
||||
|
||||
|
@ -28,17 +31,33 @@ public class UserModel extends User implements Model {
|
|||
private static final Pattern LAYOUT = Pattern.compile("\\$1\\$(\\d\\d?)\\$(.{43})");
|
||||
private static final String TABLE_NAME = "User";
|
||||
|
||||
/**
|
||||
* Create a new user
|
||||
* @param username the username
|
||||
* @param password the password
|
||||
*/
|
||||
public UserModel(String username, String password) {
|
||||
super(username);
|
||||
setPassword(password);
|
||||
create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an existing user
|
||||
* @param uuid the unique id
|
||||
* @param username the username
|
||||
* @param passwordHash the password hash
|
||||
*/
|
||||
private UserModel(UUID uuid, String username, String passwordHash) {
|
||||
super(uuid, username);
|
||||
this.passwordHash = passwordHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user from his username
|
||||
* @param username the username
|
||||
* @return the user of null if not found
|
||||
*/
|
||||
public static UserModel get(String username) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT UUID FROM "+TABLE_NAME+" WHERE username = ?");
|
||||
|
@ -55,6 +74,11 @@ public class UserModel extends User implements Model {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a user from his unique id
|
||||
* @param uuid the unique id
|
||||
* @return the user of null if not found
|
||||
*/
|
||||
public static UserModel get(UUID uuid) {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT * FROM "+TABLE_NAME+" WHERE UUID = ?");
|
||||
|
@ -75,6 +99,10 @@ public class UserModel extends User implements Model {
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user exists in the database
|
||||
* @return true if present else false
|
||||
*/
|
||||
private boolean exist() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("SELECT UUID FROM "+TABLE_NAME+" WHERE UUID = ?");
|
||||
|
@ -87,6 +115,10 @@ public class UserModel extends User implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a user in the database
|
||||
* @return true if the update is successful else false
|
||||
*/
|
||||
private boolean create() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("INSERT INTO "+TABLE_NAME+" (UUID, username, password) VALUES (?, ?, ?)");
|
||||
|
@ -100,6 +132,10 @@ public class UserModel extends User implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a user in the database
|
||||
* @return true if the update is successful else false
|
||||
*/
|
||||
public boolean save() {
|
||||
if (!exist())
|
||||
return create();
|
||||
|
@ -116,6 +152,9 @@ public class UserModel extends User implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the channel model table in the database
|
||||
*/
|
||||
public static void generateTable() {
|
||||
try {
|
||||
PreparedStatement ps = database.getConnection().prepareStatement("CREATE TABLE IF NOT EXISTS "+TABLE_NAME+" ( UUID varchar(40) primary key, username varchar(16) unique, password varchar(256) )");
|
||||
|
@ -125,41 +164,77 @@ public class UserModel extends User implements Model {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password hash
|
||||
* @return password hash
|
||||
*/
|
||||
public String getPasswordHash() {
|
||||
return passwordHash;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the password as a hash
|
||||
* @param password the plain password
|
||||
*/
|
||||
public void setPassword(String password) {
|
||||
// Generate a new salt
|
||||
byte[] passwordSalt = new byte[SIZE / 8];
|
||||
random.nextBytes(passwordSalt);
|
||||
|
||||
// Generate the hash from the password and the salt
|
||||
byte[] dk = pbkdf2(password.toCharArray(), passwordSalt, 1 << COST);
|
||||
byte[] hash = new byte[passwordSalt.length + dk.length];
|
||||
System.arraycopy(passwordSalt, 0, hash, 0, passwordSalt.length);
|
||||
System.arraycopy(dk, 0, hash, passwordSalt.length, dk.length);
|
||||
Base64.Encoder enc = Base64.getUrlEncoder().withoutPadding();
|
||||
|
||||
// Format the password hash
|
||||
passwordHash = ID + COST + '$' + enc.encodeToString(hash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a password against the password hash
|
||||
* @param password the plain password to test
|
||||
* @return true if the password match else false
|
||||
*/
|
||||
public boolean checkPassword(String password) {
|
||||
// Check the password hash integrity
|
||||
Matcher m = LAYOUT.matcher(passwordHash);
|
||||
if (!m.matches())
|
||||
throw new IllegalArgumentException("Invalid token format");
|
||||
|
||||
// Gather hash data
|
||||
int iterations = iterations(Integer.parseInt(m.group(1)));
|
||||
byte[] hash = Base64.getUrlDecoder().decode(m.group(2));
|
||||
byte[] salt = Arrays.copyOfRange(hash, 0, SIZE / 8);
|
||||
byte[] check = pbkdf2(password.toCharArray(), salt, iterations);
|
||||
|
||||
// Check if the password match the hash
|
||||
int zero = 0;
|
||||
for (int idx = 0; idx < check.length; ++idx)
|
||||
zero |= hash[salt.length + idx] ^ check[idx];
|
||||
|
||||
return zero == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the hash iteration
|
||||
* @param cost the has cost
|
||||
* @return the iterations
|
||||
*/
|
||||
private static int iterations(int cost) {
|
||||
if ((cost < 0) || (cost > 30))
|
||||
throw new IllegalArgumentException("cost: " + cost);
|
||||
return 1 << cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the password encryption
|
||||
* @param password the plain password
|
||||
* @param salt the salt
|
||||
* @param iterations the hash iterations
|
||||
* @return the password encoded hash
|
||||
*/
|
||||
private static byte[] pbkdf2(char[] password, byte[] salt, int iterations) {
|
||||
KeySpec spec = new PBEKeySpec(password, salt, iterations, SIZE);
|
||||
try {
|
||||
|
|
Reference in a new issue