czwartek, 5 czerwca 2014

Managed code vs unmanaged code

Słyszeliście czasami jak sobie dwóch kolegów programistów gada o kodzie zarządzanym/niezarządzanym, że w jednym to tak to działa a w drugim tak, że tutaj to a tutaj tamto, a wy nie bardzo wiedzieliście o co chodzi? No niby znacie .NET framework, prawda? Z racji serii wpisów traktujących o podstawach .NET framework, t tym poście postaram się przedstawić różnicę między kodem zarządzanym (managed code) a niezarządzanym (unmanaged code) w .NET Framework. A post będzie krótki bo i też w zasadzie nie ma o czym gadać a temat prosty i przyjemny. 

Gdy piszemy aplikację, kompilujemy ją i uruchamiamy, niezależnie od tego czy jest to aplikacja desktopowa, aplikacja internetowa czy jeszcze co tam sobie wymyślimy, zawsze działa ona pod kontrolą CLR (Common Language Runtime) o którym pisałem w poprzednim poście. Jest to mechanizm, który kontroluje naszą aplikację, zarządza garbage collectorem, sprawdza czy kod jest bezpieczny i możliwy do uruchomienia, itd. Zasada jest prosta: każdy kod, którą działa pod kontrolą CLR jest nazywany kodem zarządzanym "managed code". Jeśli piszemy jakikolwiek kod w C# lub VB.NET to zawsze będzie to "managed code", innymi słowy będzie to zawsze kod zarządzany przez CLR. 
Unmanaged code jest z kolei kompilowany bezpośrednio do kodu maszynowego. W .NET Framework kod niezarządzany można tworzyć jedynie przy użyciu Visual C++ (oczywiście można również kompilować do kodu zarządzanego, jeśli chcemy np. aby garbage collector załatwiał za nas sprawy związane z czyszczeniem pamięci po nieużywanych obiektach, czy do weryfikacji kodu). Pomyślmy sobie o aplikacji skype jako o przykładzie "unmanage code" (napisany w C/C++). Skype możemy oczywiście uruchomić z poziomu kodu zarządzanego, np. naszej aplikacji napisanej w WinForms, jednak CLR nie ma żadnej kontroli nad kodem niezarządzanym, a co za tym idzie nie ma żadnej kontroli nad naszą aplikacją Skype. Wyobraźmy sobie teraz, że uruchomiliśmy skype z naszej aplikacji, która po chwili łapie jakiś nieobsłużony wyjątek i się wywala - zostanie ona zamknięta czy zrestartowana czy po prostu wywali się z jakiegoś innego powodu. A co z naszym Skypem? - zostanie nadal uruchomiony, bo CLR nie ma nad nim żadnej kontroli. 

IL, CLR, CTS, CLS - czyli jak działa .NET framework

Czy wiecie jak działa .NET Framework? Czy zastanawialiście się, co powoduje, że kod złożony z liter i cyfr, kod który piszecie zamienia się automagicznie na aplikację widoczną gołym okiem. Często w niektórych pytaniach czy odpowiedziach na StackOverflow widziałem jak specjaliści wspominają o IL, CLR, CTS czy CLS. Ba nawet idąc na rozmowę kwalifikacyjną możesz dostać takie pytanie! Przecież to podstawy .NET framework! Ja sam (co wstyd się przyznać) gdy teraz przypominałem sobie wiedzę na ten temat aby popełnić ten post, dodatkowo dowiedziałem się rzeczy, o których wcześniej nie wiedziałem. Jak to się mówi: "Człowiek uczy się całe życie". Jak napisałem wcześniej, ostatnio postanowiłem to nadrobić! Postaram się wyjaśnić to w niniejszym poście co te skróty użyte w tytule oznaczają.

Aby mieć jasny obraz jak wszystko wygląda, spójrzmy najpierw na poniższy schemat zaczerpnięty z wikipedii:

Common Language Runtime diagram.svg
"Common Language Runtime diagram" autorstwa Leif Arne Storset - Praca własna, based on ASCII diagram found at Wikipedia:en:Common Language Runtime. Licencja CC BY-SA 3.0 na podstawie Wikimedia Commons.

Oraz lekko zmodyfikowana, moja własna wersja tego schematu:



Teraz, postaram się zwięźle opisać to co widać na schemacie powyżej, czyli jak to wszystko działa.
  • piszemy swój kod wykorzystując c# bądź vb.net bądź inny język .NET framework (chociaż niekoniecznie)
  • nasz kod musimy skompilować, więc robi to dla nas kompilator, odpowiedni dla danego języka
  • kompilator tworzy tzw. pośredni kod - common intermediate language (CIL bądź IL). Można go rozumieć jako "pół-skompilowany kod" dlatego, że mimo, iż zawiera już pewne instrukcje to kod jeszcze być skompilowany pod odpowiednia platformę i zoptymalizowany pod odpowiednie CPU podczas uruchamiania zwanego runtime. IL jest bezpośrednio tłumaczony dalej na kod bajtowy.
  • następnie nasz IL trafia do CLR czyli Common Language Runtime. To miejsce jest właściwie sercem całego .NET framework. Po otrzymaniu kodu, IL "wrzuca go" do komponentu zwanego JIT (Just in time compiler), który zamienia kod bajtowy na serię instrukcji dla specyficznej platformy. Następnie takie instrukcje są wykonywane. 
Powyższy opis pokazuje przez jakie poszczególne komponenty należy przejść aby nasz kod źródłowy zamienić na kod natywny. Jednak niektóre komponenty muszą być uszczegółowione. Zatem poniżej jeszcze króciutka wzmianka o niektórych komponentach i ich zadaniach:
Common Language Runtime:

  • wbudowany garbage collector - możemy sobie pomyśleć o nim jak o pewnym wątku chodzącym w tle i sprawdzającym, które obiekty w których obszary pamięci są nieużywane i mogą być usunięte z pamięci.
  • Zamienia kod pośredni na kod natywny
  • wbudowany CAS (Code Access Security) - mechanizm kontroli bezpieczeństwa aplikacji oparty na uprawnieniach kodu
  • CV (Code verification) - sprawdza poprawność kodu, np, czy metoda została wywołana z odpowiednią ilością parametrów

CTS (Common type system)

  • jest standardem, który opisuje jak poszczególne typy danych reprezentowane są w pamięci. dzięki któremu możemy wywoływać odpowiedni kod dowolnego języka .NET framework. Innymi słowy, komponent ten zapewnia, że jeśli mamy pewien typ danych zdefiniowany w 2 różnych językach to po kompilacji ten typ danych będzie rozpoznawany jako wspólny typ, dzięki czemu można łatwo komunikować się między dwoma językami. 
CLS Common Language Specification
  • jest to specyfikacja, która zawiera zbiór pewnych zasad i reguł określających jak kod aplikacji może być zamieniony na kod bytowy. Brzmi tragicznie - ale co to właściwie oznacza w przypadku .NET Framework? Oznacza to, że pisząc nasz kod zgodnie z pewnymi określonymi i konkretnymi zasadami w dowolnym języku, będzie on kompatybilny z .NET Framework. Przykład: C# jest językiem w którym rozróżniana jest wielkość liter, VB.NET jest językiem, w którym wielkość liter nie ma znaczenia. Gdy w C# zadeklarujemy 2 zmienne o takiej samej nazwie ale różniące się wielkością liter, kod skompiluje się - ale będzie niepoprawny dla VB.NET. Jest to przykład tzw. CLS-non-compliant code. 

środa, 21 maja 2014

Klonowanie obiektu - deep cloning

Post ku pamięci. Jak szybko i łatwo wykonać klonowanie całego obiektu (tzw. deep cloning). Możemy użyć takiej funkcji:


public object CloneObject(object o)
{
   Type t = o.GetType();
   PropertyInfo[] properties = t.GetProperties();

   Object p = t.InvokeMember("", System.Reflection.BindingFlags.CreateInstance,
   null, o, null);

   foreach (PropertyInfo pi in properties)
   {
      if (pi.CanWrite)
      {
         pi.SetValue(p, pi.GetValue(o, null), null);
      }
   }

   return p;
}

poniedziałek, 19 maja 2014

Prosty klient do konsumowania serwisu ASP.NET Web Api

Załóżmy, że napisaliśmy serwis, który zwraca nam pewne produkty. Nawigując pod odpowiednie dwa adresy URI pobierzemy odpowiednie zasoby:

  • http://localhost/api/products - lista wszystkich produktów
  • http://localhost/api/product/1 - odpowiedni produkt
Wchodzą pod pierwszy adres dostaniemy listę produktów, np. w takiej postaci:



Wiedząc co zwraca nasz serwis możemy wygenerować sobie klasę "Product", z której będziemy mogli później korzystać:
public class Product
{
   public int Id { get; set; }
   public string Name { get; set; }
   public string Description { get; set; }
   public decimal Price { get; set; }
}
W celu połączenia się z naszym Web API, musimy skorzystać z klasy HttpClient. Nasz cały kod może wyglądać następująco:
Metoda, dzięki której tworzymy sobie połączenie do naszego serwisu REST:
private HttpClient GetClient(string mediaType)
{
   string baseUri = @baseAddress + port;
   HttpClientHandler handler = new HttpClientHandler()
   {
      UseDefaultCredentials = false
   };
   handler.Credentials = new NetworkCredential("username", "password");
   HttpClient httpClient = new HttpClient(handler);
   httpClient.BaseAddress = new Uri(baseUri);
   httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(mediaType));
   return httpClient;
}
Metoda dzięki której pobieramy produkt:
private async void Get()
{
   using (var client = GetClient("application/json"))
   {
      HttpResponseMessage response = await client.GetAsync("/api/Products/1");
      if (response.IsSuccessStatusCode)
      {
         Product product = await response.Content.ReadAsAsync();
         //Do something with product
      }
   }
}
Metoda za pomocą której dodajemy nasz produkt:
private async void Post()
{
   using(var client = GetClient("application/json"))
   {
      var message = await client.PosAsJsonAsync("api/products",
      new Product
      { 
         Id=111,
         Name="Product 111 name",
         Description = "Product 111 description",
         Price=222,22
      });
   }
}
Metoda za pomocą której usuwamy nasz produkt:
private void Delete()
{
   using(var client = GetClient("application/json"))
   {
      var message = await Client.DeleteAsync("api/products/1");
   }
}

Należy pamiętać, że metoda "ReadAsAsync" jest metodą rozszerzającą i aby z niej skorzystać należy pobrać następującą bibliotekę z nuget'a: Microsoft.AspNet.WebApi.Client

ASP.NET Web API - basic authentication

Basic authentication (ASP.NET Web API)

Tworzenie serwisu REST z wykorzystaniem ASP.NET Web API jest stosunkowo proste. Gdy nasz serwis jest już wystawiony na świat, prawdopodobnym stanie się fakt odpowiedniego jego zabezpieczenia. Jednym z najprostszych i najbardziej standardowym sposobem jest zabezpieczenie takiego serwisu z wykorzystaniem basic authentication
Basic authentication jest najprostszym i najłatwiejszym mechanizmem autentykacji poprzez zastosowanie pary "username/password" w "plaintext".
Basic authentication działa w następujący sposób:

  1. Jeżeli żądanie "request" wymaga autentykacji, serwer zwraca status code "401" (unauthorized). Odpowiedź taka zawiere "WWW-uthenticate header", które mówi, że serwer wspiera "basic authentication".
  2. Klient wysyła kolejne żądanie ("request") ale tym razem już z odpowiednimi "credentials" umieszczonymi w "WWW-Authenticate header". Charakteryzują je następujące właściwości: 
    1. sformatowane jako string: "name:password"
    2. base64-encoded
    3. brak szyfrowania

Basic authentication wykonywany jest w kontekście pewnej domeny ("realm"). Serwer dodaje nazwę tej domeny do nagłówka WWW-Authenticate header. 
WAŻNE:
  • Ponieważ "credentials", które przesyłane są od klienta na serwer nie są szyfrowane, stosowanie basic authentication jest sensowne i bezpieczne jedynie jeśli komunikacja jest bezpieczna i przebiega przez szyfrowaną wersję protokołu HTTP czyli HTTPS. 
  • Basic authentication nie jest odporne na ataki CSRF (cross-site request forgery)

Niniejszy post przedstawia jak zaimplementować taką autentykację w naszym ASP.NET Web API (post nie dotyczy ASP.NET Web API 2, które jest prostsze i zawiera już zaimplementowane techniki autentykacji serwisu bez konieczności pisania dodatkowego kodu).

AuthorizeAttribute 

Implementacja basic authentication będzie polegała na stworzeniu własnego atrybutu do autoryzacji. W pierwszej kolejności jednak musimy napisać metodę pomocniczą, która wyłuska nam credentials (nazwę użytkownika i hasło) z wiadomości, która przychodzi do serwera. Metoda taka może wyglądać następująco:

private Credentials ParseAuthorizationHeader(string authHeader)
{
   string[] credentials = Encoding.ASCII.GetString(Convert.FromBase64String(authHeader)).Split(new[] { ':' });
   if (credentials.Length != 2 || string.IsNullOrEmpty(credentials[0]) || string.IsNullOrEmpty(credentials[1]))
      return null;
   return new Credentials() { Username = credentials[0], Password = credentials[1], };
}
Teraz wystarczy napisać naszą klasę, która będzie dziedziczyła po "AuthorizeAttribute":
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
   private const string BasicAuthResponseHeader = "WWW-Authenticate";
   private const string BasicAuthResponseHeaderValue = "Basic";

   protected IPrincipal CurrentUser
   {
      get { return Thread.CurrentPrincipal as IPrincipal; }
      set { Thread.CurrentPrincipal = value as IPrincipal; }
   }

   public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
   {
      try
      {
         AuthenticationHeaderValue authValue = actionContext.Request.Headers.Authorization;
         Credentials parsedCredentials = ParseAuthorizationHeader(authValue.Parameter);
         if (parsedCredentials != null)
         {
            this.CurrentUser = new DummyPrincipalProvider().CreatePrincipal(parsedCredentials.Username, parsedCredentials.Password);
            if (!CurrentUser.Identity.IsAuthenticated)
            {
               actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
               actionContext.Response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
               return;
            }
         }
      }
      catch (Exception ex)
      {
         actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized);
         actionContext.Response.Headers.Add(BasicAuthResponseHeader, BasicAuthResponseHeaderValue);
         return;
      }
  }
}
W powyższej implementacji korzystam z klasy "DummyPrincipalProvider", która jest dziedziczy z IPrincipalProvider i jest moją "fake'ową" implemetacją dostarczyciela tożsamości. Wygląda następująco:
public class DummyPrincipalProvider : IProvidePrincipal
{
   private const string Username = "username";
   private const string Password = "password";

   public IPrincipal CreatePrincipal(string username, string password)
   {
      if (username != Username || password != Password)
      {
         return null;
      }
      var identity = new GenericIdentity(Username);
      IPrincipal principal = new GenericPrincipal(identity, new[] { "User" });
      return principal;
    }
 }
W tym miejscu jednak powinna być implemetacja odpowiedniego dortarczyciela tożsamości, np. jeśli dane użytkowników przechowywane są bazie to wtedy najlepszym rozwiązaniem będzie użycie gołego ADO.NET lub z wykorzystaniem EntityFramework do pobrania takich danych z bazy danych. Mając tak napisany atrybut, wystarczy teraz, że klasę lub metodę, którą chcemy zabezpieczyć udekorujemy tym atrybutem:
[CustomAuthorizeAttribute]
[HttpGet, HttpHead]
public Product Get(int id)
{
   var product = productRepository.Get(id);
   if (product == null)
      throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
   return product;
}

sobota, 17 maja 2014

Javascript basics - Part 1

Wstęp

Na pewno każdy developer miał styczność z jQuery w mniejszym lub większym stopniu. Tak jak ja, zrobiłem kilka stron www, dla żony, dla znajomych, w których wykorzystywałem jQuery. Jednak tak na prawdę nigdy nie zagłębiałem się w javascript. jQuery jest wspaniałą biblioteką, jednak przyszedł czas aby poznać podstawy javascript. W związku z tym, wziąłem się za zgłębianie tajników tego języka czytając książkę Javascript Patterns. Książkę polecam każdemu, jest ona wspaniałym źródłem wiedzy dla kogoś takiego jak ja, który wcześniej właściwie nie miał styczności z czystym javascript (oczywiście pewne podstawy znałem, których musiałem się nauczyć aby korzystać z jQuery). Seria tych postów zawiera fundamentalne podstawy javascript, które wg. mnie każdy szanujący się developer powinien znać. 

Deklarowanie zmiennych

W javascript, jak można przypuszczać mamy zmienne globalne i lokalne. Jednak trzeba wiedzieć jak poprawnie je deklarować, ponieważ może okazać się, że zmienna, którą chcieliśmy zadeklarować lokalnie okaże się być zmienną globalną (tak tak, też wydawało mi się to nieprawdopodobne).

DEKLAROWANIE ZMIENNYCH LOKALNYCH
Zmienna jest zmienną lokalną gdy jest poprawnie zadeklarowana w ciele funkcji:

function foo(){
    var localVariable = "this is a localVariable";
}

zmienna localVariable w powyższym przykładzie jest zadeklarowana jako zmienna lokalna. Aby poprawnie tworzyć zmienne lokalne musimy zapamiętać następujące 2 zasady:
  • musi być zadeklarowana w ciele funkcji
  • musi być zadeklarowana ze słowem kluczowym var
Są to dwie podstawowe zasady, które musimy zapamiętać. Nic więcej! Sprawa przedstawia się trochę inaczej jeśli chodzi o zmienne globalne!


DEKLAROWANIE ZMIENNYCH GLOBALNYCH
Podstawowa zasada jest prosta, wszystko co jest deklarowane poza funkcją jest zmienna globalną! Jednakże, jest jeszcze inna zasada. Spójrzmy na poniższy przykład:

var globalVariable = "This is global variable";
secondGlobalVariable = "This is second global variable";
function foo(){
   anotherGlobalVariable = "This is another global variable";
}
Wszystko co jest zadeklarowane bez słowa kluczowego var jest zmienną globalną, nawet jeśli zadeklarowane w ciele funkcji! W związku z tym, jakie są przesłanki przy tworzeniu zmiennych globalnych?

  • każda zmienna zadeklarowana ze słowem kluczowym var poza funkcją jest zmienną globalną
  • każda zmienna zadeklarowana bez słowa kluczowego var jest również zmienna globalną (nawet jeśli jest zadeklarowana w ciele funkcji)
WAŻNE
Przy deklarowaniu zmiennych lokalnych należy pamiętać, że każda zmienna lokalna musi  być zadeklarowana ze słowem kluczowych var. Dlaczego jest to takie ważne? Rozważmy następujący przykład:

function foo(){
   var variable1 = variable2 = "Some text";
}
Zmienna variable1 jest zmienna lokalną, jednak zmienna variable2 jest już zmienną globalną! Jeśli chcemy aby obie były zmiennymi lokalnymi należy je zadeklarować następująco:

function foo(){
   var variable1, variable2;
   variable1 = variable2 = "Some text";
}
Tak zadeklarowane będą obie zmiennymi lokalnymi!

poniedziałek, 12 maja 2014

Bezpieczeństwo WCF - Message i Transport Level Security

Wprowadzenie

Bezpieczeństwo jest istotną częścią każdego kazdego oprogramowania a staje się wręcz niezbędną częścią jeśli chodzi o serwisu webowe - które wystawiane na świat i stają się widocznymi celami różnego rodzaju ataków. 

WCF posiada dwa podstawowe tryby obsługi bezpieczeństwa:
  • bezpieczeństwo na poziomie transportu (Transport Level Security)
  • bezpieczeństwo na poziomie wiadomości  (Message Level Security)
(istnieje jeszcze jeden tryb który łączy dwa powyższe - TransportWithMessageCredential)


Bezpieczeństwo wiadomości można zdefiniować za pomocą 3 aspektów, potocznie nazywanych CIA:
  • Confidentiality - poufność - oznacza, ze osoba lub podmiot, który powinien widzieć wiadomość w rzeczywistości ta osoba/podmiotem jest
  • Integrity - integralność - oznacza, ze wiadomość nie może być w żaden sposób zmieniona lub "zainfekowana" bez wykrycia tego faktu
  • Authentication - uwierzytelnianie - potwierdza tożsamość osoby/podmiotu przeglądającego wiadomość
Brzmi dość tragicznie i zniechęcająco? Można to sobie porównać np. do korzystania z konta bankowego. Tylko osoba do której konto należy może przeglądać dane swojego konta i wykonywać w nim odpowiednie operacje (no chyba ze ktoś inny jeszcze zostanie do tego upoważniony). Wpłacając lub wypłacając pieniądze chcemy mieć pewność ze kwota wpłacona/wypłacona będzie miała swoje odzwierciedlenie na naszym koncie (no może nie zawsze zwłaszcza jeśli chodzi o wypłaty ;)). Dodatkowo oczekujemy od banku iż przed dostępem do naszego konta zostaniemy odpowiednio uwierzytelnieni. Logując się online musimy podać np. nr klienta oraz hasło, korzystając z infolinii tez musimy posiadać odpowiednie dane dostępowe. Jak widać wszystkie niuanse prawdziwego życia w tym kontekście mogą być odzwierciedlone w WCF.

Message Level Security

Message Level Security jest pierwszą podstawową kategorią w całej strukturze bezpieczeństwa Windows Communication Foundation. Jest fizyczną implementacją WS-Security Specification, a jej głównym zadaniem jest rozszerzanie wiadomości SOAP (Simple Object Access Protocol) w celu zapewnienia poufności, integralności i uwierzytelniania. Oznacza to z wszystkie detale związane z bezpieczeństwem wiadomości przechowywane są w niej samej. Ma to swoje wady i zalety:

Zalety
  • End-to-end security. Jak wcześniej zostało napisane, "message level security" rozszerza wiadomość SOAP dodając do niej odpowiednie informacje zabezpieczające. Można powiedzieć, ze w przeciwieństwie do "transport level security" (np. SSL które zabezpiecza wiadomość w przypadku komunikacji punkt-punkt, tzn. ze jeśli istnieją jakieś pośrednie urządzenia to ta informacja jest za każdym razem re-transmitowana), informacje zabezpieczające są z wiadomością przez cały cykl jej życia. 
  • Increased flexibility - zwiększona elastyczność. Niekoniecznie cała wiadomość lecz część wiadomości może być podpisana lub zaszyfrowana. Dzięki temu pewna część wiadomości może być widoczna dla serwisów pośredniczących a część zaszyfrowana/podpisana co zapewnia jej integralność. Domyślnie, w przypadku stosowanie "message level security", WCF nie szyfruje wiadomości lecz podpisuje ja.
  • Support for multiple transports - wsparcie dla wielu typów transportu. Oznacza to, że "message level security" jest niezależne od typu transportu. Zabezpieczoną wiadomość możemy przesłać używając HTTP, TCP czy named pipes. 
Wady
Za MSDN:
  • Wydajność - z racji tego iż wiadomość jest rozszerzona, automatycznie staje się większa
  • Nie można używać message stereaming
  • Wymaga implementacji mechanizmów bezpieczeństwa na poziomie XML oraz wsparcia dla WS-Security Sepcification co może zmniejszyć interoperacyjność 
Kiedy uzywac
  • w przypadku gdy wiadomość będzie krążyła pomiędzy różnymi serwisami
  • w przypadku gdy serwis WCF będzie dostępny poprzez Internet i dostęp do niego będzie wymagał innych serwisów pośredniczących w komunikacji
Jest dostepnę we wszystkich typach bindingów z wyjątkiem netNamedPipeBinding oraz MSmqIntegrationBinding.

Transport Message Security

Generalnie, w odróżnieniu od "Message Level Security" tutaj wszystkie dane dotyczące bezpieczeństwa wiadomości przekazywane są za pomocą warstwy transportującej. Oznacza to iż ten typ zabezpieczeń jest zależny od typu wybranego transportu co automatycznie zmniejsza jego możliwości jeśli chodzi o opcje uwierzytelniania. Kazdy z protokolow transportowych (TCP, IPC, MSMQ czy HTTP) implementuje własny mechanizm zabezpieczenia wiadomości. Najpopularniejszym podejściem jest wykorzystanie Secure Socket Layer (SSL) do szyfrowania bądź podpisywania zawartości pakietów wysyłanych przez HTTPS.
Zalety

  • zapewnia interoperacyjność ponieważ nie musi implementować WS-Security Specification
  • większa wydajność

Wady

  • zapewnia bezpieczeństwo punkt-punkt
  • ograniczona liczba opcji uwierzytelniania w stosunku do message level security
  • zależne od protokołu transportującego

Kiedy używać

  • gdy wiadomość będzie wysyłana bezpośrednio między dwoma punktami bez angażowania jakichkolwiek pośredników, np. w przypadku bezpośredniej komunikacji klient-serwer
  • gdy serwer i klient są w lokalnej sieci intranet
Jest dostępny we wszystkich typach bindingów z wyjątkiem wsDualHttpBinding

Show me the code

Jak właściwie wszystko w WCF, bezpieczeństwo na poziomie wiadomości można zdefiniować używajac kodu deklaratywnego lub imperatywnego. Obie implementacji są banale i wyglądają następująco:
Kod deklaratywny:


   
       
   

Kod imperatywny (z wykorzystaniem domyslnego (bezparametrowego kontruktora)):
WSHttpBinding wsHttpSecurity = new WSHttpBinding();
wsHttpSecurity.Security.Mode = SecurityMode.Message;
Kod imperatywny (okreslanie security level w konstruktorze):
WSHttpBinding wsHttpSecurity = new WSHttpBinding(SecurityMode.Message);

wtorek, 6 maja 2014

Kontrola wiadomości WCF - IDispatchMessageInspector

Jeśli korzystaliście kiedyś z WCF (lub pisaliście jakikolwiek inny serwis z wykorzystaniem SOAP), możliwe że mieliście zaimplementowany mechanizm kontroli wiadomości - np. mechanizm logowania wiadomości, które przychodzą do serwera. W tym poście chciałbym opisać jak zaimplementować taki mechanizm kontroli wiadomości przed jej przetworzeniem przez serwis WCF lub przed wysłaniem odpowiedzi do klienta. Dlaczego właściwie chcielibyśmy robić taką kontrolę? Tak wcześniej wspomniałem np. aby logować takie wiadomości na przykład w celach statystycznych lub w przypadku gdy chcemy coś szczególnego dodać do naszej wiadomości - odpowiedzi  na przykład w nagłówku przed wysłaniem jej do aplikacji klienckiej.
W WCF'ie możemy taką funkcjonalność zaimplementować w "dość prosty" sposób.  (Prosty dla kogoś, kto już miał wcześniej styczność z WCF'em).

Na początku musimy stworzyć klasę, w której będziemy robić inspekcję wiadomości WCF - to własnie tak klasa będzie implementowała interfejs IDispatchMessageInspector. Interfejs ten posiada dwie metody:

  • AfterReceiveRequest - wykonywana w momencie gdy wiadomość została odebrana przez serwis lecz przed przekazaniem jej do odpowiedniej metody serwisu
  • BeforeSendReply - wywoływana po przetworzeniu naszej wiadomości przez odpowiednią metodę i przed wysłaniem odpowiedzi do aplikacji klienckiej

Dodatkowo, z racji tego iż nasza klasa będzie określała pewne zachowanie dla serwisu lub endpointa musi również implementować odpowiednio:
IServiceBehavior lub IEndpointBehavior lub oba interfejsy jeśli zdecydujemy się, że może być używana zamiennie dla serwisu i endpointa.
Kod naszej klasy wygląda następująco:

public class ServerInspector : IDispatchMessageInspector, IEndpointBehavior, IServiceBehavior
    {
        #region IDispatchMessageInspector Members

        public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
        {
            Debug.WriteLine("Called after an inbound message has been received but before the message is forwared to the target operation");
            return request;
        }

        public void BeforeSendReply(ref System.ServiceModel.Channels.Message reply, object correlationState)
        {
            Debug.WriteLine("Called after the operation has returned but before the reply is actually relayed");
        }

        #endregion

        #region IEndpointBehavior Members

        public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }

        #endregion

        #region IServiceBehavior Members

        public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                foreach (var endpoint in dispatcher.Endpoints)
                {
                    endpoint.DispatchRuntime.MessageInspectors.Add(this);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
        {
        }

        #endregion
    }

Należy zwrócić uwagę na metody ApplyDispatchBehavior w których dodajemy do naszego endpointa aktualnego inspektora. Bez dodania inspektora, nasz kod nie będzie działał. Jeśli chodzi o samą kontrolę wiadomości to może być ona wykonywana w 2 metodach o których wcześniej wspomniałem : AfterReceiveRequest oraz BeforeSendReply. Ja akurat w tych metodach dodałem zwykłe wyświetlanie wiadomości, jednak należy pamiętać, że własnie te metody przeznaczone są do przeprowadzania właściwej kontroli wiadomości WCF. 

Kolejnym krokiem jest dodanie "klasy rozszerzającej" która będzie dziedziczyła z BehaviorExtensionElement. Musimy mieć taką klasę ponieważ jak zobaczymy z chwilę, w pliku konfiguracyjnym ustawimy sobie takie własnie rozszerzenie zachowania naszego serwisu. Najważniejszą sprawą jaką trzeba zapamiętać, że taka klasa musi dziedziczyć z klasy BehaviorExtensionElement, w przeciwnym wypadku kompilator nam zaprotestuje i wywali błąd. W związku z tym nasza klasa rozszerzająca wygląda następująco:
public class ServerInspectorExtension : BehaviorExtensionElement
{
   public override Type BehaviorType
   {
      get { return typeof(ServerInspector); }
   }

   protected override object CreateBehavior()
   {
      return new ServerInspector();
   }
}
Jak widzimy klasa ta tworzy nową instancję naszego inspektora, którego dodaliśmy wcześniej. Ostatnim krokiem jest skonfigurowanie "behaviorExtension" w pliku konfiguracyjnym. Nasze rozszerzenie wygląda następująco:

   
      
   

Takie rozszerzenie przypisujemy do odpowiedniego "behavior'a":

   
      
   

środa, 16 kwietnia 2014

Stronicowanie (pagination) w SQL Server - ROW_NUMBER() vs RANK() vs DENSE_RANK()

Zastanawialiście się kiedyś jak robić stronicowanie w SQL Server? Ja ostatnio musiałem coś takiego zrobić, ale nie używając żadnego ORM'a lecz jedynie gołe ADO.NET. Sprawa wydawała się być banalna, napisałem zapytanie ale okazało się, że wyniki są jakieś dziwne i jeszcze się powielają mimo tego iż miałem DISTINCTA na zapytaniu. Poszperałem trochę w necie i doczytałem, że powinienem zamiast funkcji ROW_NUMBER() używać DENSE_RANK(). Okazuje się, że ROW_NUMBER() nadaje każdemu wierszowi kolejny, unikalny number. DENSE_RANK() nadaje unikalny numer ale jedynie wierszom, które się nie duplikują.

Na tej stronie możecie znaleźć fajnie to wszystko wyjaśnione. Również różnicę między DENSE_RANK() a RANK(). 
W skrócie:

  • ROW_NUMBER - nadaje każdemu wierszowi unikalny numer, nie zważając na duplikaty
  • RANK() - nadaje unikalnym wierszom rangę (unikalny numer) z przerwami w rankingu
  • DENSE_RANK() - nadaje unikalnym wierszom rangę (unikalny number) bez przerw w rankingu.  

Z tej powyższej strony wzięta jest również poniższa tabelka, która na szybko przedstawia różnicę między ROW_NUMBER(), RANK() oraz DENSE_RANK()
+---+------------+------+------------+
| V | ROW_NUMBER | RANK | DENSE_RANK |
+---+------------+------+------------+
| a |          1 |    1 |          1 |
| a |          2 |    1 |          1 |
| a |          3 |    1 |          1 |
| b |          4 |    4 |          2 |
| c |          5 |    5 |          3 |
| c |          6 |    5 |          3 |
| d |          7 |    7 |          4 |
| e |          8 |    8 |          5 |
+---+------------+------+------------+

wtorek, 15 kwietnia 2014

Asynchroniczne pobieranie danych i uaktualnianie CollectionView w WPF

W WPF klasa CollectionView jest niezwykle przydatna jeśli chcemy w prosty sposób zaimplementować mechanizm sortowania, grupowania lub filtrowania. Używając jej jednak w programowaniu asynchronicznym podczas uaktualniania tej kolekcji z innego wątku, dostaniemy następujący komunikat:

The calling thread cannot access this object because a different thread owns it

Nie dziwota, jest to normalnie zachowanie, które często się zdarza, ponieważ CollectionView nie obsługuje zmian własnej kolekcji z wątku innego niż Dispatcher. Co jednak zrobić aby obejść ten problem? Po pierwsze musimy mieć obiekt dispatcher'a, która w WPF'ie jest klasą zapewniającą usługi do zarządzania wątkami, np. synchronizacji. Jeśli korzystamy ze wzorca MVVM (Model-View-ViewModel), pierwsze co musimy zrobić to przekazać obiekt dispatcher'a do ViewModelu, np w konstruktorze:

private Dispatcher currentDispatcher;

public ViewModel(Dispatcher currentDispatcher)
{
    this.currentDispatcher = currentDispatcher;
}
Następnie aby dodawać obiekty do kolekcji CollectionViewSource, należy wykonać następujący kod:
currentDispatcher.Invoke(new Action(()=>
{
    this.collectionViewSource.Add(someObject);
}));

piątek, 11 kwietnia 2014

Database schema modeling - SQL Server Data Tools!

Post ku pamięci. Czy zdarzyło wam się kiedykolwiek, że mieliście 2 bazy danych, w których musieliście porównać ich schemę? Ja ostatnio miałem taką zagwozdkę, robiłem nową funkcjonalność i przepadł mi gdzieś skrypt, który uaktualniałby schemę bazy danych. Wiedziałem, że są pewne narzędzia, które by mi pomogły jednak nie chciałem korzystać z narzędzi firm trzecich. Poszperałem trochę w necie i okazało się, że Microsoft zrobił takie narzędzie. Nazywa się SQL Server Data Tools. Dzięki temu narzędzi możemy porównać sobie schemę dwóch baz, a nawet wygenerować skrypt zmian, który uaktualni nam naszą bazę danych.



SQL Server Data Tools jest dostarczane razem z Visual Studio 2013, jest możliwość dociągniecia instalki również do Visual Studio 2010 i Visual Studio 2012. Konkretną wersję można ściągnąć tutaj
http://blogs.technet.com/b/dataplatforminsider/archive/2013/11/13/microsoft-sql-server-data-tools-update.aspx

Co oferuje nam jeszcze to narzędzie? Możemy oczywiście graficznie modelować sobie naszą bazę danych. Mało tego narzędzie umożliwia nam nawet porównać dane w obu bazach za pomocą opcji "Data comparision" oraz tworzyć unit testy naszej bazy: więcej informacji znajdziemy tutaj

poniedziałek, 10 marca 2014

Szukanie procedury składowanej zawierającej ciąg znaków

Ostatnio zdarzyło mi się, że musiałem znaleźć wszystkie procedury składowane zawierające słowo "external". O tyle ważne, że w SQL Server 2012 jest to zarezerwowane słowo kluczowe a nasza baza danych, która wcześniej nie działała na SQL Server 2012 dość liberalnie podchodziła do użycia słowa "external" w procedurach składowanych m.in. nie zapisując nazwy tabeli o nazwie "external" w nawiasy kwadratowe. Czyli zamiast dbo.NazwaTabeli.[External] było dbo.NazwaTabeli.External. Przy takim zapisie program wywalał się wykonując procedurę składowaną na SQL Server 2012. 
Aby zatem znaleźć procedury składowane zawierające pewien ciąg znaków, można wykorzystać taki kod:

SELECT DISTINCT o.name AS Object_Name,o.type_desc
FROM sys.sql_modules m 
INNER JOIN sys.objects o 
ON m.object_id=o.object_id
WHERE m.definition Like '%external%'
order by Object_Name

czwartek, 6 marca 2014

Czyszczenie ClickOnce application cache bez mage.exe

Dawno temu kiedy zaczynałem przygodę z ClickOnce stworzyłem skrypcik, która czyścił application cache.
Generalnie wykorzystywała ona plik mage.exe w następujący sposób:

mage.exe -cc

Okazuje się, iż jest możliwość wyczyszczenia application cache dla ClickOnce bez potrzeby korzystania z aplikacji mage.exe
Wystarczy w oknie komend wpisać następującą komendę:

rundll32 dfshim CleanOnlineAppCache

EDIT:
Wygląda na to, iż powyższa komenda nie czyści jednak application cache. 
Ktoś na forum stackoverflow napisał, że działa ona tak samo jak mage.exe -cc:
I wanted to add the posters comment to my answer so people don't miss it. You don't have to have mage installed to clear the cache; running rundll32 dfshim CleanOnlineAppCache from the command line accomplishes the same thing as mage -cc.
Jednak z własnego doświadczenia muszę stwierdzić, że jednak nie. Nie jestem do końca pewien jaka jest różnica, gdy znajdę, na pewno to napiszę.

piątek, 7 lutego 2014

Kopiowanie zawartości jednej tabeli do drugiej

Załóżmy, że mamy dwie identyczne tabele, Table1, Table2. Table1 trzyma aktualne dane, Table2 dane historyczne. Ich struktura jest identyczna. Czasami zdarza się, że musimy przekopiować pewną ilość danych z jednej tabeli do drugiej. 
Można to zrobić następujących skryptem:

INSERT INTO Table1
SELECT * FROM Table2
WHERE some_condition

Bardzo prosty skrypcik. A piszę o nim ponieważ miałem z nim chwilę problem gdy napisałem go w takiej postaci (Z NAWIASAMI):
INSERT INTO Table1
(SELECT * FROM Table2
WHERE some_condition)

wtedy zapytanie wywala błąd. Prawdopodobnie dlatego, że oczekuje w nawiasach dokładnych parametrów a nie podzapytania.
Na szczęście szybko udało się rozwiązać problem. 

Wybieranie nazw kolumn w tabeli

Czasami zdarza się, że nasza tabela zawiera sporą ilość kolumn a my musimy wylistować wszystkie kolumny. Wiadomo, programista to leń i zawsze chce sobie ułatwić pracę. Dlatego poniższy skrypt już nie raz był przeze mnie używany. Wybiera on wszystkie kolumny z danej tabeli:

SELECT COLUMN_NAME, *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'YOUR_TABLE_NAME'

czwartek, 23 stycznia 2014

Instalacja SQL Server 2012 Management Studio Express

Ściągasz sobie SQL Server 2012 i instalujesz. Wchodzisz w menu start -> SQL Server 2012, chcesz odpalić management studio i zonk! Nie ma! No nie ma! Chyba z godzinę szukałem gdzie i jak to zainstalować. Ściągnąłem od początku SQL Server 2012 Express i nadal dupa. Okazuje się, że trzeba ściągnąć odpowiedni pakiet, o stąd:
http://www.microsoft.com/en-us/sqlserver/editions/2012-editions/express.aspx