diff --git a/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/AccountService.java b/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/AccountService.java
index 68a5a0ff..5a7c7ce3 100644
--- a/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/AccountService.java
+++ b/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/AccountService.java
@@ -111,7 +111,7 @@ public class AccountService {
* Renvoie la liste des comptes utilisateurs contenus dans la base de données sous forme d'une liste d'objets
* {@link Account}. Seuls les noms d'utilisateurs ainsi que les niveaux de permissions sont récupérés, les
* autres champs étant volontairement initialisés avec une valeur {@code null}.
- * @return Une liste d'objet {@link Account} représsentant les différents comptes utilisateurs existant dans la base
+ * @return Une liste d'objet {@link Account} représentant les différents comptes utilisateurs existant dans la base
* de données.
*/
public List getAccountList(){
diff --git a/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/FileAccountDao.java b/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/FileAccountDao.java
index f0c96a68..3695b015 100644
--- a/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/FileAccountDao.java
+++ b/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/FileAccountDao.java
@@ -10,9 +10,19 @@ import com.pqt.server.tools.security.MD5HashTool;
import java.util.*;
import java.util.stream.Collectors;
-//TODO écrire Javadoc
//TODO ajouter logs
-public class FileAccountDao implements IAccountDao {
+
+/**
+ * Implémentation de l'interface {@link IAccountDao} utilisant un fichier contenant des objets sérialisés comme
+ * source de persistance de données.
+ *
+ * Cette classe n'est pas faite pour gérer les accès concurentiels au fichier assurant la persistance, et n'est donc pas
+ * thread-safe. Elle est conçue pour que tous les accès soient effectués depuis un même thread et depuis un unique objet.
+ *
+ * Cette classe manipule les mot de passe sous forme chiffrée via un système de hash (md5) + salt, et ne fait pas
+ * persister les mots de passes non-chiffrées. Les noms d'utilisateurs sont stockés sans chiffrage.
+ */
+class FileAccountDao implements IAccountDao {
private static final String ACCOUNT_FILE_NAME = "acc.pqt";
@@ -29,76 +39,107 @@ public class FileAccountDao implements IAccountDao {
loadFromFile();
}
+ /**
+ * Recherche une correspondance entre un objet {@link Account} et les objets {@link AccountEntry} contenu dans
+ * la collection {@code entries}. La correspondance se base sur la valeur renvoyée par {@link Account#getUsername()}
+ * et sur {@link AccountEntry#getUsername()}.
+ * @param account données à utiliser pour rechercher la correspondance.
+ * @param entries collection de données à utiliser pour rechercher la correspondance
+ * @return La première correspondance trouvée, ou {@code null} si aucune correspondance n'a pu être faite.
+ */
private AccountEntry lookupMatchingEntry(Account account, Collection entries){
return entries.stream().filter(accountEntry -> accountEntry.getUsername().equals(account.getUsername())).findFirst().orElse(null);
}
@Override
- public boolean isAccountConnected(Account account) {
+ public synchronized boolean isAccountConnected(Account account) {
return lookupMatchingEntry(account, connectedAccount)!=null;
}
@Override
- public boolean submitAccountCredentials(Account acc, boolean desiredState) {
- if(isAccountRegistered(acc)){
- if(desiredState!=isAccountConnected(acc)){
+ public synchronized boolean submitAccountCredentials(Account account, boolean desiredState) {
+ if(isAccountRegistered(account)){
+ if(desiredState!=isAccountConnected(account)){
if(desiredState)
- return connect(acc);
+ return connect(account);
else
- return disconnect(acc);
+ return disconnect(account);
}
}
return false;
}
+ /**
+ * Passe un compte déconnecté dans l'état connecté. N'effecctue le changement que si un compte déconnecté correspond
+ * aux données fournies et que le mot de passe fournit par {@code account.getPassword()} corresspond à celui du
+ * compte correspondant.
+ *
+ * @param account données à utiliser pour effectuer la correspondance et l'identification
+ * @return {@code true} si le changement d'état a eu lieu, {@code false sinon}
+ */
private boolean connect(Account account){
- Optional entry = accountEntries.stream().filter(accountEntry -> accountEntry.getUsername().equals(account.getUsername())).findFirst();
- if(!entry.isPresent())
+ AccountEntry entry = lookupMatchingEntry(account, accountEntries);
+ if(entry==null)
return false;
else{
- String expectedUsername = entry.get().getUsername();
- String expectedPasswordHash = entry.get().getPasswordHash();
- String salt = entry.get().getSalt();
+ String expectedUsername = entry.getUsername();
+ String expectedPasswordHash = entry.getPasswordHash();
+ String salt = entry.getSalt();
if(expectedUsername.equals(account.getUsername()) && hashTool.hashAndSalt(account.getPassword(), salt).equals(expectedPasswordHash)){
- connectedAccount.add(entry.get());
+ connectedAccount.add(entry);
return true;
}else
return false;
}
}
+ /**
+ * Passe un comtpe connecté dans l'état déconnecté. N'effectue le changement que si un compte connecté correspond
+ * aux données fournies.
+ * @param account données à utiliser pour efffectuer la correspondance avec un compte
+ * @return {@code true} si le changement d'état a eu lieu, {@code false sinon}
+ */
private boolean disconnect(Account account){
- Optional entry = accountEntries.stream().filter(accountEntry -> accountEntry.getUsername().equals(account.getUsername())).findFirst();
- if(entry.isPresent() && connectedAccount.contains(entry.get())){
- connectedAccount.remove(entry.get());
+ AccountEntry entry = lookupMatchingEntry(account, accountEntries);
+ if(entry!=null && connectedAccount.contains(entry)){
+ connectedAccount.remove(entry);
return true;
}
return false;
}
@Override
- public boolean isAccountRegistered(Account account) {
+ public synchronized boolean isAccountRegistered(Account account) {
return lookupMatchingEntry(account, accountEntries)!=null;
}
@Override
- public AccountLevel getAccountPermissionLevel(Account account) {
+ public synchronized AccountLevel getAccountPermissionLevel(Account account) {
if(isAccountRegistered(account))
return lookupMatchingEntry(account, accountEntries).getLevel();
return null;
}
@Override
- public List getAccountList() {
+ public synchronized List getAccountList() {
return accountEntries.stream().map(accountEntry -> new Account(accountEntry.getUsername(), null, accountEntry.getLevel())).collect(Collectors.toList());
}
+ /**
+ * Sauvegarde les données des comptes dans le fichier de sauvegarde.
+ */
private void saveToFile(){
fileManager.saveSetToFile(accountEntries);
}
+ /**
+ * Charge les données des comptes depuis le fichier de sauvegarde.
+ *
+ * Attention : pour des raisons de cohérence des données, tous les comptes connectés sont repassés dans l'état
+ * déconnectés une fois le chargement fait.
+ */
private void loadFromFile(){
this.accountEntries = new HashSet<>(fileManager.loadSetFromFile());
//TODO faire check des comptes au lieu de tout déconnecter?
diff --git a/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/IAccountDao.java b/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/IAccountDao.java
index 3c9302a7..04c70f81 100644
--- a/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/IAccountDao.java
+++ b/Workspace/server/src/main/WEB-INF/classes/com/pqt/server/module/account/IAccountDao.java
@@ -5,16 +5,58 @@ import com.pqt.core.entities.user_account.AccountLevel;
import java.util.List;
-//TODO écrire Javadoc
-public interface IAccountDao {
+/**
+ * Cette interface définit les méthodes de base que doivent avoir les classes de DAO du service {@link AccountService}.
+ *
+ * Les implémentations de cette interface sont censé assurer la persistance des donnéess de connexions des comptes
+ * utilisateurs et doit également garder une trace niveau runtime de l'état des comptes (état connecté ou état
+ * déconnecté). Enfin, les implémentations doivent pouvoir déterminer une correspondance entre un nom d'utilisateur et
+ * un compte, et doit pouvoir effectuer les changements d'état sur la base d'un mot de passe non-chiffré fournit grâce
+ * à une instance {@link Account} (voir {@link #submitAccountCredentials(Account, boolean)}).
+ *
+ * @author Guillaume "Cess" Prost
+ */
+interface IAccountDao {
+ /**
+ * @see AccountService#isAccountConnected(Account)
+ *
+ * @param account données à utiliser
+ * @return {@code true} si les données correspondent à un compte et que ce dernier est connecté, {@code false}
+ * sinon.
+ */
boolean isAccountConnected(Account account);
- boolean submitAccountCredentials(Account acc, boolean desiredState);
+ /**
+ * @see AccountService#submitAccountCredentials(Account, boolean)
+ *
+ * @param account données à utiliser
+ * @param desiredState état désiré pour le compte
+ * @return {@code true} si les données correspondent à un compte et que le changement d'état a eu lieu,
+ * {@code false} sinon.
+ */
+ boolean submitAccountCredentials(Account account, boolean desiredState);
+ /**
+ * @see AccountService#isAccountRegistered(Account)
+ *
+ * @param account données à utiliser
+ * @return {@code true} si les données correspondent à un compte, {@code false} sinon.
+ */
boolean isAccountRegistered(Account account);
+ /**
+ * @see AccountService#getAccountPermissionLevel(Account)
+ * @param account données à utiliser
+ * @return Le niveau d'accréditation du compte utilisateur correspondant aux données, ou {@code null} si aucun
+ * compte ne correspond.
+ */
AccountLevel getAccountPermissionLevel(Account account);
+ /**
+ * @see AccountService#getAccountList()
+ * @return Une liste d'objet {@link Account} représentant les différents comptes utilisateurs existant dans la base
+ * de données.
+ */
List getAccountList();
}