diff --git a/.github/workflows/deploiement.yml b/.github/workflows/deploiement.yml
new file mode 100644
index 0000000..ee7acc3
--- /dev/null
+++ b/.github/workflows/deploiement.yml
@@ -0,0 +1,34 @@
+name: Déploiement
+
+on:
+ push:
+ tags: [ '**' ]
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ submodules: recursive
+ fetch-depth: 0
+ - name: Setup .NET
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 8.0.x
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build WebService --no-restore /warnaserror -c Release
+ - name: Setup Python
+ uses: actions/setup-python@v4.7.1
+ with:
+ python-version: '3.11'
+ - name: Install Paramiko
+ run: python3 -m pip install paramiko
+ - name: Deploy build
+ env:
+ DEPLOY_HOSTNAME: ${{ secrets.DEPLOY_HOSTNAME }}
+ DEPLOY_USERNAME: ${{ secrets.DEPLOY_USERNAME }}
+ DEPLOY_PASSWORD: ${{ secrets.DEPLOY_PASSWORD }}
+ run: python3 .deployment/galliumplus-deploy
\ No newline at end of file
diff --git "a/.github/workflows/V\303\251rifications.yml" b/.github/workflows/verifications.yml
similarity index 57%
rename from ".github/workflows/V\303\251rifications.yml"
rename to .github/workflows/verifications.yml
index df84d81..ac90b2c 100644
--- "a/.github/workflows/V\303\251rifications.yml"
+++ b/.github/workflows/verifications.yml
@@ -1,45 +1,12 @@
name: Vérifications
on:
- push:
- branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
- build:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- with:
- submodules: recursive
- - name: Setup .NET
- uses: actions/setup-dotnet@v3
- with:
- dotnet-version: 8.0.x
- - name: Restore dependencies
- run: dotnet restore
- - name: Build
- run: dotnet build WebService --no-restore /warnaserror -c Release
- - name: Setup Python
- uses: actions/setup-python@v4.7.1
- with:
- python-version: '3.11'
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- - name: Install Paramiko
- run: python3 -m pip install paramiko
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
- - name: Deploy build
- env:
- DEPLOY_HOSTNAME: ${{ secrets.DEPLOY_HOSTNAME }}
- DEPLOY_USERNAME: ${{ secrets.DEPLOY_USERNAME }}
- DEPLOY_PASSWORD: ${{ secrets.DEPLOY_PASSWORD }}
- run: python3 .deployment/galliumplus-deploy
- if: github.event_name == 'push' && github.ref == 'refs/heads/main'
-
unit-tests:
runs-on: ubuntu-latest
- if: github.event_name == 'pull_request'
env:
GALLIUM_ENV: test
GALLIUM_HTTP: 5080
@@ -51,13 +18,16 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v3
with:
- dotnet-version: 8.0.x
+ dotnet-version: 8.x
+ - name: Restore dependencies
+ run: dotnet restore
+ - name: Build
+ run: dotnet build WebService --no-restore /warnaserror -c Release
- name: Run unit tests
run: dotnet test CoreTests
api-tests:
runs-on: ubuntu-latest
- if: github.event_name == 'pull_request'
env:
GALLIUM_ENV: test
GALLIUM_HTTP: 5080
diff --git a/.tests/main.py b/.tests/main.py
index 3368397..8e17c0e 100644
--- a/.tests/main.py
+++ b/.tests/main.py
@@ -9,4 +9,4 @@
if __name__ == "__main__":
decimal.DefaultContext.prec = 2
- Launcher.launch("1.2.0")
+ Launcher.launch("1.3.0")
diff --git a/.tests/requirements.txt b/.tests/requirements.txt
index 5a07a18..b42b996 100644
--- a/.tests/requirements.txt
+++ b/.tests/requirements.txt
@@ -1,6 +1,6 @@
-certifi==2023.5.7
+certifi==2025.1.31
charset-normalizer==3.1.0
-idna==3.4
+idna==3.10
psutil==5.9.5
PyJWT==2.8.0
requests==2.32.3
diff --git a/.tests/tests/__init__.py b/.tests/tests/__init__.py
index e467beb..a7f894e 100644
--- a/.tests/tests/__init__.py
+++ b/.tests/tests/__init__.py
@@ -1,7 +1,8 @@
+from .access_tests import AccessTests
from .application_tests import ApplicationTests
from .category_tests import CategoryTests
+from .order_tests import OrderTests
+from .pricing_tests import PricingTests
from .product_tests import ProductTests
from .role_tests import RoleTests
-from .order_tests import OrderTests
from .user_tests import UserTests
-from .access_tests import AccessTests
diff --git a/.tests/tests/access_tests.py b/.tests/tests/access_tests.py
index a838e8b..2f7c706 100644
--- a/.tests/tests/access_tests.py
+++ b/.tests/tests/access_tests.py
@@ -93,7 +93,7 @@ def test_login_permissions_normal(self):
self.expect(president_session)
.to.have.an_item("permissions")
.of.type(int)
- .that._is.equal_to(2047) # president, full permissions
+ .that._is.equal_to(4095) # president, full permissions
)
member_token = self.expect(member_session).to.have.an_item("token").value
@@ -217,7 +217,7 @@ def test_login_permissions_minimum(self):
self.expect(president_session)
.to.have.an_item("permissions")
.of.type(int)
- .that._is.equal_to(2047) # president, full permissions
+ .that._is.equal_to(4095) # president, full permissions
)
member_token = self.expect(member_session).to.have.an_item("token").value
diff --git a/.tests/tests/audit_tests_helpers.py b/.tests/tests/audit_tests_helpers.py
index 586f7e5..727337f 100644
--- a/.tests/tests/audit_tests_helpers.py
+++ b/.tests/tests/audit_tests_helpers.py
@@ -22,9 +22,9 @@
"PriceAdded": 0x161,
"PriceModified": 0x162,
"PriceDeleted": 0x163,
- "PricingTypeAdded": 0x171,
- "PricingTypeModified": 0x172,
- "PricingTypeDeleted": 0x173,
+ "PriceListAdded": 0x171,
+ "PriceListModified": 0x172,
+ "PriceListDeleted": 0x173,
"RoleAdded": 0x181,
"RoleModified": 0x182,
"RoleDeleted": 0x183,
diff --git a/.tests/tests/category_tests.py b/.tests/tests/category_tests.py
index 0a30e6c..011e0b5 100644
--- a/.tests/tests/category_tests.py
+++ b/.tests/tests/category_tests.py
@@ -71,7 +71,7 @@ def test_category_create(self):
# Informations manquantes
invalid_category = {}
- response = self.post("users", invalid_category)
+ response = self.post("categories", invalid_category)
self.expect(response.status_code).to.be.equal_to(400)
self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
"InvalidResource"
@@ -80,7 +80,7 @@ def test_category_create(self):
# Informations non valides
invalid_category = {"name": ""}
- response = self.post("users", invalid_category)
+ response = self.post("categories", invalid_category)
self.expect(response.status_code).to.be.equal_to(400)
self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
"InvalidResource"
diff --git a/.tests/tests/pricing_tests.py b/.tests/tests/pricing_tests.py
new file mode 100644
index 0000000..6469804
--- /dev/null
+++ b/.tests/tests/pricing_tests.py
@@ -0,0 +1,172 @@
+from utils.test_base import TestBase
+from utils.auth import BearerAuth
+from .audit_tests_helpers import AuditTestHelpers
+
+
+class PricingTests(TestBase):
+ def setUp(self):
+ super().setUp()
+ self.set_authentication(BearerAuth("09876543210987654321"))
+ self.audit = AuditTestHelpers(self, 1, 3)
+
+ def tearDown(self):
+ self.unset_authentication()
+
+ def test_price_list_get_all(self):
+ response = self.get("price-lists")
+ self.expect(response.status_code).to.be.equal_to(200)
+
+ price_lists = response.json()
+ self.expect(price_lists).to.be.a(list)._and._not.empty()
+
+ price_list = price_lists[0]
+ self.expect(price_list).to.have.an_item("id").of.type(int)
+ self.expect(price_list).to.have.an_item("longName").of.type(str)
+ self.expect(price_list).to.have.an_item("shortName").of.type(str)
+ self.expect(price_list).to.have.an_item("requiresMembership").of.type(bool)
+
+ def test_price_list_get_one(self):
+ existing_id = self.get("price-lists").json()[0]["id"]
+ invalid_id = 12345
+
+ # Test avec un tarif existant
+
+ response = self.get(f"price-lists/{existing_id}")
+ self.expect(response.status_code).to.be.equal_to(200)
+
+ price_list = response.json()
+ self.expect(price_list).to.be.a(dict)
+ self.expect(price_list).to.have.an_item("id").of.type(int)
+ self.expect(price_list).to.have.an_item("longName").of.type(str)
+ self.expect(price_list).to.have.an_item("shortName").of.type(str)
+ self.expect(price_list).to.have.an_item("requiresMembership").of.type(bool)
+
+ # Test avec un tarif inexistant
+
+ response = self.get(f"price-lists/{invalid_id}")
+ self.expect(response.status_code).to.be.equal_to(404)
+
+ # A faire quand on gèrera les évènements
+
+ # def test_price_list_create(self):
+ # previous_price_list_count = len(self.get("price-lists").json())
+
+ # valid_price_list = {
+ # "longName": "Tarif spécial EtiSmash",
+ # "shortName": "EtiSmash",
+ # "requiresMembership": False,
+ # # ajouter le champ "applicableDuring" ici ?
+ # }
+
+ # with self.audit.watch():
+ # response = self.post("price-lists", valid_price_list)
+ # self.expect(response.status_code).to.be.equal_to(201)
+ # location = self.expect(response.headers).to.have.an_item("Location").value
+
+ # created_price_list = response.json()
+ # self.expect(created_price_list).to.have.an_item("id")
+ # self.expect(created_price_list["name"]).to.be.equal_to("Jus")
+
+ # response = self.get(location)
+ # self.expect(response.status_code).to.be.equal_to(200)
+ # created_price_list = response.json()
+ # self.expect(created_price_list["name"]).to.be.equal_to("Jus")
+
+ # new_price_list_count = len(self.get("price-lists").json())
+ # self.expect(new_price_list_count).to.be.equal_to(previous_price_list_count + 1)
+
+ # self.audit.expect_entries(self.audit.price_list_added_action("Jus", "eb069420"))
+
+ # for invalid_price_list in INVALID_PRICE_LISTS:
+ # response = self.post("price-lists", invalid_price_list)
+ # self.expect(response.status_code).to.be.equal_to(400)
+ # self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
+ # "InvalidResource"
+ # )
+
+ def test_price_list_edit(self):
+ valid_price_list = self.get("price-lists").json()[-1]
+ valid_price_list.update(shortName="TEST")
+ price_list_id = valid_price_list["id"]
+
+ with self.audit.watch():
+ response = self.put(f"price-lists/{price_list_id}", valid_price_list)
+ self.expect(response.status_code).to.be.equal_to(200)
+
+ edited_price_list = self.get(f"price-lists/{price_list_id}").json()
+ self.expect(edited_price_list["shortName"]).to.be.equal_to("TEST")
+
+ self.audit.expect_entries(
+ self.audit.entry(
+ "PriceListModified", id=price_list_id, name="Tarif test adhérent"
+ )
+ )
+
+ # Tarif qui n'existe pas
+
+ response = self.put("price-lists/12345", valid_price_list)
+ self.expect(response.status_code).to.be.equal_to(404)
+
+ # Informations manquantes
+
+ invalid_price_list = {}
+ response = self.put(f"price-lists/{price_list_id}", invalid_price_list)
+ self.expect(response.status_code).to.be.equal_to(400)
+ self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
+ "InvalidResource"
+ )
+
+ # Informations non valides
+
+ invalid_price_list = {"shortName": "long comme un lundi"}
+ response = self.put(f"price-lists/{price_list_id}", invalid_price_list)
+ self.expect(response.status_code).to.be.equal_to(400)
+ self.expect(response.json()).to.have.an_item("code").that._is.equal_to(
+ "InvalidResource"
+ )
+
+ # Pareil que pour test_price_list_create
+
+ # def test_price_list_delete(self):
+ # price_list = {"name": "Jus"}
+ # location = self.post("price-lists", price_list).headers["Location"]
+
+ # # On supprimme la catégorie
+
+ # with self.audit.watch():
+ # response = self.delete(location)
+ # self.expect(response.status_code).to.be.equal_to(200)
+
+ # self.audit.expect_entries(
+ # self.audit.price_list_deleted_action("Jus", "eb069420")
+ # )
+
+ # # La catégorie n'existe plus
+
+ # response = self.get(location)
+ # self.expect(response.status_code).to.be.equal_to(404)
+
+ # # On ne peut plus le supprimer
+
+ # response = self.delete(location)
+ # self.expect(response.status_code).to.be.equal_to(404)
+
+ def test_price_list_no_authentification(self):
+ self.unset_authentication()
+
+ response = self.get("price-lists")
+ self.expect(response.status_code).to.be.equal_to(401)
+ response = self.get("price-lists/1")
+ self.expect(response.status_code).to.be.equal_to(401)
+ response = self.put("price-lists/1", {})
+ self.expect(response.status_code).to.be.equal_to(401)
+
+ def test_price_list_no_permission(self):
+ self.set_authentication(BearerAuth("12345678901234567890"))
+
+ response = self.get("price-lists")
+ self.expect(response.status_code).to.be.equal_to(403)
+ response = self.get("price-lists/1")
+ self.expect(response.status_code).to.be.equal_to(403)
+ response = self.put("price-lists/1", {})
+ self.expect(response.status_code).to.be.equal_to(403)
diff --git a/.tests/utils/expectations.py b/.tests/utils/expectations.py
index c831079..45ed2f3 100644
--- a/.tests/utils/expectations.py
+++ b/.tests/utils/expectations.py
@@ -16,24 +16,11 @@ def __init__(self, test_case: TestCase, value):
self.nullable = False
def __check_measurable(self):
- if not (
- isinstance(self.value, str)
- or isinstance(self.value, list)
- or isinstance(self.value, tuple)
- or isinstance(self.value, dict)
- or isinstance(self.value, set)
- or hasattr(self.value, "__len__")
- ):
+ if not hasattr(self.value, "__len__"):
raise ValueError(f"Values of type {type(self.value)} have no length")
def __check_collection(self):
- if not (
- isinstance(self.value, dict)
- or isinstance(self.value, list)
- or isinstance(self.value, tuple)
- or isinstance(self.value, set)
- or hasattr(self.value, "__getitem__")
- ):
+ if not hasattr(self.value, "__getitem__"):
raise ValueError(f"{type(self.value)} is not a collection type")
# ASSERTIONS
diff --git a/Core/Applications/AppAccess.cs b/Core/Applications/AppAccess.cs
index a11a4be..eda90fd 100644
--- a/Core/Applications/AppAccess.cs
+++ b/Core/Applications/AppAccess.cs
@@ -8,7 +8,7 @@ namespace GalliumPlus.Core.Applications;
///
public class AppAccess
{
- [Key]
+ [PrimaryKey]
private readonly int id;
private readonly OneTimeSecret secret;
diff --git a/Core/Applications/Client.cs b/Core/Applications/Client.cs
index 2f386b5..a6d293d 100644
--- a/Core/Applications/Client.cs
+++ b/Core/Applications/Client.cs
@@ -1,6 +1,6 @@
using System.Text.Json.Serialization;
using GalliumPlus.Core.Random;
-using GalliumPlus.Core.Users;
+using GalliumPlus.Core.Security;
using KiwiQuery.Mapped;
namespace GalliumPlus.Core.Applications;
@@ -10,12 +10,12 @@ namespace GalliumPlus.Core.Applications;
///
public class Client
{
- [Key]
+ [PrimaryKey]
private int id;
private string apiKey;
private string name;
- private Permissions granted;
- private Permissions allowed;
+ private Permission granted;
+ private Permission allowed;
private bool isEnabled;
[HasOne("id")]
@@ -54,7 +54,7 @@ public string Name
///
/// Les permissions autorisées aux utilisateurs de l'application.
///
- public Permissions Allowed
+ public Permission Allowed
{
get => this.allowed;
set => this.allowed = value;
@@ -63,7 +63,7 @@ public Permissions Allowed
///
/// Les permissions données à tous les utilisteurs de l'application.
///
- public Permissions Granted
+ public Permission Granted
{
get => this.granted;
set => this.granted = value;
@@ -125,8 +125,8 @@ public Client(
string apiKey,
string name,
bool isEnabled,
- Permissions allowed,
- Permissions granted
+ Permission allowed,
+ Permission granted
)
{
this.id = id;
@@ -147,8 +147,8 @@ Permissions granted
public Client(
string name,
bool isEnabled = true,
- Permissions allowed = Permissions.NONE,
- Permissions granted = Permissions.NONE
+ Permission allowed = Permission.None,
+ Permission granted = Permission.None
)
{
var rtg = new RandomTextGenerator(new BasicRandomProvider());
@@ -162,13 +162,16 @@ public Client(
///
/// Applique les filtres de permissions.
- /// Les ajouts () sont appliqués avant
- /// les restrictions ().
+ /// Les restrictions () sont appliquées par-dessus
+ /// les ajouts ().
///
/// Les permissions à filtrer.
/// Les permission restantes.
- public Permissions Filter(Permissions permissions)
+ public Permission Filter(Permission permissions)
{
- return permissions.Grant(this.granted).Mask(this.allowed);
+ permissions = Permissions.Current.Minimum(permissions);
+ permissions = Permissions.Current.Union(permissions, this.granted);
+ permissions = Permissions.Current.Intersection(permissions, this.allowed);
+ return permissions;
}
}
\ No newline at end of file
diff --git a/Core/Applications/SameSignOn.cs b/Core/Applications/SameSignOn.cs
index 7f42103..a89d6e9 100644
--- a/Core/Applications/SameSignOn.cs
+++ b/Core/Applications/SameSignOn.cs
@@ -10,7 +10,7 @@ namespace GalliumPlus.Core.Applications;
///
public class SameSignOn
{
- [Key]
+ [PrimaryKey]
private readonly int id;
private string secret;
private SignatureType signatureType;
@@ -59,13 +59,13 @@ public class SameSignOn
///
/// Si l'application a besoin ou non d'une clé d'API à la connexion.
///
- public bool RequiresApiKey => this.scope.Includes(SameSignOnScopes.Gallium);
+ public bool RequiresApiKey => SameSignOnScopes.Current.Gallium.IsIn(this.scope);
///
/// Crée un paramétrage SSO existant.
///
/// L'identifiant de l'application auquel les informations appartiennent.
- /// Le code secret utilisé pour signer les jeton d'authentification
+ /// Le code secret utilisé pour signer les jetons d'authentification
/// La méthode de signature utilisée.
/// La portée de l'accès aux informations des utilisateurs.
/// Le nom à afficher pour présenter l'application.
diff --git a/Core/Applications/SameSignOnScopes.cs b/Core/Applications/SameSignOnScopes.cs
index f02729e..58a3959 100644
--- a/Core/Applications/SameSignOnScopes.cs
+++ b/Core/Applications/SameSignOnScopes.cs
@@ -6,27 +6,27 @@ namespace GalliumPlus.Core.Applications;
/// La portée de l'accès Same Sign-On.
///
[Flags]
-public enum SameSignOnScope
+public enum SameSignOnScope : uint
{
///
/// Accès au strict minimum (identifiant utilisateur et identifiant immuable)
///
- Minimum = 0x00,
+ Minimum = 0x0,
///
/// Accès au profil de l'utilisateur (nom et prénom).
///
- Identity = 0x01,
+ Identity = 0x1,
///
/// Accès à l'adresse mail de l'utilisateur.
///
- Email = 0x02,
+ Email = 0x2,
///
/// Accès au rôle de l'utilisateur.
///
- Role = 0x04,
+ Role = 0x4,
///
/// Accès à l'API Gallium en tant que l'utilisateur connecté.
@@ -40,17 +40,29 @@ public enum SameSignOnScope
///
/// La portée de l'accès Same Sign-On.
///
-public class SameSignOnScopes
+public class SameSignOnScopes : EnumBitflagSet
{
+ private static SameSignOnScopes? current;
+
+ public static SameSignOnScopes Current => current ??= new SameSignOnScopes();
+
///
- public static readonly FlagEnum Identity = new(SameSignOnScope.Identity);
-
+ public Flag Identity { get; }
+
///
- public static readonly FlagEnum Email = new(SameSignOnScope.Email);
-
+ public Flag Email { get; }
+
///
- public static readonly FlagEnum Role = new(SameSignOnScope.Role);
-
+ public Flag Role { get; }
+
///
- public static readonly FlagEnum Gallium = new(SameSignOnScope.Gallium);
+ public Flag Gallium { get; }
+
+ private SameSignOnScopes()
+ {
+ this.Identity = this.Flag(SameSignOnScope.Identity);
+ this.Email = this.Flag(SameSignOnScope.Email);
+ this.Role = this.Flag(SameSignOnScope.Role);
+ this.Gallium = this.Flag(SameSignOnScope.Gallium);
+ }
}
\ No newline at end of file
diff --git a/Core/Core.csproj b/Core/Core.csproj
index 36a08e3..7d5742f 100644
--- a/Core/Core.csproj
+++ b/Core/Core.csproj
@@ -12,7 +12,7 @@
-
+
diff --git a/Core/Data/ICategoryDao.cs b/Core/Data/ICategoryDao.cs
index 2adaf21..5dcf925 100644
--- a/Core/Data/ICategoryDao.cs
+++ b/Core/Data/ICategoryDao.cs
@@ -2,6 +2,4 @@
namespace GalliumPlus.Core.Data;
-public interface ICategoryDao : IBasicDao
-{
-}
\ No newline at end of file
+public interface ICategoryDao : IBasicDao;
\ No newline at end of file
diff --git a/Core/Data/IPriceListDao.cs b/Core/Data/IPriceListDao.cs
new file mode 100644
index 0000000..3b0e725
--- /dev/null
+++ b/Core/Data/IPriceListDao.cs
@@ -0,0 +1,5 @@
+using GalliumPlus.Core.Stocks;
+
+namespace GalliumPlus.Core.Data;
+
+public interface IPriceListDao : IBasicDao;
\ No newline at end of file
diff --git a/Core/Exceptions/PermissionDeniedException.cs b/Core/Exceptions/PermissionDeniedException.cs
index 8631643..4e12496 100644
--- a/Core/Exceptions/PermissionDeniedException.cs
+++ b/Core/Exceptions/PermissionDeniedException.cs
@@ -1,4 +1,4 @@
-using GalliumPlus.Core.Users;
+using GalliumPlus.Core.Security;
namespace GalliumPlus.Core.Exceptions;
@@ -8,12 +8,12 @@ namespace GalliumPlus.Core.Exceptions;
///
public class PermissionDeniedException : GalliumException
{
- private Permissions required;
+ private readonly Permission required;
///
- /// Les permissions qu étaient requises.
+ /// Les permissions qui étaient requises.
///
- public Permissions Required { get => this.required; }
+ public Permission Required => this.required;
public override ErrorCode ErrorCode => ErrorCode.PermissionDenied;
@@ -21,7 +21,7 @@ public class PermissionDeniedException : GalliumException
/// Instancie l'exception.
///
/// Les permissions requises pour effectuer l'action.
- public PermissionDeniedException(Permissions required)
+ public PermissionDeniedException(Permission required)
{
this.required = required;
}
diff --git a/Core/Items/PricingType.cs b/Core/Items/PricingType.cs
deleted file mode 100644
index 2a08b7d..0000000
--- a/Core/Items/PricingType.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace GalliumPlus.WebApi.Core.Items;
-
-public class PricingType(int id, string shortLabel, string longLabel, bool requiresMembership)
-{
- public int Id => id;
-
- public string ShortLabel => shortLabel;
-
- public string LongLabel => longLabel;
-
- public bool RequiresMembership => requiresMembership;
-}
\ No newline at end of file
diff --git a/Core/Logs/AuditLog.cs b/Core/Logs/AuditLog.cs
index 1b84ef8..9c3e8da 100644
--- a/Core/Logs/AuditLog.cs
+++ b/Core/Logs/AuditLog.cs
@@ -9,7 +9,7 @@ namespace GalliumPlus.Core.Logs;
///
public class AuditLog
{
- [Key]
+ [PrimaryKey]
private readonly int id;
private readonly LoggedAction action;
diff --git a/Core/Logs/Builders/AuditLogEntryBuilder.Generic.cs b/Core/Logs/Builders/AuditLogEntryBuilder.Generic.cs
index fd1c3af..54d648e 100644
--- a/Core/Logs/Builders/AuditLogEntryBuilder.Generic.cs
+++ b/Core/Logs/Builders/AuditLogEntryBuilder.Generic.cs
@@ -1,6 +1,3 @@
-using GalliumPlus.Core.Applications;
-using GalliumPlus.Core.Stocks;
-
namespace GalliumPlus.Core.Logs.Builders;
public partial class AuditLogEntryBuilder
diff --git a/Core/Logs/Builders/AuditLogEntryBuilder.cs b/Core/Logs/Builders/AuditLogEntryBuilder.cs
index a9473a7..d304a55 100644
--- a/Core/Logs/Builders/AuditLogEntryBuilder.cs
+++ b/Core/Logs/Builders/AuditLogEntryBuilder.cs
@@ -46,6 +46,17 @@ public IGenericEntryBuilder Category(Category category)
);
public IClientEntryBuilder Client(Client client) => new ClientEntryBuilder(client, this);
+
+
+ public IGenericEntryBuilder PriceList(PriceList priceList)
+ => new GenericEntryBuilder(
+ this,
+ LoggedAction.PriceListAdded, LoggedAction.PriceListModified, LoggedAction.PriceListDeleted,
+ root => {
+ root.details.Add("id", priceList.Id);
+ root.details.Add("name", priceList.LongName);
+ }
+ );
public IUserEntryBuilder User(User user) => new UserEntryBuilder(user, this);
}
\ No newline at end of file
diff --git a/Core/Logs/LoggedAction.cs b/Core/Logs/LoggedAction.cs
index 295e1c1..4e53d78 100644
--- a/Core/Logs/LoggedAction.cs
+++ b/Core/Logs/LoggedAction.cs
@@ -33,9 +33,9 @@ public enum LoggedAction : uint
// PriceModified = 0x162,
// PriceDeleted = 0x163,
- // PricingTypeAdded = 0x171,
- // PricingTypeModified = 0x172,
- // PricingTypeDeleted = 0x173,
+ PriceListAdded = 0x171,
+ PriceListModified = 0x172,
+ PriceListDeleted = 0x173,
// RoleAdded = 0x181,
// RoleModified = 0x182,
diff --git a/Core/Security/Permissions.cs b/Core/Security/Permissions.cs
index 23e9f19..efb58fc 100644
--- a/Core/Security/Permissions.cs
+++ b/Core/Security/Permissions.cs
@@ -4,151 +4,132 @@
* Vous pouvez rajouter de nouvelles permissions avec des puissances de 2
* (jusqu'à 2 147 483 648)
*
- * RÉFLÉCHISSEZ AUSSI AVANT DE RENOMMER/CHANGER LA SIGNIFICATION D'UNE PERMISSION
+ * RÉFLÉCHISSEZ AUSSI AVANT DE RENOMMER/CHANGER LA SIGNIFICATION D'UNE PERMISSION.
*/
-namespace GalliumPlus.Core.Users;
+using Multiflag;
+
+namespace GalliumPlus.Core.Security;
///
/// Permissions spéciales attribuées aux rôles.
///
[Flags]
-public enum Permissions : uint
+public enum Permission : uint
{
///
/// Accès en lecture aux produits et catégories.
///
- SEE_PRODUCTS_AND_CATEGORIES = 1,
-
- NOT_SEE_PRODUCTS_AND_CATEGORIES = 1 | NOT_MANAGE_PRODUCTS | NOT_MANAGE_CATEGORIES,
+ SeeProductsAndCategories = 0x1,
///
/// Gestion des produits (accès à tous, création, modification et suppression).
///
- MANAGE_PRODUCTS = 2 | SEE_PRODUCTS_AND_CATEGORIES,
-
- NOT_MANAGE_PRODUCTS = 2,
+ ManageProducts = 0x2,
///
/// Gestion des catégories (accès, création, modification et suppression).
///
- MANAGE_CATEGORIES = 4 | SEE_PRODUCTS_AND_CATEGORIES,
-
- NOT_MANAGE_CATEGORIES = 4,
+ ManageCategories = 0x4,
///
/// Accès en lecture à tous les comptes et rôles.
///
- SEE_ALL_USERS_AND_ROLES = 8,
-
- NOT_SEE_ALL_USERS_AND_ROLES = 8 | NOT_MANAGE_DEPOSITS | NOT_MANAGE_ROLES,
+ SeeAllUsersAndRoles = 0x8,
///
/// Gestion des acomptes (ajout et retrait).
///
- MANAGE_DEPOSITS = 16 | SEE_ALL_USERS_AND_ROLES,
-
- NOT_MANAGE_DEPOSITS = 16 | NOT_MANAGE_USERS,
+ ManageDeposits = 0x10,
///
/// Gestion des utilisateurs (création, modification et suppression de compte).
/// S'applique uniquement aux utilisateurs dont le rang est inférieur ou égal
/// au rang de l'utilisateur ayant cette permission.
///
- MANAGE_USERS = 32 | MANAGE_DEPOSITS,
-
- NOT_MANAGE_USERS = 32 | NOT_RESET_MEMBERSHIPS,
+ ManageUsers = 0x20,
///
/// Gestion des rôles (création, modification et suppression).
///
- MANAGE_ROLES = 64 | SEE_ALL_USERS_AND_ROLES,
-
- NOT_MANAGE_ROLES = 64,
+ ManageRoles = 0x40,
///
/// Accès aux logs.
///
- READ_LOGS = 128,
-
+ ReadLogs = 0x80,
+
///
/// Permission de révoquer toutes les adhésions.
///
- RESET_MEMBERSHIPS = 256 | MANAGE_USERS,
-
- NOT_RESET_MEMBERSHIPS = 256,
+ ResetMemberships = 0x100,
///
/// Permission de gérer les applications connectées à Gallium.
///
- MANAGE_CLIENTS = 512,
-
- NOT_MANAGE_CLIENTS = 512,
+ ManageClients = 0x200,
///
/// Permission d'utiliser les différents outils de développement associés à Gallium.
///
- USE_DEVELOPER_TOOLS = 1024,
-
- NOT_USE_DEVELOPER_TOOLS = 1024,
+ UseDeveloperTools = 0x400,
+
+ ///
+ /// Permission de gérer les tarifs des articles.
+ ///
+ ManagePrices = 0x800,
///
/// Permission de modifier les acomptes manuellement. Cette permission ne doit pas pouvoir être donnée à quiconque,
/// elle sert uniquement à garder la compatibilité avec Gallium V2.
///
- FORCE_DEPOSIT_MODIFICATION = 1073741824,
-
- NOT_FORCE_DEPOSIT_MODIFICATION = 1073741824,
+ ForceDepositModification = 0x40000000,
//=== PERMISSIONS COMPOSÉES ===//
///
/// Faire des ventes.
///
- SELL = MANAGE_PRODUCTS | MANAGE_DEPOSITS,
-
+ Sell = ManageProducts | ManageDeposits,
+
//=== VALEURS SPÉCIALES ===//
///
/// Aucune permission
///
- NONE = 0,
-
+ None = 0x0,
+
///
/// Toutes les permissions
///
- ALL = 2047,
+ All = 0xFFF
}
-public static class PermissionsExtensions
+public class Permissions : EnumBitflagSet
{
- ///
- /// Vérifie que est inclus dans ces permissions.
- ///
- /// Les permissions à tester.
- /// si les permissions sont incluses, sinon
- public static bool Includes(this Permissions @this, Permissions other)
- {
- return (@this & other) == other;
- }
+ private static Permissions? current;
- ///
- /// Ajoute à ces permissions.
- ///
- /// Les permissions à ajouter.
- /// Les permissions combinées.
- public static Permissions Grant(this Permissions @this, Permissions other)
- {
- return @this | other;
- }
+ public static Permissions Current => current ??= new Permissions();
- ///
- /// Enlève les permissions non présentes dans de ces permissions.
- ///
- /// Les permissions à garder.
- /// Les permissions restantes.
- public static Permissions Mask(this Permissions @this, Permissions other)
+ private Permissions()
{
- return @this & other;
+ var seeProductsAndCategories = this.Flag(Permission.SeeProductsAndCategories);
+ this.Flag(Permission.ManageProducts, seeProductsAndCategories);
+ this.Flag(Permission.ManageCategories, seeProductsAndCategories);
+ this.Flag(Permission.ManagePrices, seeProductsAndCategories);
+
+ var seeAllUsersAndRoles = this.Flag(Permission.SeeAllUsersAndRoles);
+ var manageDeposits = this.Flag(Permission.ManageDeposits, seeAllUsersAndRoles);
+ var manageUsers = this.Flag(Permission.ManageUsers, manageDeposits);
+ this.Flag(Permission.ManageRoles, seeAllUsersAndRoles);
+ this.Flag(Permission.ResetMemberships, manageUsers);
+
+ this.Flag(Permission.ReadLogs);
+
+ this.Flag(Permission.ManageClients);
+
+ this.Flag(Permission.UseDeveloperTools);
+
+ this.Flag(Permission.ForceDepositModification);
}
}
\ No newline at end of file
diff --git a/Core/Stocks/Availability.cs b/Core/Stocks/Availability.cs
index 8467c2c..7e21cdf 100644
--- a/Core/Stocks/Availability.cs
+++ b/Core/Stocks/Availability.cs
@@ -1,22 +1,22 @@
namespace GalliumPlus.Core.Stocks;
///
-/// Disponiblité d'un produit.
+/// La disponibilité d'un produit.
///
public enum Availability
{
///
- /// Le produit est disponible si il en reste en stock.
+ /// Le produit est disponible s'il en reste en stock.
///
- AUTO = 0,
+ Auto = 0,
///
/// Le produit est toujours considéré comme disponible.
///
- ALWAYS = 1,
+ Always = 1,
///
/// Le produit est toujours considéré comme indisponible.
///
- NEVER = 2,
+ Never = 2,
}
\ No newline at end of file
diff --git a/Core/Stocks/PriceList.cs b/Core/Stocks/PriceList.cs
new file mode 100644
index 0000000..6a7c17e
--- /dev/null
+++ b/Core/Stocks/PriceList.cs
@@ -0,0 +1,56 @@
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using KiwiQuery.Mapped;
+
+namespace GalliumPlus.Core.Stocks;
+
+///
+/// Représente un tarif, comme le tarif adhérent ou le tarif non-adhérent.
+///
+public class PriceList
+{
+ [PrimaryKey(AutoIncrement = true)]
+ private readonly int id;
+ private readonly string shortName;
+ private readonly string longName;
+ private readonly bool requiresMembership;
+
+ ///
+ /// Le code du tarif.
+ ///
+ public int Id => this.id;
+
+ ///
+ /// Le nom du tarif abrégé en un seul mot.
+ ///
+ [Required]
+ [MaxLength(16)]
+ public string ShortName => this.shortName;
+
+ ///
+ /// Le nom complet du tarif.
+ ///
+ [Required]
+ [MaxLength(50)]
+ public string LongName => this.longName;
+
+ ///
+ /// Une valeur de true indique que ce tarif est applicable uniquement aux adhérents.
+ ///
+ public bool RequiresMembership => this.requiresMembership;
+
+ ///
+ /// Instancie un code tarif.
+ ///
+ /// Le code du tarif.
+ /// Le nom abrégé du tarif.
+ /// Le nom complet du tarif.
+ /// Si le tarif est applicable uniquement aux adhérents.
+ public PriceList(int id, string shortName, string longName, bool requiresMembership)
+ {
+ this.id = id;
+ this.shortName = shortName;
+ this.longName = longName;
+ this.requiresMembership = requiresMembership;
+ }
+}
\ No newline at end of file
diff --git a/Core/Stocks/Product.cs b/Core/Stocks/Product.cs
index afef53b..f04a2c1 100644
--- a/Core/Stocks/Product.cs
+++ b/Core/Stocks/Product.cs
@@ -53,9 +53,9 @@ public class Product
///
public bool Available => this.Availability switch
{
- Availability.ALWAYS => true,
- Availability.AUTO => this.Stock > 0,
- Availability.NEVER => false,
+ Availability.Always => true,
+ Availability.Auto => this.Stock > 0,
+ Availability.Never => false,
// ignore les états invalides
_ => false,
diff --git a/Core/Users/Role.cs b/Core/Users/Role.cs
index 94700d4..c0ae752 100644
--- a/Core/Users/Role.cs
+++ b/Core/Users/Role.cs
@@ -1,3 +1,5 @@
+using GalliumPlus.Core.Security;
+
namespace GalliumPlus.Core.Users;
///
@@ -7,7 +9,7 @@ public class Role
{
private int id;
private string name;
- private Permissions permissions;
+ private Permission permissions;
///
/// L'identifiant du rôle.
@@ -22,7 +24,7 @@ public class Role
///
/// La somme des permissions attribuées au rôle.
///
- public Permissions Permissions { get => this.permissions; set => this.permissions = value; }
+ public Permission Permissions { get => this.permissions; set => this.permissions = value; }
///
/// Crée un rôle.
@@ -30,7 +32,7 @@ public class Role
/// L'identifiant du rôle.
/// Le nom affiché du rôle.
/// La somme des permissions attribuées au rôle.
- public Role(int id, string name, Permissions permissions)
+ public Role(int id, string name, Permission permissions)
{
this.id = id;
this.name = name;
diff --git a/Core/Users/Session.cs b/Core/Users/Session.cs
index 9582d44..7188588 100644
--- a/Core/Users/Session.cs
+++ b/Core/Users/Session.cs
@@ -1,5 +1,6 @@
using GalliumPlus.Core.Applications;
using GalliumPlus.Core.Random;
+using GalliumPlus.Core.Security;
namespace GalliumPlus.Core.Users;
@@ -55,7 +56,7 @@ public class Session
///
/// Les permissions accordées pour cette session.
///
- public Permissions Permissions => this.client.Filter(this.user?.Role.Permissions ?? Permissions.NONE);
+ public Permission Permissions => this.client.Filter(this.user?.Role.Permissions ?? Permission.None);
///
/// Indique si la session est expirée ou non, en prenant en compte l'inactivité.
diff --git a/CoreTests/Applications/ClientTest.cs b/CoreTests/Applications/ClientTest.cs
index 66ec263..b7574f3 100644
--- a/CoreTests/Applications/ClientTest.cs
+++ b/CoreTests/Applications/ClientTest.cs
@@ -1,119 +1,119 @@
-using GalliumPlus.Core.Users;
+using GalliumPlus.Core.Security;
namespace GalliumPlus.Core.Applications;
public class ClientTest
{
- public static readonly Regex ReApiKey = new Regex(@"^[A-Za-z0-9]{20}$");
- public static readonly Regex ReSecret = new Regex(@"^([A-Za-z0-9]{8})-([A-Za-z0-9]{12})-([A-Za-z0-9]{8})$");
+ public static readonly Regex ReApiKey = new(@"^[A-Za-z0-9]{20}$");
+ public static readonly Regex ReSecret = new(@"^([A-Za-z0-9]{8})-([A-Za-z0-9]{12})-([A-Za-z0-9]{8})$");
[Fact]
public void ConstructorExisting()
{
- Client client = new(
- id: 123,
- apiKey: "test-api-key",
- name: "App",
- isEnabled: false,
- granted: Permissions.SEE_PRODUCTS_AND_CATEGORIES,
- allowed: Permissions.MANAGE_USERS
- );
-
- Assert.Equal(123, client.Id);
- Assert.Equal("test-api-key", client.ApiKey);
- Assert.Equal("App", client.Name);
- Assert.False(client.IsEnabled);
- Assert.Equal(Permissions.SEE_PRODUCTS_AND_CATEGORIES, client.Granted);
- Assert.Equal(Permissions.MANAGE_USERS, client.Allowed);
- Assert.False(client.AllowDirectUserLogin);
- }
+ Client client = new(
+ id: 123,
+ apiKey: "test-api-key",
+ name: "App",
+ isEnabled: false,
+ granted: Permission.SeeProductsAndCategories,
+ allowed: Permission.ManageUsers
+ );
+
+ Assert.Equal(123, client.Id);
+ Assert.Equal("test-api-key", client.ApiKey);
+ Assert.Equal("App", client.Name);
+ Assert.False(client.IsEnabled);
+ Assert.Equal(Permission.SeeProductsAndCategories, client.Granted);
+ Assert.Equal(Permission.ManageUsers, client.Allowed);
+ Assert.False(client.AllowDirectUserLogin);
+ }
[Fact]
public void ConstructorNew()
{
- Client client = new(
- name: "App",
- allowed: Permissions.MANAGE_USERS
- );
-
- Assert.Matches(ReApiKey, client.ApiKey);
- Assert.Equal("App", client.Name);
- Assert.True(client.IsEnabled);
- Assert.Equal(Permissions.NONE, client.Granted);
- Assert.Equal(Permissions.MANAGE_USERS, client.Allowed);
- Assert.True(client.AllowDirectUserLogin);
- }
+ Client client = new(
+ name: "App",
+ allowed: Permission.ManageUsers
+ );
+
+ Assert.Matches(ReApiKey, client.ApiKey);
+ Assert.Equal("App", client.Name);
+ Assert.True(client.IsEnabled);
+ Assert.Equal(Permission.None, client.Granted);
+ Assert.Equal(Permission.ManageUsers, client.Allowed);
+ Assert.True(client.AllowDirectUserLogin);
+ }
[Fact]
public void AllowDirectUserLogin()
{
- Client client1 = new(
- id: 123,
- apiKey: "test-api-key",
- name: "App",
- isEnabled: true,
- granted: Permissions.NONE,
- allowed: Permissions.NONE
- );
- Client client2 = new(
- id: 123,
- apiKey: "test-api-key",
- name: "App",
- isEnabled: false,
- granted: Permissions.NONE,
- allowed: Permissions.NONE
- );
- Client client3 = new(
- id: 123,
- apiKey: "test-api-key",
- name: "App",
- isEnabled: true,
- granted: Permissions.NONE,
- allowed: Permissions.NONE
- );
- Client client4 = new(
- id: 123,
- apiKey: "test-api-key",
- name: "App",
- isEnabled: false,
- granted: Permissions.NONE,
- allowed: Permissions.NONE
- );
-
- Assert.True(client1.AllowDirectUserLogin);
- Assert.False(client2.AllowDirectUserLogin);
- Assert.True(client3.AllowDirectUserLogin);
- Assert.False(client4.AllowDirectUserLogin);
- }
+ Client client1 = new(
+ id: 123,
+ apiKey: "test-api-key",
+ name: "App",
+ isEnabled: true,
+ granted: Permission.None,
+ allowed: Permission.None
+ );
+ Client client2 = new(
+ id: 123,
+ apiKey: "test-api-key",
+ name: "App",
+ isEnabled: false,
+ granted: Permission.None,
+ allowed: Permission.None
+ );
+ Client client3 = new(
+ id: 123,
+ apiKey: "test-api-key",
+ name: "App",
+ isEnabled: true,
+ granted: Permission.None,
+ allowed: Permission.None
+ );
+ Client client4 = new(
+ id: 123,
+ apiKey: "test-api-key",
+ name: "App",
+ isEnabled: false,
+ granted: Permission.None,
+ allowed: Permission.None
+ );
+
+ Assert.True(client1.AllowDirectUserLogin);
+ Assert.False(client2.AllowDirectUserLogin);
+ Assert.True(client3.AllowDirectUserLogin);
+ Assert.False(client4.AllowDirectUserLogin);
+ }
[Fact]
public void Filter()
{
- // test simple
+ // test simple
- Permissions before1 = Permissions.SEE_PRODUCTS_AND_CATEGORIES
- | Permissions.SEE_ALL_USERS_AND_ROLES;
+ const Permission BEFORE1 = Permission.SeeProductsAndCategories
+ | Permission.SeeAllUsersAndRoles;
- Client client1 = new Client(
- name: "App 1",
- allowed: Permissions.MANAGE_PRODUCTS,
- granted: Permissions.MANAGE_PRODUCTS
- );
- Permissions after1 = client1.Filter(before1);
+ var client1 = new Client(
+ name: "App 1",
+ allowed: Permission.ManageProducts,
+ granted: Permission.ManageProducts
+ );
+ Permission after1 = client1.Filter(BEFORE1);
- Assert.Equal(Permissions.MANAGE_PRODUCTS, after1);
+ Assert.Equal(Permission.ManageProducts, after1);
- // test de priorité
+ // test de priorité
- Permissions before2 = Permissions.READ_LOGS;
+ const Permission BEFORE2 = Permission.ReadLogs;
- Client client2 = new Client(
- name: "App 2",
- granted: Permissions.SEE_PRODUCTS_AND_CATEGORIES,
- allowed: Permissions.READ_LOGS // écrase la permission donnée précedemment
- );
- Permissions after2 = client2.Filter(before2);
+ var client2 = new Client(
+ name: "App 2",
+ granted: Permission.SeeProductsAndCategories,
+ allowed: Permission.ReadLogs // écrase la permission donnée précédemment
+ );
+ Permission after2 = client2.Filter(BEFORE2);
- Assert.Equal(before2, after2);
- }
+ Assert.Equal(BEFORE2, after2);
+ }
}
\ No newline at end of file
diff --git a/CoreTests/Security/OneTimeSecretTest.cs b/CoreTests/Security/OneTimeSecretTest.cs
index 898afe9..d442867 100644
--- a/CoreTests/Security/OneTimeSecretTest.cs
+++ b/CoreTests/Security/OneTimeSecretTest.cs
@@ -1,5 +1,4 @@
using GalliumPlus.Core.Exceptions;
-using GalliumPlus.Core.Random;
namespace GalliumPlus.Core.Security;
diff --git a/CoreTests/Users/PermissionsTest.cs b/CoreTests/Users/PermissionsTest.cs
index aaba700..602f512 100644
--- a/CoreTests/Users/PermissionsTest.cs
+++ b/CoreTests/Users/PermissionsTest.cs
@@ -1,19 +1,15 @@
-namespace GalliumPlus.Core.Users;
+using GalliumPlus.Core.Security;
-using P = Permissions;
+namespace GalliumPlus.Core.Users;
+
+using P = Permission;
public class PermissionsTest
{
+ private static readonly Permissions perms = Permissions.Current;
+
[Fact]
- public void Includes()
- {
- Assert.True(P.MANAGE_PRODUCTS.Includes(P.MANAGE_PRODUCTS));
- Assert.True(P.MANAGE_PRODUCTS.Includes(P.SEE_PRODUCTS_AND_CATEGORIES));
- Assert.False(P.MANAGE_PRODUCTS.Includes(P.MANAGE_CATEGORIES));
- }
-
- [Fact]
- public void Regression()
+ public void NonRegression()
{
/*
* ATTENTION !!!
@@ -21,45 +17,18 @@ public void Regression()
* Il faut éviter cela le plus possible pour ne pas attribuer
* des permissions par erreur à certains utilisateurs.
*/
- Assert.Equal((P)1, P.SEE_PRODUCTS_AND_CATEGORIES);
- Assert.Equal((P)2 | P.SEE_PRODUCTS_AND_CATEGORIES, P.MANAGE_PRODUCTS);
- Assert.Equal((P)4 | P.SEE_PRODUCTS_AND_CATEGORIES, P.MANAGE_CATEGORIES);
- Assert.Equal((P)8, P.SEE_ALL_USERS_AND_ROLES);
- Assert.Equal((P)16 | P.SEE_ALL_USERS_AND_ROLES, P.MANAGE_DEPOSITS);
- Assert.Equal((P)32 | P.MANAGE_DEPOSITS, P.MANAGE_USERS);
- Assert.Equal((P)64 | P.SEE_ALL_USERS_AND_ROLES, P.MANAGE_ROLES);
- Assert.Equal((P)128, P.READ_LOGS);
- Assert.Equal((P)256 | P.MANAGE_USERS, P.RESET_MEMBERSHIPS);
- Assert.Equal(P.MANAGE_PRODUCTS | P.MANAGE_DEPOSITS, P.SELL);
- }
-
- [Fact]
- public void None()
- {
- Assert.False(P.NONE.Includes(P.SEE_PRODUCTS_AND_CATEGORIES));
- Assert.False(P.NONE.Includes(P.MANAGE_PRODUCTS));
- Assert.False(P.NONE.Includes(P.MANAGE_CATEGORIES));
- Assert.False(P.NONE.Includes(P.SEE_ALL_USERS_AND_ROLES));
- Assert.False(P.NONE.Includes(P.MANAGE_DEPOSITS));
- Assert.False(P.NONE.Includes(P.MANAGE_USERS));
- Assert.False(P.NONE.Includes(P.MANAGE_ROLES));
- Assert.False(P.NONE.Includes(P.READ_LOGS));
- Assert.False(P.NONE.Includes(P.RESET_MEMBERSHIPS));
- Assert.False(P.NONE.Includes(P.SELL));
- }
-
- [Fact]
- public void All()
- {
- Assert.True(P.ALL.Includes(P.SEE_PRODUCTS_AND_CATEGORIES));
- Assert.True(P.ALL.Includes(P.MANAGE_PRODUCTS));
- Assert.True(P.ALL.Includes(P.MANAGE_CATEGORIES));
- Assert.True(P.ALL.Includes(P.SEE_ALL_USERS_AND_ROLES));
- Assert.True(P.ALL.Includes(P.MANAGE_DEPOSITS));
- Assert.True(P.ALL.Includes(P.MANAGE_USERS));
- Assert.True(P.ALL.Includes(P.MANAGE_ROLES));
- Assert.True(P.ALL.Includes(P.READ_LOGS));
- Assert.True(P.ALL.Includes(P.RESET_MEMBERSHIPS));
- Assert.True(P.ALL.Includes(P.SELL));
+ Assert.Equal((P)0x1, perms.Maximum(P.SeeProductsAndCategories));
+ Assert.Equal((P)0x3, perms.Maximum(P.ManageProducts));
+ Assert.Equal((P)0x5, perms.Maximum(P.ManageCategories));
+ Assert.Equal((P)0x8, perms.Maximum(P.SeeAllUsersAndRoles));
+ Assert.Equal((P)0x18, perms.Maximum(P.ManageDeposits));
+ Assert.Equal((P)0x38, perms.Maximum(P.ManageUsers));
+ Assert.Equal((P)0x48, perms.Maximum(P.ManageRoles));
+ Assert.Equal((P)0x80, perms.Maximum(P.ReadLogs));
+ Assert.Equal((P)0x138, perms.Maximum(P.ResetMemberships));
+ Assert.Equal((P)0x200, perms.Maximum(P.ManageClients));
+ Assert.Equal((P)0x400, perms.Maximum(P.UseDeveloperTools));
+ Assert.Equal((P)0x40000000, perms.Maximum(P.ForceDepositModification));
+ Assert.Equal((P)0x1B, perms.Maximum(P.Sell));
}
}
\ No newline at end of file
diff --git a/CoreTests/Users/RoleTest.cs b/CoreTests/Users/RoleTest.cs
index e5bf377..7e1048f 100644
--- a/CoreTests/Users/RoleTest.cs
+++ b/CoreTests/Users/RoleTest.cs
@@ -1,14 +1,16 @@
-namespace GalliumPlus.Core.Users;
+using GalliumPlus.Core.Security;
+
+namespace GalliumPlus.Core.Users;
public class RoleTest
{
[Fact]
public void Constructor()
{
- Role role = new Role(123, "Rôle", Permissions.MANAGE_PRODUCTS);
+ Role role = new Role(123, "Rôle", Permission.ManageProducts);
Assert.Equal(123, role.Id);
Assert.Equal("Rôle", role.Name);
- Assert.Equal(Permissions.MANAGE_PRODUCTS, role.Permissions);
+ Assert.Equal(Permission.ManageProducts, role.Permissions);
}
}
\ No newline at end of file
diff --git a/CoreTests/Users/SessionTest.cs b/CoreTests/Users/SessionTest.cs
index 8bc0c16..a0e6bf6 100644
--- a/CoreTests/Users/SessionTest.cs
+++ b/CoreTests/Users/SessionTest.cs
@@ -1,4 +1,5 @@
using GalliumPlus.Core.Applications;
+using GalliumPlus.Core.Security;
namespace GalliumPlus.Core.Users;
@@ -14,7 +15,7 @@ public class SessionTest
1,
"mmansouri",
new UserIdentity("Mehdi", "Mansouri", "mehdi.mansouri@iut-dijon.u-bourgogne.fr", "PROF"),
- new Role(0, "Adhérent", Permissions.NONE),
+ new Role(0, "Adhérent", Permission.None),
21.30m,
false
);
@@ -39,26 +40,26 @@ public void Constructor()
[Fact]
public void PermissionsProperty()
{
- this.user.Role.Permissions = Permissions.SEE_PRODUCTS_AND_CATEGORIES
- | Permissions.SEE_ALL_USERS_AND_ROLES;
+ this.user.Role.Permissions = Permission.SeeProductsAndCategories
+ | Permission.SeeAllUsersAndRoles;
var client1 = new Client(
name: "App 1",
- granted: Permissions.MANAGE_PRODUCTS,
- allowed: Permissions.MANAGE_PRODUCTS
+ granted: Permission.ManageProducts,
+ allowed: Permission.ManageProducts
);
Session session1 = Session.LogIn(this.sessionOptions, client1, this.user);
- Assert.Equal(Permissions.MANAGE_PRODUCTS, session1.Permissions);
+ Assert.Equal(Permission.ManageProducts, session1.Permissions);
- this.user.Role.Permissions = Permissions.READ_LOGS;
+ this.user.Role.Permissions = Permission.ReadLogs;
var client2 = new Client(
name: "App 2",
- granted: Permissions.SEE_PRODUCTS_AND_CATEGORIES,
- allowed: Permissions.NONE // écrase la permission donnée précedemment
+ granted: Permission.SeeProductsAndCategories,
+ allowed: Permission.None // écrase la permission donnée précedemment
);
Session session2 = Session.LogIn(this.sessionOptions, client2, this.user);
- Assert.Equal(Permissions.NONE, session2.Permissions);
+ Assert.Equal(Permission.None, session2.Permissions);
}
[Fact]
diff --git a/CoreTests/Users/UserTest.cs b/CoreTests/Users/UserTest.cs
index 1ac385a..12d84fa 100644
--- a/CoreTests/Users/UserTest.cs
+++ b/CoreTests/Users/UserTest.cs
@@ -5,7 +5,7 @@ namespace GalliumPlus.Core.Users;
public class UserTest
{
- private readonly Role profRole = new Role(0, "Prof", Permissions.NONE);
+ private readonly Role profRole = new Role(0, "Prof", Permission.None);
private readonly PasswordInformation password = PasswordInformation.FromPassword("motdepasse123");
private string[] validIdList = new string[] { "am200927", "amdzznzs", "AM200927", "AMDzzNZs", "am200NDs" };
diff --git a/CoreTests/Usings.cs b/CoreTests/Usings.cs
index 76023e8..4d11379 100644
--- a/CoreTests/Usings.cs
+++ b/CoreTests/Usings.cs
@@ -1,3 +1,2 @@
-global using GalliumPlus.WebApi.Core;
global using System.Text.RegularExpressions;
global using Xunit;
diff --git a/External/Data/MariaDb/Implementations/ClientDao.cs b/External/Data/MariaDb/Implementations/ClientDao.cs
index 2ce3da1..e2a7890 100644
--- a/External/Data/MariaDb/Implementations/ClientDao.cs
+++ b/External/Data/MariaDb/Implementations/ClientDao.cs
@@ -3,7 +3,6 @@
using GalliumPlus.Core.Exceptions;
using KiwiQuery;
using KiwiQuery.Mapped;
-using KiwiQuery.Mapped.Exceptions;
using MySqlConnector;
namespace GalliumPlus.Data.MariaDb.Implementations
diff --git a/External/Data/MariaDb/Implementations/HashAndSaltMapper.cs b/External/Data/MariaDb/Implementations/HashAndSaltMapper.cs
index d06aa12..a28ee5b 100644
--- a/External/Data/MariaDb/Implementations/HashAndSaltMapper.cs
+++ b/External/Data/MariaDb/Implementations/HashAndSaltMapper.cs
@@ -3,82 +3,52 @@
using KiwiQuery.Mapped.Extension;
using KiwiQuery.Mapped.Mappers.Fields;
-namespace GalliumPlus.Data.MariaDb;
+namespace GalliumPlus.Data.MariaDb.Implementations;
-[SharedMapper]
-public class HashAndSaltMapper : IFieldMapper
+public abstract class HashAndSaltMapper : IFieldMapper
{
- public bool CanHandle(Type fieldType) => fieldType == typeof(PasswordInformation) || fieldType == typeof(OneTimeSecret);
+ public bool CanHandle(Type fieldType) => fieldType == typeof(T);
+
+ public IFieldMapper SpecializeFor(Type fieldType, IColumnInfo info, IFieldMapperCollection collection) => this;
- public IFieldMapper SpecializeFor(Type fieldType, IColumnInfo info, IFieldMapperCollection collection)
+ public object? ReadValue(IDataRecord record, int offset)
{
- if (fieldType == typeof(PasswordInformation))
- {
- return new SpecializedForPasswordInformation();
- }
- else if (fieldType == typeof(OneTimeSecret))
- {
- return new SpecializedForOneTimeSecret();
- }
- else
- {
- throw new InvalidOperationException($"Les champs de type {fieldType.FullName} ne sont pas pris en charge.");
- }
+ byte[] hash = new byte[32];
+ record.GetBytes(offset, 0, hash, 0, 32);
+ string salt = record.GetString(offset + 1);
+ return this.FromHashAndSalt(hash, salt);
}
- public object? ReadValue(IDataRecord record, int offset) =>
- throw new InvalidOperationException("Ce mapper ne peut pas être utilisé sans spécialisation.");
-
public IEnumerable