Logika.License 1.0.0

Logika.License

Libreria .NET per la creazione, verifica e migrazione delle licenze software Logika.

Compatibile con .NET 6, .NET 8 e .NET 10.

Installazione

dotnet add package Logika.License --source <url-server-nuget-privato>

Concetti chiave

La libreria gestisce 3 chiavi (i nomi sono storici, non modificabili):

Chiave Significato
LicenseKey Identifica la licenza, generata dall'hardware della macchina (CreateLicenseKey)
CheckKey Chiave per la verifica offline della licenza, in assenza di connettività
ActivationKey Contiene lo stato della licenza restituito dal server

Il punto d'ingresso della libreria è l'interfaccia ILicenseService. L'implementazione di default, LicenseService, comunica con il server delle licenze via HTTP.

Utilizzo manuale (senza DI)

using Logika.License.Abstractions;
using Logika.License.Services;

var httpClient = new HttpClient
{
    BaseAddress = new Uri("https://license.miaazienda.it")
};

ILicenseService service = new LicenseService(httpClient);

// Genera una nuova LicenseKey legata all'hardware della macchina corrente
string licenseKey = service.CreateLicenseKey();

HttpClient deve avere BaseAddress impostato sull'URL del server delle licenze. La sua creazione/gestione (riuso, timeout, ecc.) resta a carico dell'utilizzatore, come da best practice .NET.

API key: in uso manuale impostala tu sull'HttpClient, ad esempio httpClient.DefaultRequestHeaders.Add("ApiKey", "la-tua-api-key"); con la DI basta impostare LogikaLicenseOptions.ApiKey.

Utilizzo con Dependency Injection

Registra la libreria nel container all'avvio dell'applicazione (Program.cs / Startup.cs):

using Logika.License.DependencyInjection;

builder.Services.AddLogikaLicense(options =>
{
    options.BaseAddress = new Uri("https://license.miaazienda.it");

    // Opzionale: API key inviata ad ogni richiesta nell'header "ApiKey"
    options.ApiKey = "la-tua-api-key";

    // Opzionale: abilita il salvataggio automatico delle 3 chiavi su file JSON
    options.KeyStoreFilePath = @"C:\ProgramData\MiaApp\license.json";

    // Opzionale: configurazione aggiuntiva dell'HttpClient (header, timeout, ecc.)
    options.ConfigureHttpClient = client =>
    {
        client.Timeout = TimeSpan.FromSeconds(10);
    };
});

Poi inietta ILicenseService (e, se configurato, ILicenseKeyStore) dove serve:

public class LicenseController
{
    private readonly ILicenseService _licenseService;
    private readonly ILicenseKeyStore _keyStore;

    public LicenseController(ILicenseService licenseService, ILicenseKeyStore keyStore)
    {
        _licenseService = licenseService;
        _keyStore = keyStore;
    }

    // ...
}

Utilizzo ad alto livello (ILicenseManager)

Se l'applicazione non vuole gestire manualmente il flusso della licenza (caricare/salvare le chiavi, creare la LicenseKey, distinguere online/offline, ecc.), può affidarsi a ILicenseManager, che orchestra automaticamente ILicenseService e ILicenseKeyStore. Basta passare i dati anagrafici e si riceve un esito leggibile.

ILicenseManager è registrato automaticamente in DI quando imposti LogikaLicenseOptions.KeyStoreFilePath (serve uno store su cui persistere le chiavi).

Esempio: verifica automatica della licenza

using Logika.License.Abstractions;
using Logika.License.Models;

// Inietta ILicenseManager nel tuo controller/servizio
public class StartupCheck
{
    private readonly ILicenseManager _licenseManager;

    public StartupCheck(ILicenseManager licenseManager)
        => _licenseManager = licenseManager;

    public async Task VerifyAsync(CancellationToken ct = default)
    {
        // Una verifica per ogni prodotto software installato
        LicenseCheckResult result = await _licenseManager.EnsureLicenseAsync(
            new LicenseCheckContext
            {
                Customer = "ACME S.p.A.",
                Company = "server-01",
                User = "mario.rossi",
                SoftwareName = "MioGestionale",
                SoftwareVersion = "4.2.0"
            }, ct);

        if (result.IsEnabled)
        {
            // licenza valida o in demo: si può usare l'applicazione
            App.UI.Show($"Licenza OK - utenze attive: {result.CalCount} - {result.Reason}");
        }
        else
        {
            // result.Status indica il motivo (ExpiredLicense, ExpiredDemo, InvalidLicense...)
            App.UI.ShowError(result.Reason);
        }

        // La LicenseKey usata (creata automaticamente al primo avvio)
        string key = result.LicenseKey;
    }
}

Nella prima esecuzione EnsureLicenseAsync genera automaticamente la LicenseKey legata all'hardware e la salva; nelle successive la riutilizza. CheckKey e ActivationKey aggiornate vengono salvate ad ogni verifica andata a buon fine. Ogni prodotto software (identificato da SoftwareName) ha la sua licenza: basta chiamare EnsureLicenseAsync una volta per prodotto.

Conoscere lo stato delle licenze salvate

GetLicensesAsync restituisce una fotografia (stato + CAL) di tutte le licenze persistite, senza contattare il server:

IReadOnlyList<LicenseState> states = await licenseManager.GetLicensesAsync(ct);

foreach (LicenseState state in states)
{
    Console.WriteLine($"{state.SoftwareName}: {state.Status} - utenze attive: {state.CalCount}");
}

// Stato di un singolo prodotto (null se non ancora salvato)
LicenseState? gestione = await licenseManager.GetLicenseAsync("MioGestionale", ct);

Ottenere la sola LicenseKey di un prodotto

string licenseKey = await licenseManager.GetOrCreateLicenseKeyAsync("MioGestionale");

Esito (LicenseCheckResult)

Proprietà Descrizione
SoftwareName Prodotto software a cui appartiene la licenza verificata
Status Stato puntuale (ValidLicense, DemoLicense, ExpiredDemo, ExpiredLicense, InvalidLicense)
IsEnabled true se la licenza è valida o in demo attiva
LicenseKey La LicenseKey corrente (creata automaticamente al primo utilizzo)
CalCount Numero di CAL (utenze attive) associate alla licenza
Reason Motivazione dello stato, in italiano, pronta per l'utente finale
Error Eccezione tecnica intercettata (es. server irraggiungibile), null se nessun errore

Salvataggio delle chiavi (ILicenseKeyStore)

La libreria fornisce un'implementazione di default, JsonLicenseKeyStore, che salva in un file JSON le licenze di più prodotti software, ognuna identificata dal SoftwareName:

using Logika.License.Persistence;

var keyStore = new JsonLicenseKeyStore(@"C:\ProgramData\MiaApp\license.json");

await keyStore.SaveAsync(new StoredLicenseKeys
{
    SoftwareName = "MioGestionale",
    LicenseKey = licenseKey,
    CheckKey = result.CheckKey,
    ActivationKey = result.ActivationKey
});

// Licenza di un singolo prodotto (null se non ancora salvata)
StoredLicenseKeys? saved = await keyStore.LoadAsync("MioGestionale");

// Tutte le licenze salvate
IReadOnlyList<StoredLicenseKeys> all = await keyStore.LoadAllAsync();

Se serve un meccanismo di salvataggio diverso (registro di sistema, database, key vault, ecc.), è sufficiente implementare ILicenseKeyStore:

public interface ILicenseKeyStore
{
    Task SaveAsync(StoredLicenseKeys storedKeys, CancellationToken cancellationToken = default);
    Task<StoredLicenseKeys?> LoadAsync(string softwareName, CancellationToken cancellationToken = default);
    Task<IReadOnlyList<StoredLicenseKeys>> LoadAllAsync(CancellationToken cancellationToken = default);
}

API di ILicenseService

Metodo Descrizione
CreateLicenseKey() Genera una nuova LicenseKey legata all'hardware della macchina corrente
CheckLicense(LicenseValidationRequest) Verifica una licenza (online, con fallback offline se il server non è raggiungibile)
MigrateLicense(LicenseMigrationRequest) Migra una licenza da una vecchia LicenseKey a una nuova
GetCALsFromActivationKey(activationKey) Estrae il numero di CAL (Client Access License) da una ActivationKey
LastException Ultima eccezione intercettata internamente durante una CheckLicense fallita (utile per diagnostica)

Esempio: verifica di una licenza

var request = new LicenseValidationRequest
{
    Customer = "ACME S.p.A.",
    Company = "server-01",
    User = "mario.rossi",
    SoftwareName = "MioGestionale",
    SoftwareVersion = "4.2.0",
    LicenseKey = savedKeys.LicenseKey,
    CheckKey = savedKeys.CheckKey,
    LastActivationKey = savedKeys.ActivationKey
};

LicenseValidationResult result = await service.CheckLicense(request);

if (result.IsEnabled)
{
    // licenza valida o in demo -> aggiorno le chiavi salvate
    await keyStore.SaveAsync(new StoredLicenseKeys
    {
        LicenseKey = request.LicenseKey,
        CheckKey = result.CheckKey,
        ActivationKey = result.ActivationKey
    });
}
else
{
    // result.Status indica il motivo (InvalidLicense, ExpiredLicense, ExpiredDemo...)
}

Esempio: migrazione di una licenza

var migrationResult = await service.MigrateLicense(new LicenseMigrationRequest
{
    OldLicenseKey = vecchiaLicenseKey,
    NewLicenseKey = nuovaLicenseKey,
    Customer = "ACME S.p.A.",
    Company = "server-02",
    User = "mario.rossi",
    SoftwareName = "MioGestionale",
    SoftwareVersion = "4.2.0"
});

if (migrationResult.Success)
{
    // migrazione completata
}

Modelli pubblici

  • LicenseValidationRequest — dati necessari per CheckLicense
  • LicenseValidationResult — esito di CheckLicense (Status, IsEnabled, CheckKey, ActivationKey)
  • StoredLicenseKeys — le 3 chiavi da persistere per un prodotto (SoftwareName, LicenseKey, CheckKey, ActivationKey)
  • LicenseState — fotografia di una licenza persistita (stato, IsEnabled, CalCount, chiavi)
  • LicenseCheckContext / LicenseCheckResult — dati/esito dell'uso ad alto livello con ILicenseManager
  • LicenseMigrationRequest / LicenseMigrationResult — richiesta/esito di MigrateLicense
  • LicenseStatus (enum) — ValidLicense, DemoLicense, ExpiredDemo, ExpiredLicense, InvalidLicense

Vocabolario

Per evitare ambiguità, ogni tipo ha un ruolo preciso:

Tipo Ruolo
ILicenseService / LicenseService entry point della libreria: genera chiavi, verifica, migra, estrae CAL
ILicenseManager / LicenseManager livello ad alto livello che orchetra automaticamente service + store (più licenze, una per prodotto)
ILicenseKeyStore / JsonLicenseKeyStore persistenza delle licenze dei prodotti (file JSON di default)
LicenseServerClient (interno) client HTTP verso il server (api/Licenses/*)

Test

Il progetto Logika.License.UnitTests (xUnit) copre la verifica online/offline, la decodifica delle chiavi, il multi-prodotto di ILicenseManager e la persistenza, senza rete né hardware reali.

Per eseguirli da Visual Studio: Test > Test Explorer > Run All (oppure dotnet test da terminale).

Test end-to-end contro il server reale

I test E2E (progetto Logika.License.IntegrationTests) colpiscono la tua API delle licenze (https://localhost:7092) con l'hardware reale della macchina e vengono saltati se non configurati. Per attivarli:

  1. Apri Logika.License.IntegrationTests/appsettings.e2e.json e compila:
    {
      "baseUrl": "https://localhost:7092",
      "apiKey": "0123456789"
    }
    
  2. Avvia il server LogikaApi (F5 sul progetto LogikaApi) e rilancia i test da Test Explorer.

In alternativa (per CI o terminale) si possono usare le variabili d'ambiente LICENSE_API_BASE_URL e LICENSE_API_KEY, che hanno precedenza sul file.

La suite E2E è autocontenuta: un fixture di collection chiama una sola volta POST /api/Licenses/ResetLicenses (endpoint dev-only di LogikaApi) prima dell'esecuzione, così le licenze di test (che usano il prefisso integration-test-server-) vengono azzerate a ogni run e il database riparte pulito.

I test E2E coprono:

  • CheckLicenseIntegrationTests — chiave nuova → demo abilitata con ActivationKey ben formata (80 caratteri, controllo 00DE, data = LicenseKey + 100 giorni, 1 CAL); doppia verifica della stessa chiave; API key errata → 401 → fallback offline; server irraggiungibile → verifica offline con CheckKey valida (e senza CheckKey → licenza non valida).
  • MigrateLicenseIntegrationTests — migrazione completa (vecchia chiave disattivata, nuova attiva); migrazione già eseguita → rifiutata; vecchia chiave sconosciuta → Success=false.
  • LicenseManagerIntegrationTestsEnsureLicenseAsync end-to-end (crea e persiste le 3 chiavi su file JSON, riusa la LicenseKey, restituisce stato/CAL) e GetLicensesAsync multi-prodotto.
  • LicenseAdminEndpointsIntegrationTests — tramite FindLicenses/GetLicense/GetLicenses verifica che i campi inviati dalla libreria arrivino sul database senza alterazioni, che la licenza creata sia visibile e che il codice cliente sia scritto solo alla prima verifica (first-write-wins).

I test E2E creano licenze demo reali sul server di sviluppo (una per run). Per non interferire con la logica anti-licenze-multiple del server, ogni run usa valori univoci (ServerName/User/SoftwareName con suffisso casuale).

Note

  • Tutto ciò che si trova nel namespace Logika.License.Api è dettaglio implementativo interno e non fa parte della superficie pubblica della libreria.
  • I nomi LicenseKey, CheckKey e ActivationKey sono storici e volutamente non descrittivi del loro reale significato tecnico; fanno riferimento alla tabella nella sezione "Concetti chiave" sopra.

No packages depend on Logika.License.

Version Downloads Last updated
1.0.0 2 08/07/2026