Module Server, packg account : ajout javadoc

This commit is contained in:
Notmoo 2017-08-02 17:00:07 +02:00
parent 60fb6a069c
commit 5e5a114085
3 changed files with 107 additions and 24 deletions

View File

@ -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}. <b>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}.</b>
* @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<Account> getAccountList(){

View File

@ -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.
* <p/>
* 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.
* <p/>
* 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<AccountEntry> 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<AccountEntry> 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<AccountEntry> 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<Account> getAccountList() {
public synchronized List<Account> 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.
* <p/>
* <b>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.</b>
*/
private void loadFromFile(){
this.accountEntries = new HashSet<>(fileManager.loadSetFromFile());
//TODO faire check des comptes au lieu de tout déconnecter?

View File

@ -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}.
* <p/>
* 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<Account> getAccountList();
}