Laden...

Wie kann ich eine ASP.NET Core Anwendunge auf Laufzeitbegrenzung überprüfen?

Erstellt von JimStark vor 3 Jahren Letzter Beitrag vor 3 Jahren 1.653 Views
JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren
Wie kann ich eine ASP.NET Core Anwendunge auf Laufzeitbegrenzung überprüfen?

Hi Leute,

ich möchte eine ASP.NET Core Anwendung mit einem License Framework ausstatten (https://github.com/dnauck/Portable.Licensing/). Schutz vor Reverse Engineering oder strenge Prüfungen spielen keine Rolle.

Jetzt würde ich mir gerne eine Klasse schreiben, die mittels Portable.Licensing checkt ob es noch in der lizensierten Laufzeit ausgeführt, max. User, etc. Diese Klasse würde ich dann wieder als Service registrieren um in den Controllern darauf zugreifen zu können.
Jetzt meine Frage bzgl. Laufzeit: das Programm soll z.B. bis 31.12.2021 lizensiert sein.
An welcher Stelle bietet es sich an das zu prüfen? Vielleicht hat jemand sowas ähnliches in der Praxis gemacht und kann sagen wie er es bewerkstelligt hat.
Auf anderen Seiten habe ich gelesen, es in OnActionExecuting zu prüfen und bei einer ausgelaufenen Lizenz eine Exception zu werfen. Ist das sinnvoll bei jedem Aufruf zu prüfen bzgl. Performance,...? Oder sollte es bei wichtigen Funktionen (z.B. /Auftrag/Create) geprüft werden und da dann ein Fehler ausgegeben werden?

Viele Grüße

16.806 Beiträge seit 2008
vor 3 Jahren

Lizenprüfungen gehören zur Authorisierung: Policies.

Ist das sinnvoll bei jedem Aufruf zu prüfen bzgl. Performance,...? Oder sollte es bei wichtigen Funktionen (z.B. /Auftrag/Create) geprüft werden und da dann ein Fehler ausgegeben werden?

Das sind Fragen, die kannst Du Dir nur selbst beantworten.

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Vielen Dank an dich!

Noch eine Frage,
ich habe meinen License Service:


services.AddScoped<ILicenseManager>(_ => new LicenseManager("license.dat"));

wie kann ich der Policy ein Argument aus dem LicenseManager mitgeben?


            services.AddAuthorization(options =>
            {
                options.AddPolicy("LifetimeIsValid", policy => 
                policy.Requirements.Add(
                    new ExpirationRequirement(new DateTime(2022, 1, 1))
                    )
                ); 
            });

Anstelle dem** new DateTime(2022, 1, 1)** würde ich gerne LicenseManager.GetExpirationDate() haben. Wie greife ich innerhalb des ConfigureServices auf einem Service zu? Oder sollte ich das nochmal anders designen?

16.806 Beiträge seit 2008
vor 3 Jahren

In dem Link, den ich Dir gegeben hab, sind Beispiele, wie man ein Requirement und ein RequirementHandler umsetzt.

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Es funktioniert ja auch hardcoded, ich will nur das Datum aus dem Service auslesen.
Mein Requirement sieht so aus:


    public class ExpirationRequirement : IAuthorizationRequirement
    {

        public DateTime ExpirationDate { get; }

        public ExpirationRequirement(DateTime expirationDate)
        {
            this.ExpirationDate = expirationDate;
        }
    }

Ich versteh jetzt nur nicht wie ich auf den License Manager zugreifen kann:


policy.Requirements.Add(
                    new ExpirationRequirement(new DateTime(2022, 1, 1))
                    )
                );

So eine automatische Übergabe von z.B. dem LicenseManager wie bei Services geht ja nicht.

1.029 Beiträge seit 2010
vor 3 Jahren

Hi,

innerhalb des AddAuthorization-Aufrufs kommt man nicht an einen IServiceProvider und somit auch nicht an die Instanz des Lizenzservices bzw. dessen Optionen.

(Theoretisch kann man vorab manuell einen erzeugen - das ist aber unsauber und sollte vermieden werden)

Grundlegend sehe ich hier zwei Möglichkeiten:
a) Du baust eine "Option" und liest diese beim o.g. Aufruf auf und verwendest diese Option sowohl im Lizenzservice als auch im Requirement
b) Du hast sicher ohnehin schon eine eigene Implementierung eines AuthorizationHandlers - der bekommt sicher eh schon per DependecyInjection das notwendige Datum, womit das Requirement es hier schlicht nicht nötig hat

LG

16.806 Beiträge seit 2008
vor 3 Jahren

Man muss eigentlich nur den Link lesen, verstehen und kopieren. Mehr ist das nicht.

public class LicenseOptions
{
   public DateTimeOffset ExpirationDateUtc {get;set;}
}

public class LicenseActiveRequirement: IAuthorizationRequirement { }

public class LicenseActiveRequirementHandler : AuthorizationHandler<LicenseActiveRequirement>
{
    private LicenseOptions _licenseOptions;

    public LicenseActiveRequirementHandler (IOption<LicenseOptions> licenseOptionsAccessor)
    {
        _licenseOptions = _licenseOptionsAccessor.Value;
    }

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, LicenseActiveRequirement requirement)
    {
         if( DateTimeOffset.UtcNow < _licenseOptions.ExpirationDateUtc )
         {
              context.Succeed(requirement);
         }
         else
         {
              context.Fail();
          }
    }

Aber es gibt noch andere Möglichkeiten, wie zB nen Service Filter; kommt einfach drauf an, was man machen will.

Hint: DateTimeOffset verwenden. DateTime ist Mottenkiste.

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

b) Du hast sicher ohnehin schon eine eigene Implementierung eines AuthorizationHandlers - der bekommt sicher eh schon per DependecyInjection das notwendige Datum, womit das Requirement es hier schlicht nicht nötig hat

LG

Danke für die Antwort, ja genau so habe ich es.
Dazu noch eine Frage, mein License Service habe ich ja als Scoped, der Handler ist aber Singleton, was so ja bei beiden Sinn macht.
Bekomme da folgende Exception:> Fehlermeldung:

InvalidOperationException: Cannot consume scoped service 'XYZ.License.Validation.ILicenseManager' from singleton 'Microsoft.AspNetCore.Authorization.IAuthorizationHandler'.

Also der Aufruf sieht so aus:


            services.AddScoped<ILicenseManager>(_ => new LicenseManager("license.dat"));
...
            services.AddSingleton<IAuthorizationHandler, ExceptionHandler>();

Der Handler hat folgenden Konstruktor:


public ExceptionHandler(ILicenseManager licenseManager){...}

16.806 Beiträge seit 2008
vor 3 Jahren

Richtig. Ist ja auch logisch, dass man in einem Singleton-Context kein Scoped-Service konsumieren kann.

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Ja das ist ja mein Problem die ganze Zeit,
wie kann ich da jetzt auf meinen Scoped Service zugreifen?

16.806 Beiträge seit 2008
vor 3 Jahren

Dann mach doch einfach Dein Handler Scoped, oder wo ist das Problem?
Oder weißte einfach nicht was Scoped und Singleton ist?

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Dann mach doch einfach Dein Handler Scoped, oder wo ist das Problem?
Oder weißte einfach nicht was Scoped und Singleton ist?

Ok danke, jetzt funktioniert es.
Das Problem ist ehr, dass ich nicht verstehe warum man bei dem Handler jetzt auch Scoped anstelle von Singleton nehmen kann. Also worin sich die beiden utnerscheiden ist mir schon klar, aber was das praktisch heißt versteh ich nicht.

16.806 Beiträge seit 2008
vor 3 Jahren

Singleton: 1 Instanz für die gesamte Anwendung
Scoped: 1 Instanz pro Request

=> Service lifetimes

Du kannst niemals einen Service konsumieren, der eine kürzere Laufzeit hat als Dein Context.

Würdest Du aus einem Singleton einen Service konsumieren könnten, der Scoped ist, dann würde das bedeuten, dass der Service von Request A plötzlich auch für Request B gilt.

Les Dir die Docs durch (wie schon oft genannt).
Da steht alles im Detail.

Service Lifestimes musst Du aus dem FF beherrschen, sonst wirds mit ASP.NET Core schwer.
Darauf basiert die gesamte Pipeline.

JimStark Themenstarter:in
309 Beiträge seit 2020
vor 3 Jahren

Ja, ich weiß nur nicht wann ich was verwende:

don't inject via constructor injection because it forces the service to behave like a singleton.

Also hätte ich den LicenseManager direkt als singleton machen können da er ja eh so behandelt wird?

Oder du hast mal geschrieben:

Der Service würde als Scoped* als im DI Container registeriert werden.
Dann kannst ihn in der Action als Ctor-Parameter empfangen.

*je nach Kontext auch Transient.
Aber auf keinen Fall als Singleton.

Das ist doch dann jedesmal ein neuer Datenbank Zugriff, wann macht das Sinn bei sowas Transient zu benutzen? Und warum kann ich da nicht Singleton nehmen? Gefahr das die Verbindung im Laufe der Zeit verloren geht, etc.?
Sorry aber sowas kann ich mit Transferwissen noch nicht rauslesen 😄

Funktioniert jetzt aufjedenfall, Danke!

16.806 Beiträge seit 2008
vor 3 Jahren

Bitte, warum liest Du nicht einfach die Dokumentation?
Ich erzähl Dir hier genau das, was da schon steht. X(

Die Empfehlung ist: jede Registrierung soll zu kurz wie möglich sein.
Das heisst: Transient, wo immer möglich.
Das ist das A und O jeder Webanwendung: so wenig allokierte Ressourcen wie möglich, so kurze Lifetimes wie möglich.

Nur wenn die Anforderungen existieren nimmt man Scoped (zB Datenbanken) oder Singletons (Instance Shared Stuff).

ICH kann Dir nicht erzählen ob LicenseManager besser nen Singleton sein sollte.
Die Bezeichnung Manager ist hier klar, dass es eine Logik gibt. Schließt nicht auf Singleton.

Wenn es nur ein Datum ist, dann brauchst Du kein Manager, sondern Options.
Aber keiner hier ausser Dir kennt die Anforderungen um entscheiden zu können, welche Registrierung passt.