[REPO REFACTOR]: changed to a better git repository structure with branches
This commit is contained in:
128
BYODSEC/src/main/java/net/miarma/byodsec/Byodsec.java
Normal file
128
BYODSEC/src/main/java/net/miarma/byodsec/Byodsec.java
Normal file
@@ -0,0 +1,128 @@
|
||||
package net.miarma.byodsec;
|
||||
|
||||
import com.formdev.flatlaf.themes.FlatMacDarkLaf;
|
||||
import jiconfont.icons.font_awesome.FontAwesome;
|
||||
import jiconfont.icons.google_material_design_icons.GoogleMaterialDesignIcons;
|
||||
import jiconfont.swing.IconFontSwing;
|
||||
import net.miarma.byodsec.common.ConfigManager;
|
||||
import net.miarma.byodsec.common.db.DBPopulator;
|
||||
import net.miarma.byodsec.client.ui.MainWindow;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
@SuppressWarnings("all")
|
||||
public class Byodsec {
|
||||
private static final Logger logger = LoggerFactory.getLogger(Byodsec.class);
|
||||
private static ConfigManager configManager = ConfigManager.getInstance();
|
||||
|
||||
public static void main(String[] args) {
|
||||
initConfig();
|
||||
initDB();
|
||||
initProperties();
|
||||
initJKS();
|
||||
initUI();
|
||||
}
|
||||
|
||||
private static void initConfig() {
|
||||
File configFile = configManager.getConfigFile();
|
||||
File parentDir = configFile.getParentFile();
|
||||
|
||||
if (!parentDir.exists()) {
|
||||
if (parentDir.mkdirs()) {
|
||||
logger.info("Created app directory: " + parentDir.getAbsolutePath());
|
||||
} else {
|
||||
logger.error("Failed to create app directory: " + parentDir.getAbsolutePath());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!configFile.exists()) {
|
||||
try (InputStream in = Byodsec.class.getClassLoader().getResourceAsStream("default.properties")) {
|
||||
if (in != null) {
|
||||
Files.copy(in, configFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
logger.info("Default config file created at: {}", configFile.getAbsolutePath());
|
||||
} else {
|
||||
logger.error("Resource default.properties not found!");
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.error("Error creating config file: ", e);
|
||||
}
|
||||
} else {
|
||||
logger.info("Config file already exists at: {}", configFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
configManager.loadConfig();
|
||||
}
|
||||
|
||||
private static void initDB() {
|
||||
if(DBPopulator.getInstance().isFirstRun())
|
||||
DBPopulator.getInstance().populate();
|
||||
}
|
||||
|
||||
private static void initJKS() {
|
||||
File jksFile = configManager.getJksFile();
|
||||
|
||||
if (!jksFile.exists()) {
|
||||
String path = jksFile.getAbsolutePath();
|
||||
String password = configManager.getStringProperty("jksPassword");
|
||||
|
||||
logger.info("Executing JKS initialization command...");
|
||||
ProcessBuilder pb = new ProcessBuilder(
|
||||
"keytool",
|
||||
"-genkeypair",
|
||||
"-alias", "byodsec",
|
||||
"-keyalg", "RSA",
|
||||
"-keystore", path,
|
||||
"-storepass", password,
|
||||
"-keypass", password,
|
||||
"-dname", "CN=localhost, OU=Byodsec, O=Chat33, L=Sevilla, ST=Andalucia, C=ES",
|
||||
"-validity", "365"
|
||||
);
|
||||
|
||||
pb.inheritIO();
|
||||
Process process = null;
|
||||
try {
|
||||
process = pb.start();
|
||||
process.waitFor();
|
||||
} catch (IOException e) {
|
||||
logger.error("Error executing JKS initialization command: ", e);
|
||||
throw new RuntimeException(e);
|
||||
} catch (InterruptedException e) {
|
||||
logger.error("Error executing JKS initialization command: ", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static void initProperties() {
|
||||
// especificamos mediante propiedades el KS y la contraseña
|
||||
System.setProperty("javax.net.ssl.keyStore", configManager.getJksFile().getAbsolutePath());
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", configManager.getStringProperty("jksPassword"));
|
||||
System.setProperty("javax.net.ssl.trustStore", configManager.getJksFile().getAbsolutePath());
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", configManager.getStringProperty("jksPassword"));
|
||||
}
|
||||
|
||||
private static void initUI() {
|
||||
try {
|
||||
UIManager.setLookAndFeel(new FlatMacDarkLaf());
|
||||
} catch(UnsupportedLookAndFeelException e) {
|
||||
logger.error("Error setting LaF. Falling back to default Swing looks.");
|
||||
}
|
||||
IconFontSwing.register(FontAwesome.getIconFont());
|
||||
IconFontSwing.register(GoogleMaterialDesignIcons.getIconFont());
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
new MainWindow().setVisible(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package net.miarma.byodsec.client;
|
||||
|
||||
import java.io.*;
|
||||
import java.security.*;
|
||||
import java.security.cert.CertificateException;
|
||||
|
||||
import javax.net.ssl.*;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import net.miarma.byodsec.common.ConfigManager;
|
||||
import net.miarma.byodsec.common.db.dto.LoginDTO;
|
||||
import net.miarma.byodsec.common.db.dto.MessageDTO;
|
||||
import net.miarma.byodsec.common.socket.SocketRequest;
|
||||
import net.miarma.byodsec.common.socket.SocketResponse;
|
||||
|
||||
public class ClientSocket implements AutoCloseable {
|
||||
private SSLSocket socket;
|
||||
private PrintWriter output;
|
||||
private BufferedReader input;
|
||||
private final Gson gson = new Gson();
|
||||
private final ConfigManager configManager = ConfigManager.getInstance();
|
||||
|
||||
public ClientSocket() throws IOException {
|
||||
configManager.loadConfig();
|
||||
|
||||
// inicializamos el socket SSL, la versión de TLS y los Cipher Suites a utilizar
|
||||
SSLSocketFactory factory;
|
||||
try {
|
||||
factory = initSSL();
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Error initializing SSL context.", e);
|
||||
}
|
||||
|
||||
this.socket = (SSLSocket) factory.createSocket("localhost", 6969);
|
||||
this.socket.setEnabledProtocols(new String[]{ "TLSv1.3" });
|
||||
this.socket.setEnabledCipherSuites(new String[]{
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_CHACHA20_POLY1305_SHA256"
|
||||
});
|
||||
|
||||
// buffers
|
||||
this.output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
|
||||
this.input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
}
|
||||
|
||||
private SSLSocketFactory initSSL() throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
|
||||
KeyStore ks = KeyStore.getInstance("JKS");
|
||||
ks.load(new FileInputStream(configManager.getJksFile().getAbsolutePath()),
|
||||
configManager.getStringProperty("jksPassword").toCharArray());
|
||||
|
||||
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(ks, configManager.getStringProperty("jksPassword").toCharArray());
|
||||
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
|
||||
tmf.init(ks);
|
||||
|
||||
SSLContext sslContext = SSLContext.getInstance("TLSv1.3");
|
||||
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
||||
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
|
||||
public SocketResponse login(String username, String password) throws IOException {
|
||||
SocketRequest msg = new SocketRequest(SocketRequest.Action.LOGIN, new LoginDTO(username, password));
|
||||
output.println(gson.toJson(msg));
|
||||
return gson.fromJson(input.readLine(), SocketResponse.class);
|
||||
}
|
||||
|
||||
public SocketResponse register(String username, String password) throws IOException {
|
||||
SocketRequest msg = new SocketRequest(SocketRequest.Action.REGISTER, new LoginDTO(username, password));
|
||||
output.println(gson.toJson(msg));
|
||||
return gson.fromJson(input.readLine(), SocketResponse.class);
|
||||
}
|
||||
|
||||
public SocketResponse logout(String userId) throws IOException {
|
||||
SocketRequest msg = new SocketRequest(SocketRequest.Action.LOGOUT, userId);
|
||||
output.println(gson.toJson(msg));
|
||||
return gson.fromJson(input.readLine(), SocketResponse.class);
|
||||
}
|
||||
|
||||
public SocketResponse sendMessage(MessageDTO message) throws Exception {
|
||||
SocketRequest msg = new SocketRequest(SocketRequest.Action.SEND_MESSAGE, message);
|
||||
output.println(gson.toJson(msg));
|
||||
return gson.fromJson(input.readLine(), SocketResponse.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
try {
|
||||
input.close();
|
||||
output.close();
|
||||
socket.close();
|
||||
} catch (Exception ignored) {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package net.miarma.byodsec.client.ui;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class AutoShrinkLabel extends JLabel {
|
||||
public AutoShrinkLabel(String text) {
|
||||
super(text);
|
||||
setHorizontalAlignment(SwingConstants.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
String text = getText();
|
||||
if (text == null || text.isEmpty()) {
|
||||
super.paintComponent(g);
|
||||
return;
|
||||
}
|
||||
|
||||
Insets insets = getInsets();
|
||||
int availableHeight = getHeight() - insets.top - insets.bottom;
|
||||
int availableWidth = getWidth() - insets.left - insets.right;
|
||||
|
||||
if (availableWidth <= 0 || availableHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
Graphics2D graphics2d = (Graphics2D) g.create();
|
||||
|
||||
Font font = getFont();
|
||||
int fontSize = font.getSize();
|
||||
|
||||
FontMetrics fm;
|
||||
int textWidth;
|
||||
int textHeight;
|
||||
|
||||
do {
|
||||
Font testFont = font.deriveFont((float) fontSize);
|
||||
fm = graphics2d.getFontMetrics(testFont);
|
||||
textWidth = fm.stringWidth(text);
|
||||
textHeight = fm.getHeight();
|
||||
if (textWidth <= availableWidth && textHeight <= availableHeight) {
|
||||
font = testFont;
|
||||
break;
|
||||
}
|
||||
fontSize--;
|
||||
} while (fontSize > 5);
|
||||
|
||||
graphics2d.setFont(font);
|
||||
|
||||
int x = (getWidth() - fm.stringWidth(text)) / 2;
|
||||
int y = (getHeight() + fm.getAscent() - fm.getDescent()) / 2;
|
||||
|
||||
graphics2d.drawString(text, x, y);
|
||||
graphics2d.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(200, 50);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* Created by JFormDesigner on Wed Oct 01 16:21:10 CEST 2025
|
||||
*/
|
||||
|
||||
package net.miarma.byodsec.client.ui;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.io.IOException;
|
||||
import javax.swing.*;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import jiconfont.icons.font_awesome.FontAwesome;
|
||||
import jiconfont.swing.IconFontSwing;
|
||||
import net.miarma.byodsec.common.Constants;
|
||||
|
||||
import net.miarma.byodsec.common.security.IntegrityProvider;
|
||||
import net.miarma.byodsec.common.socket.SocketResponse;
|
||||
import net.miarma.byodsec.common.socket.SocketStatus;
|
||||
import net.miarma.byodsec.common.db.dto.MessageDTO;
|
||||
import net.miarma.byodsec.common.db.entities.UserEntity;
|
||||
import net.miarma.byodsec.client.ClientSocket;
|
||||
import net.miginfocom.swing.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* @author jomaa
|
||||
*/
|
||||
public class MainWindow extends JFrame {
|
||||
public static UserEntity _loggedUser;
|
||||
private static final Logger logger = LoggerFactory.getLogger(ClientSocket.class);
|
||||
|
||||
public MainWindow() {
|
||||
initComponents();
|
||||
this.setTitle(Constants.APP_NAME);
|
||||
versionLabel.setText(Constants.APP_NAME + " v" + Constants.APP_VERSION + " by Security Team 33");
|
||||
setIcons();
|
||||
}
|
||||
|
||||
private void setIcons() {
|
||||
userLabel.setIcon(IconFontSwing.buildIcon(FontAwesome.USER, 18, Color.WHITE));
|
||||
passwordLabel.setIcon(IconFontSwing.buildIcon(FontAwesome.KEY, 18, Color.WHITE));
|
||||
loginBtn.setIcon(IconFontSwing.buildIcon(FontAwesome.SIGN_IN, 18, Color.WHITE));
|
||||
registerBtn.setIcon(IconFontSwing.buildIcon(FontAwesome.USER_PLUS, 18, Color.WHITE));
|
||||
clientLabel.setIcon(IconFontSwing.buildIcon(FontAwesome.USER_CIRCLE, 16, Color.WHITE));
|
||||
logoutBtn.setIcon(IconFontSwing.buildIcon(FontAwesome.SIGN_OUT, 16, Color.WHITE));
|
||||
}
|
||||
|
||||
private void clearLoginInputs() {
|
||||
userTextField.setText("");
|
||||
passwordTextField.setText("");
|
||||
}
|
||||
|
||||
private void clearMainInputs() {
|
||||
clientTextField.setText("");
|
||||
}
|
||||
|
||||
private void appendChatMessage(String message) {
|
||||
chatTextArea.append(message + "\r\n");
|
||||
chatTextArea.setCaretPosition(chatTextArea.getDocument().getLength());
|
||||
}
|
||||
|
||||
private void setView(String name) {
|
||||
((CardLayout)getContentPane().getLayout()).show(getContentPane(), name);
|
||||
}
|
||||
|
||||
private void loginBtn(ActionEvent e) {
|
||||
String username = userTextField.getText();
|
||||
String password = new String(passwordTextField.getPassword());
|
||||
|
||||
try (ClientSocket client = new ClientSocket()) {
|
||||
SocketResponse res = client.login(username, password);
|
||||
|
||||
if (res.getStatus() == SocketStatus.OK) {
|
||||
_loggedUser = new Gson().fromJson(
|
||||
new Gson().toJson(res.getData()),
|
||||
UserEntity.class
|
||||
);
|
||||
logger.info("El usuario '{}' ha iniciado sesión correctamente.", username);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
res.getMessage().getMessage(),
|
||||
res.getStatus().toString(),
|
||||
JOptionPane.INFORMATION_MESSAGE
|
||||
);
|
||||
clientLabel.setText("Cliente: " + _loggedUser.getUserName());
|
||||
this.setTitle(Constants.APP_NAME + " @" + _loggedUser.getUserName());
|
||||
clearLoginInputs();
|
||||
setView("mainPanel");
|
||||
} else if(res.getStatus() == SocketStatus.ALREADY_LOGGED_IN) {
|
||||
logger.info("El usuario '{}' ya tiene una sesión activa.", username);
|
||||
JOptionPane.showMessageDialog(this,
|
||||
res.getMessage() != null ? res.getMessage().getMessage() : "Respuesta sin mensaje",
|
||||
res.getStatus().toString(),
|
||||
JOptionPane.WARNING_MESSAGE
|
||||
);
|
||||
} else {
|
||||
logger.warn("Intento de login fallido para '{}': {}", username,
|
||||
res.getMessage() != null ? res.getMessage().getMessage() : "Respuesta sin mensaje");
|
||||
JOptionPane.showMessageDialog(this,
|
||||
res.getMessage() != null ? res.getMessage().getMessage() : "Respuesta sin mensaje",
|
||||
res.getStatus().toString(),
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.error("Error de conexión con el servidor al intentar loguear '{}': {}", username, ex.getMessage());
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Error de conexión con el servidor",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void registerBtn(ActionEvent e) {
|
||||
String username = userTextField.getText();
|
||||
String password = new String(passwordTextField.getPassword());
|
||||
|
||||
try (ClientSocket client = new ClientSocket()) {
|
||||
SocketResponse res = client.register(username, password);
|
||||
|
||||
if(res.getStatus() == SocketStatus.OK) {
|
||||
logger.info("Usuario '{}' registrado correctamente.", username);
|
||||
} else {
|
||||
logger.warn("Error al registrar '{}': {}", username,
|
||||
res.getMessage() != null ? res.getMessage().getMessage() : "Respuesta sin mensaje");
|
||||
}
|
||||
|
||||
JOptionPane.showMessageDialog(this,
|
||||
res.getMessage() != null ? res.getMessage().getMessage() : "Respuesta sin mensaje",
|
||||
res.getStatus().toString(),
|
||||
res.getStatus() == SocketStatus.OK ?
|
||||
JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
|
||||
clearLoginInputs();
|
||||
} catch (IOException ex) {
|
||||
logger.error("Error de conexión con el servidor al registrar '{}': {}", username, ex.getMessage());
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Error de conexión con el servidor",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void logout() {
|
||||
if (_loggedUser != null) {
|
||||
try (ClientSocket client = new ClientSocket()) {
|
||||
SocketResponse res = client.logout(_loggedUser.getUserId());
|
||||
|
||||
if (res != null && res.getMessage() != null) {
|
||||
if (res.getStatus() == SocketStatus.OK) {
|
||||
logger.info("Usuario '{}' ha cerrado sesión correctamente.", _loggedUser.getUserName());
|
||||
this.setTitle(Constants.APP_NAME);
|
||||
} else {
|
||||
logger.warn("Intento de logout de '{}' con estado: {}", _loggedUser.getUserName(), res.getStatus());
|
||||
}
|
||||
|
||||
JOptionPane.showMessageDialog(this,
|
||||
res.getMessage().getMessage(),
|
||||
res.getStatus().toString(),
|
||||
res.getStatus() == SocketStatus.OK ?
|
||||
JOptionPane.INFORMATION_MESSAGE : JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
logger.error("Error de conexión con el servidor al hacer logout de '{}': {}", _loggedUser.getUserName(), ex.getMessage());
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Error de conexión con el servidor",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clearMainInputs();
|
||||
_loggedUser = null;
|
||||
}
|
||||
|
||||
private void thisWindowClosing(WindowEvent e) {
|
||||
logout();
|
||||
}
|
||||
|
||||
private void send(ActionEvent e) {
|
||||
try (ClientSocket client = new ClientSocket()) {
|
||||
String message = clientTextField.getText();
|
||||
MessageDTO dto = new MessageDTO(_loggedUser.getUserName(), message);
|
||||
SocketResponse res = client.sendMessage(dto);
|
||||
|
||||
appendChatMessage(String.format("%s: %s", _loggedUser.getUserName(), message));
|
||||
appendChatMessage("Servidor: " + res.getData());
|
||||
|
||||
if (res.getStatus() == SocketStatus.OK) {
|
||||
logger.info("Mensaje enviado: {} -> Servidor", _loggedUser.getUserName());
|
||||
} else if (res.getStatus() == SocketStatus.SESSION_EXPIRED) {
|
||||
logger.warn("Sesión expirada para '{}'. Se cerrará sesión.", _loggedUser.getUserName());
|
||||
_loggedUser = null;
|
||||
setView("loginPanel");
|
||||
clearMainInputs();
|
||||
} else {
|
||||
logger.warn("Error enviando mensaje de {} a Servidor: {}", _loggedUser.getUserName(),
|
||||
res.getMessage() != null ? res.getMessage().getMessage() : "Sin mensaje");
|
||||
}
|
||||
|
||||
clearMainInputs();
|
||||
} catch (Exception ex) {
|
||||
logger.error("Error de conexión con el servidor enviando mensaje de {} a Servidor: {}",
|
||||
_loggedUser.getUserName(), ex.getMessage());
|
||||
JOptionPane.showMessageDialog(this,
|
||||
"Error de conexión con el servidor",
|
||||
"Error",
|
||||
JOptionPane.ERROR_MESSAGE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void logoutBtn(ActionEvent e) {
|
||||
logout();
|
||||
setView("loginPanel");
|
||||
}
|
||||
|
||||
private void initComponents() {
|
||||
// JFormDesigner - Component initialization - DO NOT MODIFY //GEN-BEGIN:initComponents @formatter:off
|
||||
// Generated using JFormDesigner Educational license - José Manuel Amador Gallardo (José Manuel Amador)
|
||||
loginPanel = new JPanel();
|
||||
logo = new JLabel();
|
||||
userPanel = new JPanel();
|
||||
userLabel = new JLabel();
|
||||
userTextField = new JTextField();
|
||||
passwordPanel = new JPanel();
|
||||
passwordLabel = new JLabel();
|
||||
passwordTextField = new JPasswordField();
|
||||
loginBtn = new JButton();
|
||||
btnLoginPanel = new JPanel();
|
||||
noAccountLabel = new JLabel();
|
||||
registerBtn = new JButton();
|
||||
versionLabel = new JLabel();
|
||||
mainPanel = new JPanel();
|
||||
chatScrollPane = new JScrollPane();
|
||||
chatTextArea = new JTextArea();
|
||||
clientPanel = new JPanel();
|
||||
clientLabel = new JLabel();
|
||||
logoutBtn = new JButton();
|
||||
clientTextField = new JTextField();
|
||||
|
||||
//======== this ========
|
||||
setResizable(false);
|
||||
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
thisWindowClosing(e);
|
||||
}
|
||||
});
|
||||
var contentPane = getContentPane();
|
||||
contentPane.setLayout(new CardLayout(6, 6));
|
||||
|
||||
//======== loginPanel ========
|
||||
{
|
||||
loginPanel.setLayout(new MigLayout(
|
||||
"hidemode 3,gapy 12",
|
||||
// columns
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[]para" +
|
||||
"[fill]" +
|
||||
"[fill]para" +
|
||||
"[]" +
|
||||
"[grow,fill]"));
|
||||
|
||||
//---- logo ----
|
||||
logo.setIcon(new ImageIcon(getClass().getResource("/images/logo.png")));
|
||||
logo.setMaximumSize(new Dimension(256, 256));
|
||||
logo.setMinimumSize(new Dimension(256, 256));
|
||||
logo.setPreferredSize(new Dimension(256, 256));
|
||||
logo.setHorizontalTextPosition(SwingConstants.CENTER);
|
||||
loginPanel.add(logo, "cell 0 0,alignx center,growx 0");
|
||||
|
||||
//======== userPanel ========
|
||||
{
|
||||
userPanel.setLayout(new MigLayout(
|
||||
"insets 0,hidemode 3",
|
||||
// columns
|
||||
"[fill]" +
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[grow,fill]"));
|
||||
|
||||
//---- userLabel ----
|
||||
userLabel.setText("Usuario:");
|
||||
userLabel.setFont(new Font("Adwaita Sans", Font.PLAIN, 18));
|
||||
userLabel.setMaximumSize(new Dimension(130, 23));
|
||||
userLabel.setMinimumSize(new Dimension(130, 23));
|
||||
userLabel.setPreferredSize(new Dimension(130, 23));
|
||||
userPanel.add(userLabel, "cell 0 0");
|
||||
|
||||
//---- userTextField ----
|
||||
userTextField.setFont(new Font("Adwaita Sans", Font.PLAIN, 18));
|
||||
userPanel.add(userTextField, "cell 1 0");
|
||||
}
|
||||
loginPanel.add(userPanel, "cell 0 1");
|
||||
|
||||
//======== passwordPanel ========
|
||||
{
|
||||
passwordPanel.setLayout(new MigLayout(
|
||||
"insets 0,hidemode 3",
|
||||
// columns
|
||||
"[fill]" +
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[grow,fill]"));
|
||||
|
||||
//---- passwordLabel ----
|
||||
passwordLabel.setText("Contrase\u00f1a:");
|
||||
passwordLabel.setFont(new Font("Adwaita Sans", Font.PLAIN, 18));
|
||||
passwordLabel.setMaximumSize(new Dimension(130, 23));
|
||||
passwordLabel.setMinimumSize(new Dimension(130, 23));
|
||||
passwordLabel.setPreferredSize(new Dimension(130, 23));
|
||||
passwordPanel.add(passwordLabel, "cell 0 0");
|
||||
|
||||
//---- passwordTextField ----
|
||||
passwordTextField.setFont(new Font("Adwaita Sans", Font.PLAIN, 18));
|
||||
passwordPanel.add(passwordTextField, "cell 1 0");
|
||||
}
|
||||
loginPanel.add(passwordPanel, "cell 0 2");
|
||||
|
||||
//---- loginBtn ----
|
||||
loginBtn.setText("Iniciar sesi\u00f3n");
|
||||
loginBtn.setFont(new Font("Adwaita Sans", Font.PLAIN, 18));
|
||||
loginBtn.addActionListener(e -> loginBtn(e));
|
||||
loginPanel.add(loginBtn, "cell 0 3,alignx trailing,growx 0");
|
||||
|
||||
//======== btnLoginPanel ========
|
||||
{
|
||||
btnLoginPanel.setLayout(new MigLayout(
|
||||
"hidemode 3,aligny bottom",
|
||||
// columns
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[]" +
|
||||
"[fill]para" +
|
||||
"[]"));
|
||||
|
||||
//---- noAccountLabel ----
|
||||
noAccountLabel.setText("\u00bfNo tiene cuenta?");
|
||||
btnLoginPanel.add(noAccountLabel, "cell 0 0,alignx center,growx 0");
|
||||
|
||||
//---- registerBtn ----
|
||||
registerBtn.setText("Registrarse");
|
||||
registerBtn.setFont(new Font("Adwaita Sans", Font.PLAIN, 18));
|
||||
registerBtn.addActionListener(e -> registerBtn(e));
|
||||
btnLoginPanel.add(registerBtn, "cell 0 1,alignx center,growx 0");
|
||||
|
||||
//---- versionLabel ----
|
||||
versionLabel.setForeground(new Color(0xa0a0a0));
|
||||
btnLoginPanel.add(versionLabel, "cell 0 2,alignx center,growx 0");
|
||||
}
|
||||
loginPanel.add(btnLoginPanel, "cell 0 4");
|
||||
}
|
||||
contentPane.add(loginPanel, "loginPanel");
|
||||
|
||||
//======== mainPanel ========
|
||||
{
|
||||
mainPanel.setLayout(new MigLayout(
|
||||
"hidemode 3",
|
||||
// columns
|
||||
"[grow,fill]",
|
||||
// rows
|
||||
"[grow,fill]" +
|
||||
"[fill]" +
|
||||
"[fill]"));
|
||||
|
||||
//======== chatScrollPane ========
|
||||
{
|
||||
|
||||
//---- chatTextArea ----
|
||||
chatTextArea.setEditable(false);
|
||||
chatTextArea.setFocusable(false);
|
||||
chatTextArea.setFont(new Font("Adwaita Mono", Font.PLAIN, 16));
|
||||
chatScrollPane.setViewportView(chatTextArea);
|
||||
}
|
||||
mainPanel.add(chatScrollPane, "cell 0 0");
|
||||
|
||||
//======== clientPanel ========
|
||||
{
|
||||
clientPanel.setLayout(new MigLayout(
|
||||
"insets 0,hidemode 3",
|
||||
// columns
|
||||
"[grow,fill]" +
|
||||
"[fill]",
|
||||
// rows
|
||||
"[]"));
|
||||
|
||||
//---- clientLabel ----
|
||||
clientLabel.setText("Cliente: $c");
|
||||
clientLabel.setFont(new Font("Adwaita Sans", Font.PLAIN, 16));
|
||||
clientPanel.add(clientLabel, "cell 0 0");
|
||||
|
||||
//---- logoutBtn ----
|
||||
logoutBtn.setText("Cerrar sesi\u00f3n");
|
||||
logoutBtn.setFont(new Font("Adwaita Sans", Font.PLAIN, 16));
|
||||
logoutBtn.addActionListener(e -> logoutBtn(e));
|
||||
clientPanel.add(logoutBtn, "cell 1 0");
|
||||
}
|
||||
mainPanel.add(clientPanel, "cell 0 1");
|
||||
|
||||
//---- clientTextField ----
|
||||
clientTextField.addActionListener(e -> send(e));
|
||||
mainPanel.add(clientTextField, "cell 0 2");
|
||||
}
|
||||
contentPane.add(mainPanel, "mainPanel");
|
||||
setSize(400, 600);
|
||||
setLocationRelativeTo(getOwner());
|
||||
// JFormDesigner - End of component initialization //GEN-END:initComponents @formatter:on
|
||||
}
|
||||
|
||||
// JFormDesigner - Variables declaration - DO NOT MODIFY //GEN-BEGIN:variables @formatter:off
|
||||
// Generated using JFormDesigner Educational license - José Manuel Amador Gallardo (José Manuel Amador)
|
||||
private JPanel loginPanel;
|
||||
private JLabel logo;
|
||||
private JPanel userPanel;
|
||||
private JLabel userLabel;
|
||||
private JTextField userTextField;
|
||||
private JPanel passwordPanel;
|
||||
private JLabel passwordLabel;
|
||||
private JPasswordField passwordTextField;
|
||||
private JButton loginBtn;
|
||||
private JPanel btnLoginPanel;
|
||||
private JLabel noAccountLabel;
|
||||
private JButton registerBtn;
|
||||
private JLabel versionLabel;
|
||||
private JPanel mainPanel;
|
||||
private JScrollPane chatScrollPane;
|
||||
private JTextArea chatTextArea;
|
||||
private JPanel clientPanel;
|
||||
private JLabel clientLabel;
|
||||
private JButton logoutBtn;
|
||||
private JTextField clientTextField;
|
||||
// JFormDesigner - End of variables declaration //GEN-END:variables @formatter:on
|
||||
}
|
||||
@@ -0,0 +1,176 @@
|
||||
JFDML JFormDesigner: "8.2.4.0.393" Java: "21.0.8" encoding: "UTF-8"
|
||||
|
||||
new FormModel {
|
||||
contentType: "form/swing"
|
||||
root: new FormRoot {
|
||||
add( new FormWindow( "javax.swing.JFrame", new FormLayoutManager( class java.awt.CardLayout ) {
|
||||
"hgap": 6
|
||||
"vgap": 6
|
||||
} ) {
|
||||
name: "this"
|
||||
"$sizePolicy": 1
|
||||
"resizable": false
|
||||
"defaultCloseOperation": 3
|
||||
addEvent( new FormEvent( "java.awt.event.WindowListener", "windowClosing", "thisWindowClosing", true ) )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "hidemode 3,gapy 12"
|
||||
"$columnConstraints": "[grow,fill]"
|
||||
"$rowConstraints": "[]para[fill][fill]para[][grow,fill]"
|
||||
} ) {
|
||||
name: "loginPanel"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "logo"
|
||||
"icon": new com.jformdesigner.model.SwingIcon( 0, "/images/logo.png" )
|
||||
"maximumSize": new java.awt.Dimension( 256, 256 )
|
||||
"minimumSize": new java.awt.Dimension( 256, 256 )
|
||||
"preferredSize": new java.awt.Dimension( 256, 256 )
|
||||
"horizontalTextPosition": 0
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0,alignx center,growx 0"
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3"
|
||||
"$columnConstraints": "[fill][grow,fill]"
|
||||
"$rowConstraints": "[grow,fill]"
|
||||
} ) {
|
||||
name: "userPanel"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "userLabel"
|
||||
"text": "Usuario:"
|
||||
"font": &Font0 new java.awt.Font( "Adwaita Sans", 0, 18 )
|
||||
"maximumSize": new java.awt.Dimension( 130, 23 )
|
||||
"minimumSize": new java.awt.Dimension( 130, 23 )
|
||||
"preferredSize": new java.awt.Dimension( 130, 23 )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "userTextField"
|
||||
"font": &Font1 new java.awt.Font( "Adwaita Sans", 0, 18 )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3"
|
||||
"$columnConstraints": "[fill][grow,fill]"
|
||||
"$rowConstraints": "[grow,fill]"
|
||||
} ) {
|
||||
name: "passwordPanel"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "passwordLabel"
|
||||
"text": "Contraseña:"
|
||||
"font": #Font0
|
||||
"maximumSize": new java.awt.Dimension( 130, 23 )
|
||||
"minimumSize": new java.awt.Dimension( 130, 23 )
|
||||
"preferredSize": new java.awt.Dimension( 130, 23 )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JPasswordField" ) {
|
||||
name: "passwordTextField"
|
||||
"font": #Font1
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JButton" ) {
|
||||
name: "loginBtn"
|
||||
"text": "Iniciar sesión"
|
||||
"font": &Font2 new java.awt.Font( "Adwaita Sans", 0, 18 )
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "loginBtn", true ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 3,alignx trailing,growx 0"
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "hidemode 3,aligny bottom"
|
||||
"$columnConstraints": "[grow,fill]"
|
||||
"$rowConstraints": "[][fill]para[]"
|
||||
} ) {
|
||||
name: "btnLoginPanel"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "noAccountLabel"
|
||||
"text": "¿No tiene cuenta?"
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0,alignx center,growx 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JButton" ) {
|
||||
name: "registerBtn"
|
||||
"text": "Registrarse"
|
||||
"font": #Font2
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "registerBtn", true ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1,alignx center,growx 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "versionLabel"
|
||||
"foreground": new java.awt.Color( 160, 160, 160, 255 )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2,alignx center,growx 0"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 4"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||
"value": "loginPanel"
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "hidemode 3"
|
||||
"$columnConstraints": "[grow,fill]"
|
||||
"$rowConstraints": "[grow,fill][fill][fill]"
|
||||
} ) {
|
||||
name: "mainPanel"
|
||||
add( new FormContainer( "javax.swing.JScrollPane", new FormLayoutManager( class javax.swing.JScrollPane ) ) {
|
||||
name: "chatScrollPane"
|
||||
add( new FormComponent( "javax.swing.JTextArea" ) {
|
||||
name: "chatTextArea"
|
||||
"editable": false
|
||||
"focusable": false
|
||||
"font": new java.awt.Font( "Adwaita Mono", 0, 16 )
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
|
||||
"$layoutConstraints": "insets 0,hidemode 3"
|
||||
"$columnConstraints": "[grow,fill][fill]"
|
||||
"$rowConstraints": "[]"
|
||||
} ) {
|
||||
name: "clientPanel"
|
||||
add( new FormComponent( "javax.swing.JLabel" ) {
|
||||
name: "clientLabel"
|
||||
"text": "Cliente: $c"
|
||||
"font": &Font3 new java.awt.Font( "Adwaita Sans", 0, 16 )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 0"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JButton" ) {
|
||||
name: "logoutBtn"
|
||||
"text": "Cerrar sesión"
|
||||
"font": #Font3
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "logoutBtn", true ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 1 0"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 1"
|
||||
} )
|
||||
add( new FormComponent( "javax.swing.JTextField" ) {
|
||||
name: "clientTextField"
|
||||
addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "send", true ) )
|
||||
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
|
||||
"value": "cell 0 2"
|
||||
} )
|
||||
}, new FormLayoutConstraints( class java.lang.String ) {
|
||||
"value": "mainPanel"
|
||||
} )
|
||||
}, new FormLayoutConstraints( null ) {
|
||||
"location": new java.awt.Point( 0, 0 )
|
||||
"size": new java.awt.Dimension( 400, 600 )
|
||||
} )
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package net.miarma.byodsec.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Properties;
|
||||
|
||||
public class ConfigManager {
|
||||
private static ConfigManager INSTANCE;
|
||||
private final File jksFile;
|
||||
private final File configFile;
|
||||
private final Properties config;
|
||||
private static final String JKS_FILE_NAME = "byodsec.jks";
|
||||
private static final String CONFIG_FILE_NAME = "config.properties";
|
||||
private final Logger logger = LoggerFactory.getLogger(ConfigManager.class);
|
||||
|
||||
private ConfigManager() {
|
||||
String jksPath = getBaseDir() + JKS_FILE_NAME;
|
||||
String configPath = getBaseDir() + CONFIG_FILE_NAME;
|
||||
this.jksFile = new File(jksPath);
|
||||
this.configFile = new File(configPath);
|
||||
this.config = new Properties();
|
||||
}
|
||||
|
||||
public static ConfigManager getInstance() {
|
||||
if(INSTANCE == null) {
|
||||
INSTANCE = new ConfigManager();
|
||||
}
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public void loadConfig() {
|
||||
try (FileInputStream fis = new FileInputStream(configFile);
|
||||
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8)) {
|
||||
config.load(isr);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error loading configuration file: ", e);
|
||||
}
|
||||
}
|
||||
|
||||
public File getJksFile() {
|
||||
return jksFile;
|
||||
}
|
||||
|
||||
public File getConfigFile() {
|
||||
return configFile;
|
||||
}
|
||||
|
||||
public String getHomeDir() {
|
||||
return getOS() == OSType.WINDOWS ?
|
||||
"C:/Users/" + System.getProperty("user.name") + "/" :
|
||||
getOS() == OSType.LINUX ?
|
||||
(System.getProperty("user.home").contains("root") ? "/root/" :
|
||||
"/home/" + System.getProperty("user.name") + "/") :
|
||||
getOS() == OSType.MACOS ? "/Users/" + System.getProperty("user.name") + "/" : System.getProperty("user.home");
|
||||
}
|
||||
|
||||
public String getBaseDir() {
|
||||
return getHomeDir() +
|
||||
(getOS() == OSType.WINDOWS || getOS() == OSType.MACOS ? ".byodsec/" :
|
||||
getOS() == OSType.LINUX ? ".config/byodsec/" :
|
||||
".byodsec/");
|
||||
}
|
||||
|
||||
public static OSType getOS() {
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.contains("win")) {
|
||||
return OSType.WINDOWS;
|
||||
} else if (os.contains("nix") || os.contains("nux")) {
|
||||
return OSType.LINUX;
|
||||
} else if (os.contains("mac")) {
|
||||
return OSType.MACOS;
|
||||
} else {
|
||||
return OSType.INVALID_OS;
|
||||
}
|
||||
}
|
||||
|
||||
public String getStringProperty(String key) {
|
||||
return config.getProperty(key);
|
||||
}
|
||||
|
||||
public int getIntProperty(String key) {
|
||||
String value = config.getProperty(key);
|
||||
return value != null ? Integer.parseInt(value) : 10;
|
||||
}
|
||||
|
||||
public boolean getBooleanProperty(String key) {
|
||||
return Boolean.parseBoolean(config.getProperty(key));
|
||||
}
|
||||
|
||||
public void setProperty(String key, String value) {
|
||||
config.setProperty(key, value);
|
||||
saveConfig();
|
||||
}
|
||||
|
||||
private void saveConfig() {
|
||||
try (FileOutputStream fos = new FileOutputStream(configFile)) {
|
||||
config.store(fos, "Configuration for: " + Constants.APP_NAME);
|
||||
} catch (IOException e) {
|
||||
logger.error("Error saving configuration file: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.miarma.byodsec.common;
|
||||
|
||||
public class Constants {
|
||||
public static final String APP_NAME = "BYODSEC";
|
||||
public static final String APP_VERSION = "1.0.0";
|
||||
|
||||
private Constants() {
|
||||
throw new AssertionError("Utility class cannot be instantiated.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package net.miarma.byodsec.common;
|
||||
|
||||
/**
|
||||
* Enum que representa los diferentes tipos de sistemas operativos soportados
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public enum OSType {
|
||||
LINUX, WINDOWS, MACOS, INVALID_OS
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package net.miarma.byodsec.common.db;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.persistence.Persistence;
|
||||
|
||||
public class DBConnector {
|
||||
private static DBConnector instance;
|
||||
private EntityManagerFactory emf;
|
||||
private final ThreadLocal<EntityManager> threadLocal = new ThreadLocal<>();
|
||||
|
||||
private DBConnector() {
|
||||
this.emf = Persistence.createEntityManagerFactory("ssii-pai2");
|
||||
}
|
||||
|
||||
public static DBConnector getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new DBConnector();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public EntityManager getEntityManager() {
|
||||
EntityManager em = threadLocal.get();
|
||||
if (em == null || !em.isOpen()) {
|
||||
em = emf.createEntityManager();
|
||||
threadLocal.set(em);
|
||||
}
|
||||
return em;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
EntityManager em = threadLocal.get();
|
||||
if (em != null && em.isOpen()) {
|
||||
em.close();
|
||||
}
|
||||
threadLocal.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.miarma.byodsec.common.db;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import net.miarma.byodsec.common.security.PasswordHasher;
|
||||
import net.miarma.byodsec.common.db.entities.UserEntity;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class DBPopulator {
|
||||
private final DBConnector conn = DBConnector.getInstance();
|
||||
private EntityManager em;
|
||||
private static DBPopulator instance;
|
||||
|
||||
private DBPopulator() {
|
||||
em = conn.getEntityManager();
|
||||
}
|
||||
|
||||
public static DBPopulator getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new DBPopulator();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public boolean isFirstRun() {
|
||||
TypedQuery<Long> q = em.createQuery("SELECT COUNT(u) FROM UserEntity u", Long.class);
|
||||
Long count = q.getSingleResult();
|
||||
return count == 0;
|
||||
}
|
||||
|
||||
public void populate() {
|
||||
UserEntity u1 = new UserEntity(UUID.randomUUID().toString(),
|
||||
"ricolinad", PasswordHasher.hash("ricardo2025"));
|
||||
UserEntity u2 = new UserEntity(UUID.randomUUID().toString(),
|
||||
"alvgulveg", PasswordHasher.hash("alcalde_dimision"));
|
||||
UserEntity u3 = new UserEntity(UUID.randomUUID().toString(),
|
||||
"smt4497", PasswordHasher.hash("quemenlaus"));
|
||||
try {
|
||||
em.getTransaction().begin();
|
||||
em.persist(u1);
|
||||
em.persist(u2);
|
||||
em.persist(u3);
|
||||
em.getTransaction().commit();
|
||||
} catch (Exception e) {
|
||||
em.getTransaction().rollback();
|
||||
} finally {
|
||||
em.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package net.miarma.byodsec.common.db.dao;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import net.miarma.byodsec.common.db.entities.SessionEntity;
|
||||
|
||||
public class SessionDAO {
|
||||
public SessionEntity getByUserId(EntityManager em, String userId) {
|
||||
return em.createQuery("SELECT s FROM SessionEntity s WHERE s.userId = :userId", SessionEntity.class)
|
||||
.setParameter("userId", userId)
|
||||
.getSingleResult();
|
||||
}
|
||||
|
||||
public boolean isLoggedIn(EntityManager em, String userId) {
|
||||
Long count = em.createQuery(
|
||||
"SELECT COUNT(s) FROM SessionEntity s WHERE s.userId = :userId", Long.class)
|
||||
.setParameter("userId", userId)
|
||||
.getSingleResult();
|
||||
return count > 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package net.miarma.byodsec.common.db.dao;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import net.miarma.byodsec.common.db.entities.UserEntity;
|
||||
|
||||
public class UserDAO {
|
||||
public UserEntity getByUserName(EntityManager em, String userName) {
|
||||
try {
|
||||
return em.createQuery("SELECT u FROM UserEntity u WHERE u.userName = :userName", UserEntity.class)
|
||||
.setParameter("userName", userName)
|
||||
.getSingleResult();
|
||||
} catch (jakarta.persistence.NoResultException e) {
|
||||
return null; // No se encontró, devolvemos null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.miarma.byodsec.common.db.dto;
|
||||
|
||||
public class LoginDTO {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
public LoginDTO() {
|
||||
}
|
||||
|
||||
public LoginDTO(String username, String password) {
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.miarma.byodsec.common.db.dto;
|
||||
|
||||
public class MessageDTO {
|
||||
private String fromUserName;
|
||||
private String messageText;
|
||||
|
||||
public MessageDTO() {}
|
||||
|
||||
public MessageDTO(String fromUserName, String messageText) {
|
||||
this.fromUserName = fromUserName;
|
||||
this.messageText = messageText;
|
||||
}
|
||||
|
||||
public String getFromUserName() {
|
||||
return fromUserName;
|
||||
}
|
||||
|
||||
public void setFromUserName(String fromUserName) {
|
||||
this.fromUserName = fromUserName;
|
||||
}
|
||||
|
||||
public String getMessageText() {
|
||||
return messageText;
|
||||
}
|
||||
|
||||
public void setMessageText(String messageText) {
|
||||
this.messageText = messageText;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package net.miarma.byodsec.common.db.entities;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Entity
|
||||
@Table(name = "sessions")
|
||||
public class SessionEntity {
|
||||
@Id
|
||||
@Column(name = "sessionId", nullable = false, updatable = false)
|
||||
private String sessionId;
|
||||
|
||||
@Column(name = "userId", nullable = false)
|
||||
private String userId;
|
||||
|
||||
@Column(name = "createdAt", nullable = false, updatable = false, insertable = false)
|
||||
private LocalDateTime createdAt;
|
||||
|
||||
@Column(name = "expiresAt", nullable = false, updatable = false, insertable = false)
|
||||
private LocalDateTime expiresAt;
|
||||
|
||||
public SessionEntity() {}
|
||||
|
||||
public SessionEntity(String sessionId, String userId, LocalDateTime createdAt, LocalDateTime expiresAt) {
|
||||
this.sessionId = sessionId;
|
||||
this.userId = userId;
|
||||
this.createdAt = createdAt;
|
||||
this.expiresAt = expiresAt;
|
||||
}
|
||||
|
||||
public String getSessionId() {
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
public void setSessionId(String sessionId) {
|
||||
this.sessionId = sessionId;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(LocalDateTime createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
|
||||
public LocalDateTime getExpiresAt() {
|
||||
return expiresAt;
|
||||
}
|
||||
|
||||
public void setExpiresAt(LocalDateTime expiresAt) {
|
||||
this.expiresAt = expiresAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package net.miarma.byodsec.common.db.entities;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name = "users")
|
||||
public class UserEntity {
|
||||
@Id
|
||||
@Column(name = "userId", nullable = false, updatable = false, length = 36)
|
||||
private String userId;
|
||||
|
||||
@Column(name = "userName", unique = true, nullable = false, length = 64)
|
||||
private String userName;
|
||||
|
||||
@Column(name = "password", nullable = false, length = 256)
|
||||
private String password;
|
||||
|
||||
public UserEntity() {}
|
||||
|
||||
public UserEntity(String userId, String userName, String password) {
|
||||
this.userId = userId;
|
||||
this.userName = userName;
|
||||
this.password = password;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public String getPassword() {
|
||||
return password;
|
||||
}
|
||||
|
||||
public void setPassword(String password) {
|
||||
this.password = password;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package net.miarma.byodsec.common.security;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
public class IntegrityProvider {
|
||||
public static String generateHMAC(String key, String data) throws Exception {
|
||||
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
|
||||
SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
|
||||
sha256HMAC.init(secretKey);
|
||||
return byteArrayToHex(sha256HMAC.doFinal(data.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public static String generateNonce() {
|
||||
byte[] nonce = new byte[16];
|
||||
new SecureRandom().nextBytes(nonce);
|
||||
return Base64.getEncoder().encodeToString(nonce);
|
||||
}
|
||||
|
||||
public static String byteArrayToHex(byte[] arr) {
|
||||
StringBuilder sb = new StringBuilder(arr.length * 2);
|
||||
for(byte b : arr) {
|
||||
sb.append(String.format("%02x", b));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.miarma.byodsec.common.security;
|
||||
|
||||
import org.mindrot.jbcrypt.BCrypt;
|
||||
|
||||
/**
|
||||
* Clase de utilidad para el hash de contraseñas.
|
||||
* Utiliza BCrypt para generar y verificar hashes de contraseñas.
|
||||
*
|
||||
* @author José Manuel Amador Gallardo
|
||||
*/
|
||||
public class PasswordHasher {
|
||||
|
||||
private static final int SALT_ROUNDS = 12;
|
||||
|
||||
public static String hash(String plainPassword) {
|
||||
return BCrypt.hashpw(plainPassword, BCrypt.gensalt(SALT_ROUNDS));
|
||||
}
|
||||
|
||||
public static boolean verify(String plainPassword, String hashedPassword) {
|
||||
return BCrypt.checkpw(plainPassword, hashedPassword);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package net.miarma.byodsec.common.socket;
|
||||
|
||||
public class SocketRequest {
|
||||
private SocketRequest.Action action;
|
||||
private Object payload;
|
||||
|
||||
public SocketRequest() {}
|
||||
|
||||
public SocketRequest(SocketRequest.Action action, Object payload) {
|
||||
this.action = action;
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
public SocketRequest.Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(SocketRequest.Action action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public Object getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public void setPayload(Object payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SocketRequest{" +
|
||||
"action='" + action.name() + '\'' +
|
||||
", payload=" + (payload != null ? payload.toString() : "null") +
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum Action {
|
||||
LOGIN, REGISTER, SEND_MESSAGE, LOGOUT
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package net.miarma.byodsec.common.socket;
|
||||
|
||||
public class SocketResponse {
|
||||
|
||||
private SocketStatus status;
|
||||
private Message message;
|
||||
private Object data;
|
||||
|
||||
public SocketResponse(SocketStatus status, Message message) {
|
||||
this(status, message, message != null ? message.getMessage() : null);
|
||||
}
|
||||
|
||||
public SocketResponse(SocketStatus status, Message message, Object data) {
|
||||
this.status = status;
|
||||
this.message = message;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public SocketStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Object getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SocketResponse{" +
|
||||
"status=" + status +
|
||||
", message=" + (message != null ? message.name() : "null") +
|
||||
", data=" + (data != null ? data.toString() : "null") +
|
||||
'}';
|
||||
}
|
||||
|
||||
public enum Message {
|
||||
LOGGED_OUT("Ha cerrado sesión correctamente"),
|
||||
LOGGED_IN("Ha iniciado sesión correctamente"),
|
||||
MESSAGE_SENT("Su mensaje se ha enviado"),
|
||||
USER_NOT_FOUND("El usuario no existe."),
|
||||
WRONG_PASSWORD("Contraseña incorrecta."),
|
||||
USER_ALREADY_EXISTS("El usuario ya está registrado."),
|
||||
CONNECTION_ERROR("Error de conexión con el servidor."),
|
||||
UNKNOWN_ERROR("Ha ocurrido un error inesperado."),
|
||||
SESSION_EXPIRED("Tu sesión ha expirado."),
|
||||
INTEGRITY_FAIL("Fallo de integridad en el mensaje."),
|
||||
INVALID_AMOUNT("Cantidad no válida."),
|
||||
REPLAY_ATTACK("Intento de repetición detectado."),
|
||||
RECIPIENT_NOT_FOUND("El destinatario no existe"),
|
||||
REGISTERED("Se ha registrado correctamente"),
|
||||
SESSION_ACTIVE("Ya hay una sesión activa para este usuario.");
|
||||
|
||||
private final String message;
|
||||
|
||||
Message(String userMessage) {
|
||||
this.message = userMessage;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package net.miarma.byodsec.common.socket;
|
||||
|
||||
public enum SocketStatus {
|
||||
OK,
|
||||
ERROR,
|
||||
SESSION_EXPIRED,
|
||||
ALREADY_LOGGED_IN,
|
||||
INTEGRITY_FAIL,
|
||||
UNAUTHORIZED,
|
||||
INVALID_REQUEST;
|
||||
|
||||
public static boolean isOk(String status) {
|
||||
return OK.name().equalsIgnoreCase(status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package net.miarma.byodsec.server;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import net.miarma.byodsec.common.db.DBConnector;
|
||||
import net.miarma.byodsec.common.db.dao.SessionDAO;
|
||||
import net.miarma.byodsec.common.db.dao.UserDAO;
|
||||
import net.miarma.byodsec.common.db.dto.LoginDTO;
|
||||
import net.miarma.byodsec.common.db.dto.MessageDTO;
|
||||
import net.miarma.byodsec.common.db.entities.SessionEntity;
|
||||
import net.miarma.byodsec.common.db.entities.UserEntity;
|
||||
import net.miarma.byodsec.common.security.PasswordHasher;
|
||||
import net.miarma.byodsec.common.socket.SocketRequest;
|
||||
import net.miarma.byodsec.common.socket.SocketResponse;
|
||||
import net.miarma.byodsec.common.socket.SocketStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import java.io.*;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ClientHandler implements Runnable {
|
||||
private final SSLSocket socket;
|
||||
private final Gson gson;
|
||||
private final UserDAO userDAO;
|
||||
private final SessionDAO sessionDAO;
|
||||
private final DBConnector conn;
|
||||
private final Logger logger;
|
||||
|
||||
public ClientHandler(SSLSocket socket, Gson gson, UserDAO userDAO, SessionDAO sessionDAO, DBConnector conn, Logger logger) {
|
||||
this.socket = socket;
|
||||
this.gson = gson;
|
||||
this.userDAO = userDAO;
|
||||
this.sessionDAO = sessionDAO;
|
||||
this.conn = conn;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try (
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
PrintWriter output = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
|
||||
) {
|
||||
logger.info("Nueva conexión desde {}:{}", socket.getInetAddress(), socket.getPort());
|
||||
SocketRequest msg = gson.fromJson(input.readLine(), SocketRequest.class);
|
||||
SocketResponse res;
|
||||
|
||||
switch (msg.getAction()) {
|
||||
case REGISTER -> {
|
||||
LoginDTO dto = gson.fromJson(gson.toJson(msg.getPayload()), LoginDTO.class);
|
||||
logger.info("Procesando REGISTER para {}", dto.getUsername());
|
||||
EntityManager em = conn.getEntityManager();
|
||||
try {
|
||||
if (userDAO.getByUserName(em, dto.getUsername()) != null) {
|
||||
res = new SocketResponse(SocketStatus.INVALID_REQUEST, SocketResponse.Message.USER_ALREADY_EXISTS);
|
||||
} else {
|
||||
em.getTransaction().begin();
|
||||
UserEntity u = new UserEntity(UUID.randomUUID().toString(),
|
||||
dto.getUsername(), PasswordHasher.hash(dto.getPassword()));
|
||||
em.persist(u);
|
||||
em.getTransaction().commit();
|
||||
res = new SocketResponse(SocketStatus.OK, SocketResponse.Message.REGISTERED, u);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
if (em.getTransaction().isActive()) em.getTransaction().rollback();
|
||||
logger.error(e.getMessage(), e);
|
||||
res = new SocketResponse(SocketStatus.ERROR, SocketResponse.Message.UNKNOWN_ERROR, e.getMessage());
|
||||
} finally {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
||||
case LOGIN -> {
|
||||
LoginDTO dto = gson.fromJson(gson.toJson(msg.getPayload()), LoginDTO.class);
|
||||
logger.info("Procesando LOGIN para {}", dto.getUsername());
|
||||
try (EntityManager em = conn.getEntityManager()) {
|
||||
UserEntity user = userDAO.getByUserName(em, dto.getUsername());
|
||||
if (user == null) {
|
||||
res = new SocketResponse(SocketStatus.INVALID_REQUEST, SocketResponse.Message.USER_NOT_FOUND);
|
||||
} else if (PasswordHasher.verify(dto.getPassword(), user.getPassword())) {
|
||||
if(sessionDAO.isLoggedIn(em, user.getUserId())) {
|
||||
res = new SocketResponse(SocketStatus.ALREADY_LOGGED_IN, SocketResponse.Message.SESSION_ACTIVE);
|
||||
} else {
|
||||
res = new SocketResponse(SocketStatus.OK, SocketResponse.Message.LOGGED_IN, user);
|
||||
|
||||
SessionEntity session = new SessionEntity();
|
||||
session.setSessionId(UUID.randomUUID().toString());
|
||||
session.setUserId(user.getUserId());
|
||||
|
||||
em.getTransaction().begin();
|
||||
em.persist(session);
|
||||
em.getTransaction().commit();
|
||||
}
|
||||
} else {
|
||||
res = new SocketResponse(SocketStatus.INVALID_REQUEST, SocketResponse.Message.WRONG_PASSWORD);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
res = new SocketResponse(SocketStatus.ERROR, SocketResponse.Message.UNKNOWN_ERROR, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
case LOGOUT -> {
|
||||
String userId = gson.fromJson(gson.toJson(msg.getPayload()), String.class);
|
||||
logger.info("Procesando LOGOUT para {}", userId);
|
||||
try (EntityManager em = conn.getEntityManager()) {
|
||||
SessionEntity session = sessionDAO.getByUserId(em, userId);
|
||||
if (session != null) {
|
||||
em.getTransaction().begin();
|
||||
em.remove(session);
|
||||
em.getTransaction().commit();
|
||||
res = new SocketResponse(SocketStatus.OK, SocketResponse.Message.LOGGED_OUT);
|
||||
} else {
|
||||
res = new SocketResponse(SocketStatus.SESSION_EXPIRED, SocketResponse.Message.SESSION_EXPIRED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case SEND_MESSAGE -> {
|
||||
MessageDTO dto = gson.fromJson(gson.toJson(msg.getPayload()), MessageDTO.class);
|
||||
|
||||
logger.info("Procesando SEND_MESSAGE de {} a Servidor | {}", dto.getFromUserName(), dto.getMessageText());
|
||||
|
||||
EntityManager em = conn.getEntityManager();
|
||||
try {
|
||||
UserEntity fromUser = userDAO.getByUserName(em, dto.getFromUserName());
|
||||
|
||||
if (fromUser == null || !sessionDAO.isLoggedIn(em, fromUser.getUserId())) {
|
||||
res = new SocketResponse(SocketStatus.SESSION_EXPIRED, SocketResponse.Message.SESSION_EXPIRED);
|
||||
} else if (dto.getMessageText().isBlank()) {
|
||||
res = new SocketResponse(SocketStatus.INVALID_REQUEST, SocketResponse.Message.INVALID_AMOUNT);
|
||||
} else {
|
||||
res = new SocketResponse(SocketStatus.OK, SocketResponse.Message.MESSAGE_SENT, dto.getMessageText());
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
res = new SocketResponse(SocketStatus.ERROR, SocketResponse.Message.UNKNOWN_ERROR, e.getMessage());
|
||||
} finally {
|
||||
conn.close();
|
||||
}
|
||||
}
|
||||
|
||||
default -> {
|
||||
logger.warn("Acción desconocida recibida: {}", msg.getAction());
|
||||
res = new SocketResponse(SocketStatus.INVALID_REQUEST, SocketResponse.Message.UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
output.println(gson.toJson(res));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.miarma.byodsec.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.net.ssl.SSLServerSocket;
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
|
||||
import net.miarma.byodsec.common.ConfigManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
|
||||
import net.miarma.byodsec.common.db.DBConnector;
|
||||
import net.miarma.byodsec.common.db.dao.SessionDAO;
|
||||
import net.miarma.byodsec.common.db.dao.UserDAO;
|
||||
|
||||
public class RemoteSocket {
|
||||
static void main(String[] args) throws IOException {
|
||||
ConfigManager configManager = ConfigManager.getInstance();
|
||||
configManager.loadConfig();
|
||||
DBConnector conn = DBConnector.getInstance();
|
||||
UserDAO userDAO = new UserDAO();
|
||||
SessionDAO sessionDAO = new SessionDAO();
|
||||
Gson gson = new Gson();
|
||||
final Logger logger = LoggerFactory.getLogger(RemoteSocket.class);
|
||||
|
||||
// especificamos mediante propiedades el KS y la contraseña
|
||||
System.setProperty("javax.net.ssl.keyStore", configManager.getJksFile().getAbsolutePath());
|
||||
System.setProperty("javax.net.ssl.keyStorePassword", configManager.getStringProperty("jksPassword"));
|
||||
System.setProperty("javax.net.ssl.trustStore", configManager.getJksFile().getAbsolutePath());
|
||||
System.setProperty("javax.net.ssl.trustStorePassword", configManager.getStringProperty("jksPassword"));
|
||||
|
||||
// inicializamos el socket SSL, la versión de TLS y los Cipher Suites a utilizar
|
||||
SSLServerSocketFactory factory = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault();
|
||||
SSLServerSocket serverSocket = (SSLServerSocket) factory.createServerSocket(6969);
|
||||
serverSocket.setEnabledProtocols(new String[]{ "TLSv1.3" });
|
||||
serverSocket.setEnabledCipherSuites(new String[]{
|
||||
"TLS_AES_256_GCM_SHA384",
|
||||
"TLS_AES_128_GCM_SHA256",
|
||||
"TLS_CHACHA20_POLY1305_SHA256"
|
||||
});
|
||||
serverSocket.setNeedClientAuth(true);
|
||||
|
||||
// main loop
|
||||
Executor executor = Executors.newFixedThreadPool(300);
|
||||
while (true) {
|
||||
SSLSocket socket = (SSLSocket) serverSocket.accept();
|
||||
executor.execute(new ClientHandler(socket, gson, userDAO, sessionDAO, conn, logger));
|
||||
}
|
||||
}
|
||||
}
|
||||
29
BYODSEC/src/main/resources/META-INF/persistence.xml
Normal file
29
BYODSEC/src/main/resources/META-INF/persistence.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
|
||||
version="2.0">
|
||||
|
||||
<persistence-unit name="ssii-pai2">
|
||||
<description>
|
||||
Persistence unit for PAI-2
|
||||
</description>
|
||||
|
||||
<class>net.miarma.byodsec.common.db.entities.UserEntity</class>
|
||||
<class>net.miarma.byodsec.common.db.entities.SessionEntity</class>
|
||||
|
||||
<properties>
|
||||
<property name="jakarta.persistence.jdbc.url" value="jdbc:mariadb://miarma.net:3307/pai2" />
|
||||
<property name="jakarta.persistence.jdbc.user" value="admin" />
|
||||
<property name="jakarta.persistence.jdbc.password" value="55ii.P4I.1;" />
|
||||
<property name="jakarta.persistence.jdbc.driver" value="org.mariadb.jdbc.Driver"/>
|
||||
<property name="hibernate.dialect" value="org.hibernate.dialect.MariaDBDialect" />
|
||||
|
||||
<property name="hibernate.show_sql" value="true" />
|
||||
<property name="hibernate.format_sql" value="true" />
|
||||
<property name="hibernate.highlight_sql" value="true" />
|
||||
</properties>
|
||||
|
||||
</persistence-unit>
|
||||
|
||||
</persistence>
|
||||
|
||||
1
BYODSEC/src/main/resources/default.properties
Normal file
1
BYODSEC/src/main/resources/default.properties
Normal file
@@ -0,0 +1 @@
|
||||
jksPassword=
|
||||
BIN
BYODSEC/src/main/resources/images/banco.png
Normal file
BIN
BYODSEC/src/main/resources/images/banco.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 23 KiB |
BIN
BYODSEC/src/main/resources/images/logo.png
Normal file
BIN
BYODSEC/src/main/resources/images/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
53
BYODSEC/src/main/resources/logback.xml
Normal file
53
BYODSEC/src/main/resources/logback.xml
Normal file
@@ -0,0 +1,53 @@
|
||||
<configuration>
|
||||
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<appender name="CLIENT" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/client.log</file>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/client-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
<cleanHistoryOnStart>true</cleanHistoryOnStart>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<appender name="SERVER" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<file>log/server.log</file>
|
||||
<encoder>
|
||||
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
</encoder>
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>log/server-%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<maxHistory>30</maxHistory>
|
||||
<cleanHistoryOnStart>true</cleanHistoryOnStart>
|
||||
</rollingPolicy>
|
||||
</appender>
|
||||
|
||||
<logger name="net.miarma.byodsec.ui" level="INFO" additivity="false">
|
||||
<appender-ref ref="CLIENT"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
<logger name="net.miarma.byodsec.client.ClientSocket" level="INFO" additivity="false">
|
||||
<appender-ref ref="CLIENT"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
|
||||
<logger name="net.miarma.byodsec.server.RemoteSocket" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="SERVER"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
<logger name="net.miarma.byodsec.db" level="INFO" additivity="false">
|
||||
<appender-ref ref="SERVER"/>
|
||||
<appender-ref ref="STDOUT"/>
|
||||
</logger>
|
||||
|
||||
<root level="DEBUG">
|
||||
<appender-ref ref="STDOUT" />
|
||||
</root>
|
||||
</configuration>
|
||||
27
BYODSEC/src/main/resources/scripts/db.sql
Normal file
27
BYODSEC/src/main/resources/scripts/db.sql
Normal file
@@ -0,0 +1,27 @@
|
||||
USE pai2;
|
||||
|
||||
-- Cleanup
|
||||
DROP TABLE IF EXISTS sessions;
|
||||
DROP TABLE IF EXISTS users;
|
||||
-- END Cleanup
|
||||
|
||||
-- DDL
|
||||
CREATE TABLE users (
|
||||
userId UUID NOT NULL PRIMARY KEY,
|
||||
userName VARCHAR(64) NOT NULL UNIQUE,
|
||||
password VARCHAR(256) NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE sessions (
|
||||
sessionId UUID NOT NULL PRIMARY KEY,
|
||||
userId UUID NOT NULL UNIQUE,
|
||||
createdAt TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
expiresAt TIMESTAMP AS (createdAt + INTERVAL 15 MINUTE) STORED,
|
||||
FOREIGN KEY (userId) REFERENCES users(userId) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE EVENT IF NOT EXISTS clear_expired_sessions
|
||||
ON SCHEDULE EVERY 1 MINUTE
|
||||
DO
|
||||
DELETE FROM sessions WHERE expiresAt <= NOW();
|
||||
-- END DDL
|
||||
Reference in New Issue
Block a user