<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>
            <code>Zend_Auth_Adapter_DbTable</code> zapewnia możliwość
            przeprowadzenia uwierzytelniania w oparciu o dane przechowywane w
            tabeli bazy danych. Z tego względu, że klasa
            <code>Zend_Auth_Adapter_DbTable</code> wymaga przekazania instancji
            klasy <code>Zend_Db_Adapter_Abstract</code> 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:
            <itemizedlist>
                <listitem>
                    <para>
                        <code>tableName</code>: Jest to nazwa tabeli bazy
                        danych, która zawiera dane uwierzytelniania i do której
                        jest przeprowadzane zapytanie uwierzytelniające.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>identityColumn</code>: 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>
                        <code>credentialColumn</code>: 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ę
                        <code>credentialTreatment</code>.
                    </para>
                </listitem>
                <listitem>
                    <para>
                        <code>credentialTreatment</code>: 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
                        <code>'MD5(?)'</code> czy <code>'PASSWORD(?)'</code>,
                        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>
        </para>
        <example id="zend.auth.adapter.dbtable.introduction.example.basic_usage">
            <title>Podstawowe użycie</title>
            <para>
                Jak wyjaśniono we wprowadzeniu, konstruktor klasy
                <code>Zend_Auth_Adapter_DbTable</code> wymaga przekazania mu
                instancji klasy <code>Zend_Db_Adapter_Abstract</code>,
                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 role="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
                <code>Zend_Auth_Adapter_DbTable</code>. Opcje konfiguracyjne
                mogą być przekazane do konstruktora lub przekazane jako
                parametry do metod dostępowych już po utworzeniu instancji:
            </para>
            <programlisting role="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
                <code>authenticate()</code>:
            </para>
            <programlisting role="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 <code>getIdentity()</code>
                obiektu rezultatu uwierzytelniania, obiekt
                <code>Zend_Auth_Adapter_DbTable</code> pozwala także na
                odebranie wiersza tabeli po udanym uwierzytelnieniu.
            </para>
            <programlisting role="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 <code>Zend_Auth_Adapter_DbTable</code> 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
            <code>Zend_Auth</code> obiekt tożsamości zawierający inne użyteczne
            informacje. W takim przypadku może użyć metody
            <code>getResultRowObject()</code> aby zwrócić obiekt klasy
            <code>stdClass</code>. Poniższy kod ilustruje sposób jego użycia:
        </para>
        <programlisting role="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 Zend_Auth (i odpowiednio
            Zend_Auth_Adapter_DbTable) jest
            <emphasis role="strong">uwierzytelnianie</emphasis> a nie
            <emphasis role="strong">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 Zend_Auth_Adapter_DbTable 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 role="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 role="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 role="php"><![CDATA[
for ($i = 0; $i < 50; $i++)
{
    $dynamicSalt .= chr(rand(33, 126));
}
]]>
        </programlisting>
        <para>
            I skonfigurujmy sterownik bazy danych:
        </para>
        <programlisting role="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 SQL injection), a nie będzie
                miał dostępu do kodu źródłowego, hasła wciąż będą dla niego
                nieprzydatne.
            </para>
        </note>
    </sect2>
</sect1>
