Tuesday, October 27, 2020

Materiały edukacyjne - funkcje w Javascript

Czwarty wykład z Wybranych elementów praktyki projektowania oprogramowania, o programowaniu funkcyjnym w Javascript, to jeden z moich ulubionych, łącznie z kolejnym o obiektowości prototypowej. Tu opowiadam o tym jak w Javascript zobaczyć język funkcyjny, z takimi elementami funkcyjnymi jak funkcje wyższych rzędów, rekursja, domknięcia, kontynuacje. Zrozumienie tych elementów języka znacznie ułatwia swobodne poruszanie się w bibliotekach UI, takich jak React (komponenty funkcyjne).
Zapraszam do oglądania.

Friday, September 11, 2020

Węzeł Krajowy w .NET - rozpoczęcie pracy

Rozpocząłem prace zmierzające do wsparcia Węzła Krajowego w bibliotece klienckiej SAML2/ePUAP. Podstawą pracy jest dokument Instrukcja integratora DU opublikowany przez COI.
Po rozwiązaniu problemów z SAML2 przy integracji z ePUAP, integracja z WK wnosi kolejny poziom wyzwania. W szczególności:
  • Węzeł Krajowy wymusza kryptografię opartą na certyfikatach eliptycznych (ECDSA). Problem jest taki że o ile biblioteka standardowa .NET obsługuje szyfrowanie/deszyfrowanie na takich certyfikatach, SignedXml już nie - w metodzie ComputeSignature można wprost podejrzeć if-a, który sprawdza typ certyfikatu i jeśli to nie jest RSA, wyrzuca wyjątek. Jest to poważne zaniedbanie!, z którym niełatwo sobie poradzić (kto ma ochotę na napisanie samodzielnie implementacji podpisywania XMLi, która obsługuje wszystkie niuanse, transformaty, itd. i przy okazji dodaje obsługę certyfikatów eliptycznych?)
  • Węzeł Krajowy upraszcza komunikację bo nie wymaga dodatkowego kroku z wołaniem usługi, ale za to - asercje o użytkowniku zwraca w postaci zaszyfrowanej. Problem w tym że na tę chwilę nie mam pojęcia jak to chwycić w C#
  • Na dzień dzisiejszy jest kawałeczek sukcesu, taki pierwszy set z trzech. Mianowicie, potrafię wygenerować sygnaturę tak żeby AuthnRequest był poprawnie obsłużony. Jeśli to komuś pomoże, to używam Bouncy Castle i uwaga - wbrew intuicji nie można używać signera SHA256WITHECDSA tylko SHA-256withPLAIN-ECDSA - straciłem na tym kilka godzin.
    W kolejnych dniach dalsza część pojedynku...

    Monday, August 24, 2020

    OldMusicBox.ePUAP.Client 0.64

    Biblioteka kliencka dla ePUAP, OldMusicBox.ePUAP.Client w aktualnej wersji domyka zagadnienie komunikacji za pomocą usługi Skrzynek Podawczych. Konkretnie - usługa WS-Doręczyciel::dorecz umożliwiająca wysyłanie powiadomień na skrzynki użytkowników z Urzędowym Potwierdzeniem Doręczenia (UPD) otrzymała wsparcie w generowaniu takich powiadomień.
    Konkretnie, WS-Doręczyciel::Dorecz wymaga aby przekazywany dokument był podpisany podpisem kwalifikowanym w standardzie XAdES. Jako że C# nie posiada natywnego wsparcia dla sygnatur XAdES, trzeba popracować na poziomie niżej, na poziomie SignedXml i wymagane przez XAdES węzły złożyć sobie samodzielnie. ePUAP zadowala się najprostszym wariantem sygnatury.
    Drugi element wsparcia dotyczy szablonu pisma ogólnego, które umożliwia przesłanie dowolnej treści z załącznikami binarnymi (np. PDF) i wizualizuje się na witrynie ePUAP dzięki istniejącej tam transformacie. Załączony przykład pokazuje jak wygenerować dokument w tej składni.
    Połączenie obu mechanizmów pozwala zbudować infrastrukturę dla przesyłania podpisanych dokumentów do użytkowników końcowych ePUAP (osób fizycznych).

    Friday, May 29, 2020

    SessionAuthenticationModule::OnAuthenticateRequest is broken

    Relying on base class library modules should be safe. Unless it's the SessionAuthenticationModule that misses correct handling of some edge cases. Here's the story.
    We use the SessionAuthenticationModule for ages. It's great and reliable in 99.99% of cases. However, there could be a case where there's something wrong with the cookie. The cookie is there but it has been somehow altered in the browser. Either it doesn't contain a valid base64 stream or the stream is valid but there's no XML inside the stream.
    The problem is that the SAM first tries to decode the cookie
    protected virtual void OnAuthenticateRequest(object sender, EventArgs eventArgs)
    {
     HttpApplication httpApplication = (HttpApplication)sender;
     HttpRequest request = HttpContext.Current.Request;
     SessionSecurityToken sessionSecurityToken = null;
     if (!TryReadSessionTokenFromCookie(out sessionSecurityToken) && string.Equals(request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
     {
                   ..
    
    Now, guess what happens when TryReadSessionTokenFromCookie throws an exception? Well, the exception is not caught and thus it's escalated to your code. And now
    • what your code does when an exception occurs? You catch it by a top level exception handler and you possibly shows a generic error page.
    • what a user does when they see the error page? They refresh the browser.
    • is the error gone? Nope, the flawed cookie is still there
    What you get is a nice stacktrace of the problem
       w System.Convert.FromBase64_Decode(Char* startInputPtr, Int32 inputLength, Byte* startDestPtr, Int32 destLength)
       w System.Convert.FromBase64CharPtr(Char* inputPtr, Int32 inputLength)
       w System.Convert.FromBase64String(String s)
       w System.IdentityModel.Services.ChunkedCookieHandler.ReadInternal(String name, HttpCookieCollection requestCookies)
       w System.IdentityModel.Services.ChunkedCookieHandler.ReadCore(String name, HttpContext context)
       w System.IdentityModel.Services.CookieHandler.Read(String name, HttpContext context)
       w System.IdentityModel.Services.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken)
       w System.IdentityModel.Services.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs)
    
    My first attempt to this was to replace the cookie handler by a custom cookie handler. It's not that obvious how to do that but anyway
    public class CustomCookieHandler : System.IdentityModel.Services.CookieHandler
    {
        private CookieHandler _handler; // = new ChunkedCookieHandler();
    
        public CustomCookieHandler(CookieHandler handler)
        {
            if ( handler != null )
            {
                this._handler = handler;
    
                this.Name       = handler.Name;
                this.Path       = handler.Path;
                this.RequireSsl = handler.RequireSsl;
            }
            else
            {
                throw new ArgumentNullException();
            }
        }
    
        // public override 
    
        protected override void DeleteCore(string name, string path, string domain, HttpContext context)
        {
            _handler.Delete(name, path, domain, context);
        }
    
        protected override byte[] ReadCore(string name, HttpContext context)
        {
            try
            {
                return _handler.Read(name, context);                
            }
            catch
            {
                return null;
            }
        }
    
        protected override void WriteCore(byte[] value, string name, string path, string domain, DateTime expirationTime, bool secure, bool httpOnly, HttpContext context)
        {
            _handler.Write(value, name, path, domain, expirationTime, secure, httpOnly, context);
        }
    }
    
    This could be plugged into the pipeline somewhere early with
    FederatedAuthentication.FederationConfigurationCreated +=
            ( s, fede ) =>
            {
                fede.FederationConfiguration.CookieHandler = new CustomCookieHandler(fede.FederationConfiguration.CookieHandler);
            };
    
    Case closed? Not really, looks like the base64 cookie data can be valid but what comes out is not XML and is not parsed! The exception changes to
       w System.Xml.XmlExceptionHelper.ThrowXmlException(XmlDictionaryReader reader, XmlException exception)
       w System.Xml.XmlUTF8TextReader.VerifyNCName(String s)
       w System.Xml.XmlUTF8TextReader.ReadQualifiedName(PrefixHandle prefix, StringHandle localName)
       w System.Xml.XmlUTF8TextReader.ReadStartElement()
       w System.Xml.XmlUTF8TextReader.Read()
       w System.Xml.XmlBaseReader.MoveToContent()
       w System.IdentityModel.Services.SessionAuthenticationModule.GetKeyId(Byte[] sessionCookie)
       w System.IdentityModel.Services.SessionAuthenticationModule.ReadSessionTokenFromCookie(Byte[] sessionCookie)
       w System.IdentityModel.Services.SessionAuthenticationModule.TryReadSessionTokenFromCookie(SessionSecurityToken& sessionToken)
       w System.IdentityModel.Services.SessionAuthenticationModule.OnAuthenticateRequest(Object sender, EventArgs eventArgs)
    
    What this custom cookie handler should do would be then to make sure whatever is read, can be interpreted as XML further down. But this would slow down the custom cookie handler dramatically! Each time a cookie is read, it would be first tried to contain an XML by my custom code and then read as XML again by the existing code.
    An ultimate solution? Delete your SAM cookies in the error page. As simple as that. A side effect is users are logged out of the application after an exception is caught by the top level exception handler.

    Thursday, May 21, 2020

    Materiały edukacyjne - Repository, Unit of Work

    Jak implementować wzorce Repository i Unit of Work żeby architekturze pomagać a nie przeszkadzać?

    Corona hobbies

    It's not that you should only stay home and work remotely. It's a perfect time to spend time on your hobbies. My primary hobby is music and I wish I could spend more time practicing.