<?xml version="1.0" encoding="UTF-8"?>
<!-- Reviewed: no -->
<sect1 id="zend.auth.adapter.dbtable">
    <title>Uwierzytelnianie w oparciu o tabelę bazy danych</title>
    <sect2 id="zend.auth.adapter.dbtable.introduction">
        <title>Wprowadzenie</title>
        <para>
            <classname>Zend_Auth_Adapter_DbTable</classname> zapewnia możliwość
            przeprowadzenia uwierzytelniania w oparciu o dane przechowywane w
            tabeli bazy danych. Z tego względu, że klasa
            <classname>Zend_Auth_Adapter_DbTable</classname> wymaga przekazania instancji
            klasy <classname>Zend_Db_Adapter_Abstract</classname> do jej konstruktora,
            każda ta instancja jest powiązana z konkretnym połączeniem do bazy
            danych. Inne opcje konfiguracyjne mogą być ustawione za pomocą
            konstruktora lub za pomocą metod instancji, po jednej dla każdej z
            opcji.
        </para>

        <para>
            Dostępne opcje konfiguracyjne to:
        </para>

        <itemizedlist>
            <listitem>
                <para>
                    <emphasis>tableName</emphasis>: Jest to nazwa tabeli bazy
                    danych, która zawiera dane uwierzytelniania i do której
                    jest przeprowadzane zapytanie uwierzytelniające.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>identityColumn</emphasis>: Jest to nazwa kolumny
                    tabeli bazy danych, która reprezentuje tożsamość.
                    Kolumna tożsamości musi zawierać unikalne wartości,
                    na przykład takie jak nazwa użytkownika czy adres
                    e-mail.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>credentialColumn</emphasis>: Jest to nazwa kolumny
                    tabeli bazy danych, która reprezentuje wartość
                    uwierzytelniającą. W prostym schemacie uwierzytelniania
                    opartym o nazwę tożsamości i hasło, wartość
                    uwierzytelniająca odpowiada hasłu. Zobacz także opcję
                    <emphasis>credentialTreatment</emphasis>.
                </para>
            </listitem>
            <listitem>
                <para>
                    <emphasis>credentialTreatment</emphasis>: W wielu przypadkach,
                    hasło i inne wrażliwe dane są zszyfrowane, haszowane,
                    zakodowane lub w inny sposób przetworzone przez
                    jakąś funkcję lub algorytm. Określając metodę
                    przerobienia danych, taką jak na przykład
                    'MD5(?)' czy 'PASSWORD(?)',
                    programista może użyć konkretnej funkcji SQL na danych
                    uwierzytelniających. Z tego względu, że te funkcje są
                    specyficzne dla konkretnych systemów baz danych, zajrzyj
                    do odpowiednich dokumentacji aby sprawdzić dostępność
                    takich funkcji dla twojego systemu bazy danych.
                </para>
            </listitem>
        </itemizedlist>

        <example id="zend.auth.adapter.dbtable.introduction.example.basic_usage">
            <title>Podstawowe użycie</title>
            <para>
                Jak wyjaśniono we wprowadzeniu, konstruktor klasy
                <classname>Zend_Auth_Adapter_DbTable</classname> wymaga przekazania mu
                instancji klasy <classname>Zend_Db_Adapter_Abstract</classname>,
                zapewniającej połączenie do bazy danych, z którym powiązana jest
                instancja adaptera uwierzytelniania. Na początku powinno być
                utworzone połączenie do bazy danych.
            </para>

            <para>
                Poniższy kod tworzy adapter bazy danych przechowywanej w pamięci,
                tworzy prostą strukturę tabeli, a następnie wstawia wiersz, w
                oparciu o który przeprowadzimy później zapytanie
                uwierzytelniające. Ten przykład wymaga dostępnego rozszerzenia
                PDO SQLite:
            </para>

            <programlisting language="php"><![CDATA[
// Tworzymy połączenie do bazy danych SQLite przechowywanej w pamięci
$dbAdapter = new Zend_Db_Adapter_Pdo_Sqlite(array('dbname' =>
                                                  ':memory:'));

// Budujemy zapytanie tworzące prostą tabelę
$sqlCreate = 'CREATE TABLE [users] ('
           . '[id] INTEGER  NOT NULL PRIMARY KEY, '
           . '[username] VARCHAR(50) UNIQUE NOT NULL, '
           . '[password] VARCHAR(32) NULL, '
           . '[real_name] VARCHAR(150) NULL)';

// Tworzymy tabelę z danymi uwierzytelniania
$dbAdapter->query($sqlCreate);

// Budujemy zapytanie wstawiające wiersz, dla którego możemy przeprowadzić
// próbę uwierzytelniania
$sqlInsert = "INSERT INTO users (username, password, real_name) "
           . "VALUES ('my_username', 'my_password', 'My Real Name')";

// Wstawiamy dane
$dbAdapter->query($sqlInsert);
]]></programlisting>

            <para>
                Gdy połączenie do bazy danych oraz dane w tabeli są już
                dostępne, możesz utworzyć instancję klasy
                <classname>Zend_Auth_Adapter_DbTable</classname>. Opcje konfiguracyjne
                mogą być przekazane do konstruktora lub przekazane jako
                parametry do metod dostępowych już po utworzeniu instancji:
            </para>

            <programlisting language="php"><![CDATA[
// Konfigurujemy instancję za pomocą parametrów konstruktora
$authAdapter = new Zend_Auth_Adapter_DbTable(
    $dbAdapter,
    'users',
    'username',
    'password'
);

// ...lub konfigurujemy instancję za pomocą metod dostępowych
$authAdapter = new Zend_Auth_Adapter_DbTable($dbAdapter);
$authAdapter
    ->setTableName('users')
    ->setIdentityColumn('username')
    ->setCredentialColumn('password')
;
]]></programlisting>

            <para>
                W tym momencie intancja adaptera uwierzytelniania jest gotowa
                do przeprowadzenia zapytań uwierzytelniających. W celu
                utworzenia zapytania, wejściowe dane uwierzytelniania
                są przekazywane do adaptera przed wywołaniem metody
                <methodname>authenticate()</methodname>:
            </para>

            <programlisting language="php"><![CDATA[
// Ustawiamy wartości danych uwierzytelniania (np., z formularza logowania)
$authAdapter
    ->setIdentity('my_username')
    ->setCredential('my_password');

// Przeprowadzamy zapytanie uwierzytelniające, zapisując rezultat
$result = $authAdapter->authenticate();
]]></programlisting>

            <para>
                Oprócz możliwości użycia metody <methodname>getIdentity()</methodname>
                obiektu rezultatu uwierzytelniania, obiekt
                <classname>Zend_Auth_Adapter_DbTable</classname> pozwala także na
                odebranie wiersza tabeli po udanym uwierzytelnieniu.
            </para>

            <programlisting language="php"><![CDATA[
// Wyświetlamy tożsamość
echo $result->getIdentity() . "\n\n";

// Wyświetlamy wiersz rezultatów
print_r($authAdapter->getResultRowObject());

/* Wyświetlone dane:
my_username

Array
(
    [id] => 1
    [username] => my_username
    [password] => my_password
    [real_name] => My Real Name
)
*/
]]></programlisting>

            <para>
                Z tego względu, że wiersz tabeli zawiera dane potrzebne do
                uwierzytelniania, ważne jest, aby dane były zabezpieczone przed
                dostępem przez osoby nieuprawnione.
            </para>

        </example>
    </sect2>
    <sect2 id="zend.auth.adapter.dbtable.advanced.storing_result_row">
        <title>Zaawansowane użycie: Stałe przechowywanie obiektu DbTable Result</title>
        <para>
            Domyślnie <classname>Zend_Auth_Adapter_DbTable</classname> po udanym
            uwierzytelnieniu zwraca do obiektu uwierzytelniającego spowrotem
            tę samą tożsamość. W innym przykładzie użycia programista może
            chcieć przechować w stałym mechanizmie przechowywania
            <classname>Zend_Auth</classname> obiekt tożsamości zawierający inne użyteczne
            informacje. W takim przypadku może użyć metody
            <methodname>getResultRowObject()</methodname> aby zwrócić obiekt klasy
            <classname>stdClass</classname>. Poniższy kod ilustruje sposób jego użycia:
        </para>

        <programlisting language="php"><![CDATA[
// uwierzytelniamy za pomocą Zend_Auth_Adapter_DbTable
$result = $this->_auth->authenticate($adapter);

if ($result->isValid()) {

    // przechowujemy tożsamość jako obiekt, w którym zwracane są jedynie
    // pola username oraz real_name
    $storage = $this->_auth->getStorage();
    $storage->write($adapter->getResultRowObject(array(
        'username',
        'real_name'
    )));

    // przechowujemy tożsamość jako obiekt, w którym kolumna zawierająca
    // hasło została pominięta
    $storage->write($adapter->getResultRowObject(
        null,
        'password'
    ));

    /* ... */

} else {

    /* ... */

}
]]></programlisting>
    </sect2>
    <sect2 id="zend.auth.adapter.dbtable.advanced.advanced_usage">
        <title>Przykład zaawansowanego użycia</title>
        <para>
            O ile głównym przeznaczeniem komponentu <classname>Zend_Auth</classname> 
            (i odpowiednio <classname>Zend_Auth_Adapter_DbTable</classname>) jest
            <emphasis>uwierzytelnianie</emphasis> a nie <emphasis>autoryzacja</emphasis>,
            jest kilka problemów które możemy rozwiązać odrobinę przekraczając
            pole zastosowań komponentu. Zależnie od tego jak zdecydujesz
            wyjaśnić swój problem, czasem może być przydatne rozwiązanie
            problemu autoryzacji podczas uwierzytelniania.
        </para>

        <para>
            Komponent <classname>Zend_Auth_Adapter_DbTable</classname> posiada 
            pewien wbudowany mechanizm, który może być użyty do dodania dodatkowych 
            warunków podczas uwierzytelniania, dzięki czemu można rozwiązać niektóre
            problemy.
        </para>

        <programlisting language="php"><![CDATA[
// Wartość pola "status" dla tego konta nie jest równa wartości "compromised"
$adapter = new Zend_Auth_Adapter_DbTable(
    $db,
    'users',
    'username',
    'password',
    'MD5(?) AND status != "compromised"'
);

// Wartość pola "active" dla tego konta jest równa wartości "TRUE"
$adapter = new Zend_Auth_Adapter_DbTable(
    $db,
    'users',
    'username',
    'password',
    'MD5(?) AND active = "TRUE"'
);
]]></programlisting>

        <para>
            Innym przykładem może być implementacja mechanizmu saltingu. Jest to
            technika pozwalająca w znaczny sposób zwiększyć bezpieczeństwo
            aplikacji. Polega ona na tym, że dołączając do każdego hasła losowy
            łańcuch znaków spowodujemy, że niemożliwe będzie przeprowadzenie
            ataku brute force na bazę danych w oparciu o przygotowany słownik.
        </para>

        <para>
            Zaczniemy od zmodyfikowania schematu tabeli bazy danych, aby móc
            przechowywać nasz łańcuch znaków salt:
        </para>

        <programlisting language="php"><![CDATA[
$sqlAlter = "ALTER TABLE [users] "
          . "ADD COLUMN [password_salt] "
          . "AFTER [password]";

$dbAdapter->query($sqlAlter);
]]></programlisting>

        <para>
            W prosty sposób wygenerujmy salt dla każdego rejestrującego się
            użytkownika:
        </para>

        <programlisting language="php"><![CDATA[
for ($i = 0; $i < 50; $i++)
{
    $dynamicSalt .= chr(rand(33, 126));
}
]]></programlisting>

        <para>
            I skonfigurujmy sterownik bazy danych:
        </para>

        <programlisting language="php"><![CDATA[
$adapter = new Zend_Auth_Adapter_DbTable(
    $db,
    'users',
    'username',
    'password',
    "MD5(CONCAT('"
    . Zend_Registry::get('staticSalt')
    . "', ?, password_salt))"
);
]]></programlisting>
        <note>
            <para>
                Możesz jeszcze zwiększyć bezpieczeństwo używając dodatkowo
                statycznego fragmentu łańcucha znaków umieszczonego na stałe
                w kodzie aplikacji. W przypadku, gdy atakujący uzyska dostęp
                do bazy danych (np. za pomocą ataku <acronym>SQL</acronym> 
                injection), a nie będzie miał dostępu do kodu źródłowego, 
                hasła wciąż będą dla niego nieprzydatne.
            </para>
        </note>

        <para>
            Innym sposobem jest użycie metody <methodname>getDbSelect()</methodname> 
            klasy <classname>Zend_Auth_Adapter_DbTable</classname> po utworzeniu
            adaptera. Ta metoda zwróci obiekt klasy <classname>Zend_Db_Select</classname>, 
            który ma być użyty do przeprowadzenia uwierzytalniania. Ważne jest, że
            ta metoda zawsze zwróci ten sam obiekt, niezależnie od tego czy metoda
            <methodname>authenticate()</methodname> została wywołana czy nie.
            Ten obiekt <emphasis>nie będzie</emphasis> posiadał żadnych informacji
            dotyczących nazwy tożsamości i hasła, ponieważ te dane będą umieszczone
            tam dopiero w czasie wywołania metody <methodname>authenticate()</methodname>.
        </para>

        <para>
            Przykładem sytuacji w której można by użyć metody getDbSelect() 
            może być potrzeba sprawdzenia statusu użytkownika, czyli sprawdzenia
            czy konto użytkownika jest aktywne.
        </para>

        <programlisting language="php"><![CDATA[
// Kontynuując poprzedni przykład 
$adapter = new Zend_Auth_Adapter_DbTable(
    $db,
    'users',
    'username',
    'password',
    'MD5(?)'
);

// pobieramy obiekt Zend_Db_Select (przez referencję)
$select = $adapter->getDbSelect();
$select->where('active = "TRUE"');

// uwierytelniamy, z warunkiem users.active = TRUE
$adapter->authenticate();
]]></programlisting>
    </sect2>
</sect1>
