2014-12-13 17:31:13

Aktualizacja ASP.Net Identity do wesji 2

Ostatnimi czasy w jednym z projektów pociągnąłem sobie aktualizacje bibliotek przez Nuget'a. A że byłem leniwy to pociągnąłem je jak leci, nie patrząc co aktualizuje o change logu poszczególnych bibliotek nie mówiąc.

Nie róbcie tego w domu!
Jedną z aktualizacji była nowa wersja mechanizmu ASP.Net Identity. Aktualizacja zmieniła encje wykorzystywane przez mechanizm, nie zmieniła jednak bazy danych (być może ze względu na fakt iż nie korzystam z mechanizmu migracji).
Fakt ten można dość łatwo przeoczyć, wystarczy, że podczas testowania ręcznego nie wykona się żadnej operacji związanej z autoryzacją. Ponieważ miałem aktywną sesję sprzed aktualizacji nie musiałem się ani logować, ani rejestrować, wyjątek został więc zgłoszony tuż po wylogowaniu.

Co robić? Jak żyć?

Aby wyłączyć zgłaszanie wyjątku należy w klasie dziedziczącej po IdentityDbContext, np. ApplicationDbContext, ustawić parametr konstruktora throwIfV1Schema na false.

public ApplicationDbContext()
    : base("CannectionStringName", throwIfV1Schema:false

}

Nie należy tego jednak traktować jako rozwiązanie problemu, jest to jednak krok niezbędny do dalszej naprawy. Drugim krokiem jest włączenie mechanizmu migracji (pod warunkiem że nie jest on włączony) wpisując w Package Manager Console (View -> Other Windows -> Package Manager Console)

Enable-Migrations

Powinna wygenerować się migracja zakładająca strukturę bazy dla pierwszej wersji mechanizmu Identity.
Następnie należy wygenerować migrację dostosowującą strukturę bazy do wersji drugiej. Wykonuje się to wpisując w Package Manager Console

Add-Migration <Nazwa-Migracji>

Mechanizm migracji sam się zorientuje jakie Właściwości zostały dodane do DTO i wygeneruje w miarę poprawne operacje Up i Down. Następnie należy zastosować migrację wpisująć:

update-database - TargetMigration <Nazwa-Migracji>

Na koniec należy usunąć ustawienie flagi throwIfV1Schema na false.

A co gdy się nie może albo nie chce użyć mechanizmu migracji?

Rozwiązaniem może być wykonanie powyższych kroków z tą różnicą, że w kroku nanoszącym zmiany na bazę danych uruchomia się w Package Manager Console

update-database - TargetMigration <Nazwa-Migracji> -Script

Spowoduje to wygenerowanie skryptu, który byłby uruchomiony podczas aktualizacji struktury bazy. Skrypt ten można dostosować do standardów panujących w danej organizacji, a następnie wycofać zmiany przy pomocy Git-a/SVN/CSV/wcześniejszej kopii. Aby oszczędzić innym tej pracy wrzucam skrypt który wygenerowałem w taki sposób.

Subiektywnie

W tej chwili można przeanalizować zmiany w strukturze bazy jakie wnosi druga wersja mechanizmu.

  • Zmiana nazwy kolumny User_Id w tabeli AspNetUserClaims na UserId - oceniam ją pozytywnie sprawia że nazewnictwo kolumn jest spójne, jej wadą jest naruszenie wstecznej kompatybilności, więc jeżeli w kodzie występują zapytania SQL, albo baza zawiera procedury składowane używające tej kolumny należy je poprawić.
  • Dodanie do tabeli Email, EmailConfirmed, PhoneNumber, PhoneNumberConfirmed, TwoFactorEnabled, LockoutEndDateUtc, LockoutEnabled, AccessFailedCount jak dla mnie wygląda mechanizm Identity próbuje za dużo narzucać programistom i architektom. Istniejąca bądź planowana struktura bazy może zakładać przechowywanie wielu adresów email użytkownika, albo brak komunikacji e-mail, to samo dotyczy numeru telefonu. Zliczanie nieudanych prób logowania jest może dobrą praktyką, ale nie konieczną, może być zastąpione innymi heurystykami.
  • Ograniczenie długości kolumn Name w tabeli AspNetRoles do 256 znaków - szansa że ktoś nazwał grupę dłuższą nazwą jest mało-prawdopodobna, a nawet jeżeli to jedyną efektem ubocznym będzie obcięcie długości tej nazwy.
  • Ograniczenie długości kolumn UserName w tabeli AspNetusers do 256 znaków - podobnie jak w powyższym przypadku szanse, że ktoś użytkownik miał nazwę-login dłuższy niż 256 znaków jest maloprawdopodobna. Jeżeli jednak z jakiegoś powodu taki się znalazł trzeba go poinformować o zaistniałej sytuacji.
  • Nadanie wymagania unikalności na kolumnach wymienianych w poprzednich dwóch akapitów. Zmiana jak dla mnie logiczna, unikalność nazwy użytkownika i tak była walidowana na poziomie aplikacji, natomiast prawdopodobieństwo że nazwa roli nie była w jakimś systemie unikalna jest znikoma.
  • Usunięcie kolumny Discriminator z Tabeli AspNetUsers - każde usunięcie elemntu API, czy to kolumny czy to metody, czy właściwości jest złe i ten przypadek nie jest wyjątkiem. Zmusza on programistów do aktualizacji zapytań i procedur składowaZmiana nazwy kolumny User_Id w tabeli AspNetUserClaims na UserId - oceniam ją pozytywnie sprawia że nazewnictwo kolumn jest spójne, jej wadą jest naruszenie wstecznej kompatybilności, więc jeżeli w kodzie występują zapytania SQL, albo baza zawiera procedury składowane używające tej kolumny należy je poprawić.
  • Dodanie do tabeli Email, EmailConfirmed, PhoneNumber, PhoneNumberConfirmed, TwoFactorEnabled, LockoutEndDateUtc, LockoutEnabled, AccessFailedCount jak dla mnie wygląda mechanizm Identity próbuje za dużo narzucać programistom i architektom. Istniejąca bądź planowana struktura bazy może zakładać przechowywanie wielu adresów email użytkownika, albo brak komunikacji e-mail, to samo dotyczy numeru telefonu. Zliczanie nieudanych prób logowania jest może dobrą praktyką, ale nie konieczną, może być zastąpione innymi heurystykami.
  • Ograniczenie długości kolumn Name w tabeli AspNetRoles do 256 znaków - szansa że ktoś nazwał grupę dłuższą nazwą jest mało-prawdopodobna, a nawet jeżeli to jedyną efektem ubocznym będzie obcięcie długości tej nazwy.
  • Ograniczenie długości kolumn UserName w tabeli AspNetusers do 256 znaków - podobnie jak w powyższym przypadku szanse, że ktoś użytkownik miał nazwę-login dłuższy niż 256 znaków jest maloprawdopodobna. Jeżeli jednak z jakiegoś powodu taki się znalazł trzeba go poinformować o zaistniałej sytuacji.
  • Nadanie wymagania unikalności na kolumnach wymienianych w poprzednich dwóch akapitów. Zmiana jak dla mnie logiczna, unikalność nazwy użytkownika i tak była walidowana na poziomie aplikacji, natomiast prawdopodobieństwo że nazwa roli nie była w jakimś systemie unikalna jest znikoma.
  • Usunięcie kolumny Discriminator z Tabeli AspNetUsers - każde usunięcie elemntu API, czy to kolumny czy to metody, czy właściwości jest złe i ten przypadek nie jest wyjątkiem. Zmusza on programistów do aktualizacji zapytań i procedur składowanych z nich korzystających. Kolumna ta była bardziej ogólna niż kolumny związane z blokadą konta.

Podsumowując: o ile same zmiany w wersji drugiej mechanizmu Identity mogą pozytywnie wpłynąć na funkcjonalność i bezpieczeństwo aplikacji to nie podoba mi się wrzucenie ich jako wersji mechanizmu. Według mnie powinny być osobnymi pakietami Nugetowymi, które można zainstalować, jeżeli wystąpi taka potrzeba. Podobnie w przypadku integrowania mechanizmu OAuth2 z pierwszą wersją Identity.

Tagi

C Sharp

Komentarze:

2015-01-04 09:42:48

krissus

Copy-paste spowodował podwojenie się wypunktowanej listy zmian. A poza tym się zgadzam z powyższym

2015-01-04 09:53:32

szogun1987

Dziękuję za uwagę. Poprawka zastosowana :-)