Skip to content

Conversation

@aurelijusb
Copy link
Owner

Rebased: #278

Išnaudokime Symfony teikiamas galimybes naudotojų atpažinomui ir teisių valdymui.

Sugeneruota su scipts/backend.sh
  composer require symfony/security-bundle

Per naršyklę nieko nematome, bet galima patikrinti, ar veikia su scripts/backend.sh
  bin/console config:dump-reference security
  bin/console debug:config security

Sukuriamas pavyzdinis security.yaml failas.
dump-reference komanda parodo galimas nustatymų reikšmes.
debug:config komanda parodo apskaičiuotas reikšmes (esamus nustatymus).

Dokumentacija:
  https://symfony.com/doc/current/security.html
  https://symfony.com/doc/current/reference/configuration/debug.html
Profiler toolbar leidžia matyti ne tik kokiu greičiu užsikrovė puslapis, bet ir koks naudotojas yra prisijungęs.
Sugeneruota routes/dev konfigūracija, kad leistų užsikrauti Profiler juostai naudojant AJAX.

Su savimi prideda ir kitas derinimui (angl. debug) skirtas bibliotekas. Pvz {{ dump(kintamas) }} komandą Twig'e.

Sugeneruota su scripts/backend.sh
  composer require profiler --dev

Ši biblioteka skirta tik programavimo etapui (angl. development), o ne produkcinei sistemai (žr. require-dev ir ['dev' => true, 'test' => true])

Dokumentacija:
  https://symfony.com/doc/current/profiler.html
Galima rašyti viską patiems, arba galima susigeneruoti pavyzdį.
MakerBundle standartiškai klausinėja parametrų,
bet dėl demonstracijos greičio tiesiog naudojama vienos eilutės komanda
(no-interaction).

Sugeneruota su scripts/backend.sh
  ./bin/console make:user User --is-entity --identity-property-name=email --with-password --use-argon2 --no-interaction

Naudojami parametrai:
  --is-entity – duomenys bus saugomi duomenų bazėje
  --identity-property-name – naudotojai bus atskiriami pagal el. pašto adresą (nes pagal jį dažniausiai galima priminti slaptažodį ir įprasta, kad 1 naudotjas = 1 el. pašto adresas)
  --with-password – naudojamas slaptažodis
  --use-argon2 – naudojamas naujausias (PHP 7.3) šifravimo (hash = maišos funkcijos) algoritmas
  --no-interaction – neklausinėti parametrų (patogiau demostracijai)

Dokumentacija:
  https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
Naudotojo modelį apsirašėme kaip Entity klasę, bet vis tiek reikia,
kad duomenų bazėje būtų atitinkamos lentelės.

Sugeneruota su scripts/backend.sh
  bin/console make:migration
  bin/console doctrine:migrations:migrate

Patikrinimui su scripts/mysql.sh
  DESCRIBE symfony.user;

Dokumentacija:
  https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
  https://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html
Ji bus reikalinga registracijos formos kūrimui
(kitu atveju make:registration-form mestų klaidą)

Sugeneruota su scripts/backend.sh
  composer require form validator

Dokumentacija:
  https://symfony.com/doc/current/validation.html
Sugeneruota naudojant scripts/backend.sh
  bin/console make:registration-form

Naudojant atsakymus:
  Do you want to add a @UniqueEntity validation annotation on your User class to make sure duplicate accounts aren't created? [yes]
  Do you want to automatically authenticate the user after registration? [no]
  What route should the user be redirected to after registration? [home]

Kol nėra pilnai pabaigas prisijungimas, negalime naudoti "automatically authenticate" funkcionalumo.
Po registracijos tiesiog nukreipiame į egzistuojantį route'ą (pagrindinio puslapio adresą).

Laukelių suvedimui naudojamos Symfony formos.
Sutikimo su sąlygomis laukelis atėjo kartu su GDPR įstatymu (duomenų apsaugos).

Patikrinimui naršyklėje:
  http://127.0.0.1:8000/register

Patikrinimui per scripts/mysql.sh
  SELECT * FROM symfony.user;

Dokumentacija:
  https://symfony.com/doc/current/doctrine/registration_form.html
  https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
  https://symfony.com/doc/current/forms.html
  https://gdpr-info.eu/
Jau turėjome naudotojo klasę ir registraciją – trūko tik pačio prisijungimo.

Kodas sugeneruotas su scripts/backend.sh
  bin/console make:auth

Pasirinkti atsakymai:
  What style of authentication do you want? [1]
  The class name of the authenticator to create [AppCustomAuthenticator]
  Choose a name for the controller class [SecurityController]
  Do you want to generate a '/logout' URL? (yes/no) [yes]

Yra sukuriama forma http://127.0.0.1:8000/login adresu,
bet bandant prisjungti reikalauja pakeitimų:
  TODO: provide a valid redirect inside /code/src/Security/AppCustomAuthenticator.php

Dokumentacija:
   https://symfony.com/doc/current/security/form_login_setup
   https://symfony.com/doc/current/security/guard_authentication.html#step-3-configure-the-authenticator
Nukreipiame prisjungusį naudotoją į pagrindinį puslapį.
Survarkome eilutes, kad atitiktų kodo stilių.

Prisijungimas veikia, bet nėra nei Atsijungti mygtuko,
nei automatinio prisjungimo po registracijos.

Dokumentacija:
  https://symfony.com/doc/current/security/form_login_setup#finishing-the-login-form
Twig šablonose yra specialus kintamasis "app",
kurio vienas iš elementų ir yra "user".

Kai nėra prisijungta – "app.user" yra null.
Kai prisijungiama, Symfony pakeičia kintamąjį į konkretų naudotoją.

Twige getter'iai pasiekiami ir bet get žodelio (pvz. $user->getUserName -> user.username, $user->getRoles -> user.roles)

Užmiršus, kokie adresai naudojami, pasitikrinti galima per scripts/backend.sh
  bin/console debug:route

Dokumentacija:
  https://symfony.com/doc/master/templating/app_variable.html
  http://api.symfony.com/master/Symfony/Component/Security/Core/User/UserInterface.html
  https://getbootstrap.com/docs/4.0/components/navbar/
Patogu, jei norima nukreipti į Prisijungimą arba neužtenka standartinių Security konfiguracijos galimybių.
Naudotojo objektas įdedamas į kontrolerio metodą (analogiškai kaip Service'ams).
Alternatyva būtų naudoti "$this->getUser()", bet naudojant per argumentus –
lengviau bus perkeleti į kitą projektą (ar paruošti testams).

Nors pavyzdžiuose yra Security $security, $security->getUser(),
bet praktikoje kitos funkcijos (credentials, attributes) rečiau naudojamas
nei tiesiogiai kviečiamas UserInterface.

Jei naudotojas(-a) neprisijungęs, reikia "UserInterface $user = null",
kitaip Symfony nežinos ką daryti su null (neprisijungęs), kurio negalima paversti į UserInterface objektą.

Pastaba:
  AccessDeniedException yra standartinis atsakymas, kai neprisiregistruojama, bet tada grąžinas HTTP 500 statusas.
  Produkcinėje aplinkoje reikėtų reaguoti į kernel.exception arba užsidėti templates/bundles/TwigBundle/Exception/error.html.twig žmogiškai klaidos žinutei

Dokumentacija:
  https://symfony.com/doc/current/security.html#b-fetching-the-user-from-a-service
  https://symfony.com/doc/current/controller.html#redirecting
  https://symfony.com/blog/new-in-symfony-3-2-user-value-resolver-for-controllers
  https://symfony.com/doc/current/security.html#securing-controllers-and-other-code
Sugeneruota su scripts/backend.sh
  composer require admin

easycorp/easyadmin-bundle yra oficialiai siūlomas Symfony kūrėjų.
Taigi galima tikėtis geresnio suderinamumo su Symfony karkasu.

Nors ir yra sukuriamas kelias (route):
  ./bin/console debug:route easyadmin

Vis tiek išmes klaidos pranešimą, nes nenurodėme, ką administruoti:
  The backend is empty because you haven't configured any Doctrine entity to manage

Dokumentacija:
   https://symfony.com/doc/master/bundles/EasyAdminBundle/book/installation.html
   https://github.com/EasyCorp/EasyAdminBundle
   https://packagist.org/packages/easycorp/easyadmin-bundle
   https://symfony.com/doc/current/translation.html
Per naršyklę atidarius:
  http://127.0.0.1:8000/admin/

Jau kažką rodo, bet dar yra daug kas, ką reikėtų tobulinti
(neatpažįsta prisijungusio naudotojo, pasiekiamas visų, nėra visų laukų).

Dokumentacija:
  https://symfony.com/doc/master/bundles/EasyAdminBundle/book/your-first-backend.html
Nes negerai, kad kiekvienas žmogus galėtų redaguoti/matyti visus registruotus naudotojus.

Tai nėra visiškai saugu! Bet kol mokomės tokiu būdu galima lengvai patestuoti.
Ir tai yra geras būdas suvokti "access_control" veikimą.

Patikrinimui:
  Neprisijungus ir į http://127.0.0.1:8000/admin/ nukreipia į kitą puslapį
  Prisjungus rodo normaliai.

Abejojant dėl konfiguracijos, galima ją pasitikrinti su scripts/backend.sh
  bin/console config:dump-reference security access_control
  bin/console debug:config security access_control

Abejojant dėl nuorodų, galima ją pasitikrinti su scripts/backend.sh
  bin/console debug:route

Dokumentacija:
  https://symfony.com/doc/current/security/access_control.html
Ją naudosime pasikeisti administratoriaus rolei.
Daroma prielaida, kad prisijungimas prie serverio yra labiau apsaugotas
nei naršyklė ir tai yra intuityviau nei keisti kažką per duomenų bazę.

Sugeneruota per scripts/backend.sh
  bin/console make:command app:promote-user

Patikrinimui per scripts/backend.sh
  bin/console list app
  bin/console app:promote-user

Dokumentacija:
  https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
  https://symfony.com/doc/current/components/console.html
Per komandinę eilutę pridedame ROLE_ADMIN rolę registruotam naudotojui –
taip apsaugome administravimo panelę nuo įsilaužėlių.

UserRepository klasėje pasidarome papildomą metodą,
kad būtų patogiau ieškoti elemento (duomenų bazės įžanga).

Patikrinimui per scripts/backend.sh
  bin/console app:promote-user --help
  bin/console app:promote-user aurelijus@banelis.lt

Ir tada bandome užeiti į http://127.0.0.1:8000/admin

Pakeitus rolę – gali reikėti iš naujo prisijungti,
nes naudotojo pasikeitimas tikrinamas pagal serialize
metodą su išsaugota versija Sesijoje.

Dokumentacija:
  https://symfony.com/doc/current/components/console.html
  https://symfony.com/doc/3.4/doctrine/repository.html
  https://symfony.com/doc/current/security.html#roles
  https://symfony.com/doc/current/security/user_provider.html#understanding-how-users-are-refreshed-from-the-session
Jei prisjungtume naudotoju, kuris turi ne tą rolę,
Symfony duoda 403 Forbidden klaidą.

Jei naudotojas iš viso nėra prisijungęs –
Symfony yra pakankamai protingas, kad nukreiptų į prisijungimo adresą.

Kadangi jau yra gražus šablonas, tai galima naudoti AccessDeniedException klaidą,
kuri sugeneruos 403 klaidos puslapį.

Kai APP_ENV=dev (paleidus scripts/install-dev.sh) atvaizdavimą šablonas matomas per:
  http://127.0.0.1:8000/_error/403.html

Kai APP_ENV=prod (paleidus scripts/install-prod.sh) ši klaida bus rodoma per:
  http://127.0.0.1:8000/admin/
  Kai naudotojas yra prisijungęs, bet neturi ROLE_ADMIN teisių

Dokumentacija:
  https://symfony.com/doc/current/controller/error_pages.html
  https://symfony.com/doc/current/bundles/override.html#templates
Per Profiler Toolbar -> Twig -> Rendering Call Graph
randame mus domintančią dalį.

Per Profiler Toolbar -> Configuration -> Enabled Bundles
randame Bundle pavadinimą ir atitikmenį failų sistemoje.

Nukopijuojame turinį į templates/bundles/BUNDLE_PAVADINIMAS/ORIGINAL_ADRESAS

Atkreipkite dėmesį, kad pavadinimas gali būti EasyAdminBundle, o ne EasyAdmin!

Jei nepasikeičia, gali prireikti: bin/console cache:clear

Dokumentacija:
  https://symfony.com/doc/current/bundles/override.html#templates
Pritaikome layout.html.twig šabloną,
kad jis naudotų mūsų projekto base.html.twig tėvinį šabloną.

Reikia daryti atsargiai, nes lengva pralesiti kokią nors CSS/JavaScript
dalį ir biblioteka nebeveiks.

Dokumentacija:
  https://symfony.com/doc/current/bundles/override.html#templates
  https://twig.symfony.com/doc/3.x/tags/extends.html
Kitų rašytos bibliotekos turi savo dokumentaciją,
todėl norint jas pritaikyti savo reikmėms,
reikia eiti per jų dokumentaciją, tikrinti su Profiler toolbar, xDebug ir pan.

Šiame pavyzdyje skirtingai atvaizduojamos rolės (templates parametras).
Naudojamas formos tipas rolėms (type parametras).
Slaptažodžio įrašymui naudojami virtualūs metodai (reikšmės apsaičiuojamos, bet neįrašomos į duomenų bazę – įrašymui išskaičiuojama iš kitų laukų).

Dokumentacija:
  https://symfony.com/doc/master/bundles/EasyAdminBundle/book/list-search-show-configuration.html#customize-the-properties-displayed
  https://symfony.com/doc/master/bundles/EasyAdminBundle/book/edit-new-configuration.html#virtual-properties
  https://symfony.com/doc/current/reference/forms/types.html
  https://symfony.com/doc/current/reference/forms/types/collection.html
Kadangi registracijos puslapis naudoja Symfony form,
todėl galima vienoje vietoje nustatyti šabloną
ir visos iki tol naudotos formos gaus gražesnį (telefonams tinkamesnį)
dizainą.

Patikrinimui:
  http://127.0.0.1:8000/register

Dokumentacija:
   https://symfony.com/doc/current/form/bootstrap4.html
   https://symfony.com/doc/current/forms.html
Jei norime, kad naudotojas turėtų daugiau informacijos,
reikia pridėti papildomus laukus į Entity kalsę.

PHPStorm turi gerą funkciją sugeneruoti Getter'ius ir Setter'ius
(jie bus naudojami Twig'e ir AdminBundle)

Šioje stadijoje duomenų modelis ir duomenų bazė gali skirtis,
todėl galima pamatyti klaidų:
  An exception occurred while executing 'SELECT
  SQLSTATE[42S22]: Column not found

Tą pataisysime sekančiame žingsnyje

Dokumentacija:
  https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/cookbook/working-with-datetime.html
  https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html#column
Naujiems laukams reikia naujų stulpelių duomenų bazės lentelėse.

Symfony turi MigrationBundle, kuris gali sugeneruoti migraciją
(SQL užklausas) pagal Entity ir dabartinės duomenų bazės skirtumą.

Sugeneravimui naudojama dabartinė data ir reikšmė "migration_versions",
todėl atkrartojant skirtinga tvarka gali nesuveikti
(daroma prielaida, kad migracijos yra sukurtos pagal datą viena po kitos).

Sugeneruota naudojant scripts/backend.sh
  bin/console doctrine:migrations:diff

Duomenų bazė atnaujinta naudojant scripts/backend.sh
  bin/console doctrine:migrations:migrate

Dokumentacija:
  https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/cookbook/working-with-datetime.html
Symfony naudoja formas, kad būtų galima sugeneruoti
visus patirkinimus (validacijas) daug paprasčiau.

Twig'e visi getter'iai veikia ir be "get" dalies (getHomepage -> homepage)

Kadangi pridedame naują lauką – tai naudojame NULL senoms duomenų bazės eilutėms.
Standartiškai visi formos laukai yra privalomi – tai pakeičiame,
kad svetainės adresas būtų nebūtinas.

Dokumentacija:
  https://symfony.com/doc/current/forms.html
  https://symfony.com/doc/current/reference/forms/types/form.html#required
Vietoj nukreipimo į kitą puslapį,
iškviečiame prisjungimui naudotą klasę.

Patikrinimui:
  Po registracijos http://127.0.0.1:8000/register
  turėtų rodyti jau naujai prisjungusį naudotoją

Dokumentacija:
  https://symfony.com/doc/current/security/guard_authentication.html#manually-authenticating-a-user
Jei nenurodomas slaptažodis – forma duoda NULL reikšmę ir nesutampa tipai.
Be to praktikoje naudotojų slaptažodžiai administratoriui nėra žinomi,
ir dažniau keičiami kiti laukai.

Patirkinimui:
  Pakeisti naudotją per EasyAdmin ir išsaugoti pakeitimus neužpildžius slaptažodžio.
Kadangi pakeičiami atskirų laukų tipai/savybės,
todėl reikia išvardyti visus naudojamus laukus.

Pridedant lauką irgi verta naudoti jį labiausiai atitinkantį tipą

Dokumentacija:
  https://symfony.com/doc/current/reference/forms/types.html
  https://symfony.com/doc/master/bundles/EasyAdminBundle/book/list-search-show-configuration.html#property-types-defined-by-easyadmin
Kitus komponentus (angl. bundles) galima praplėsti ne tik perrašant jų dalis,
bet ir įterpiant savo funckionalumą tem skirtose vietose.

Pvz. EasyAdmin bundle yra palikęs vietas praplėtimui prieš ir po įrašų kūrimo/redagivmo/trynimo.
Tokiu būdu galima plabai švariai papildyti esamą funkcionalumą

Patogumui – MakerBundle turi generatorių tokiems praplėtimams

Kodas sugeneruotas su scripts/backend.sh
  bin/console make:subscriber EasyAdminSubscriber easy_admin.pre_update

Dokumentacija:
  https://symfony.com/doc/master/bundles/EasyAdminBundle/book/complex-dynamic-backends.html#customization-based-on-symfony-events
  https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
  https://symfony.com/doc/current/event_dispatcher.html
  https://github.com/EasyCorp/EasyAdminBundle/blob/master/src/Event/EasyAdminEvents.php#L48
  https://github.com/EasyCorp/EasyAdminBundle/blob/master/src/Controller/AdminControllerTrait.php#L212
Vien sukurti klasę neužtenka,
reikia pasakyti Symfony karkasui (per konfiguraciją),
kad šita klasę reikia iškviesti.

Tam naudojami Service.
Tag'ai nurodo karkasui kaip interpretuoti klasę (kada ir kokius metodus iškviesti).

"my.easy_admin.pre_edit.subscrubier" yra bet koks sugalvotas pavadinimas.
Symfony yra protingas ir su "autowire: true" pagal funkcijų argumentų tipus gali sudėlioti atitinkama reikšmes.

Patikrinimui:
  Pabandome pakeisti naudotoją per http://127.0.0.1:8000/admin/
  Turėtų parodyti, ką gauname į Event'o argumentą.

Dirbant su EventSubscriberInterface, praverčia xDebug žinios.

Dokumentacija:
  https://symfony.com/doc/current/event_dispatcher.html
  https://knpuniversity.com/screencast/fosuserbundle/customize-events
  https://github.com/EasyCorp/EasyAdminBundle/blob/master/src/Event/EasyAdminEvents.php#L48
  https://github.com/EasyCorp/EasyAdminBundle/blob/master/src/Controller/AdminControllerTrait.php#L212
  https://symfony.com/doc/current/logging.html
Event'as yra iššaukiamas prieš rašant į duomenų bazę.
Kaip argumentas paduodamas User objektas ir EntityManager.

Mes norime pakeisti datą, tik jei buvo panaudotas setPlainPassword metodas.

Glaliausiai viską gražiai atvaizduojame, pakeisdami password atvaizdavimo šabloną

Patkrinimui:
  Pakeitus slaptažodį per http://127.0.0.1:8000/admin/
  Turėtų slaptažodžio laukelyje rodyti pakeitimo datą

Dokumentacija:
  https://twig.symfony.com/doc/2.x/filters/date.html
  https://symfony.com/doc/current/event_dispatcher.html#creating-an-event-subscriber
  https://symfony.com/doc/master/bundles/EasyAdminBundle/book/complex-dynamic-backends.html#event-subscriber-example

# Conflicts:
#	src/Entity/User.php
#	src/EventSubscriber/EasyAdminSubscriber.php
aurelijusb and others added 3 commits November 19, 2019 19:18
Naudotojo modelį apsirašėme kaip Entity klasę, bet vis tiek reikia,
kad duomenų bazėje būtų atitinkamos lentelės.

Sugeneruota su scripts/backend.sh
  bin/console doctrine:migrations:diff

Duomenų bazės atnaujinimui su scripts/backend.sh
  bin/console doctrine:migrations:migrate

Patikrinimui su scripts/mysql.sh
  DESCRIBE symfony.user;

Dokumentacija:
  https://symfony.com/doc/current/bundles/SymfonyMakerBundle/index.html
  https://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html
Merge remote-tracking branch 'indre/symfony-namu-darbas-3' into indre-sf-hw3

Conflicts:
	config/packages/easy_admin.yaml
	src/Entity/User.php
	src/Form/RegistrationFormType.php
	templates/base.html.twig
	templates/registration/register.html.twig
	templates/security/profile.html.twig
@aurelijusb
Copy link
Owner Author

Realiame projekte, Merge conflicts sprendžia užduoties vykdytojas, o ne tikrintojas (reviewer).
Bet, kada informavai iš anksto ir bce8013 rodo, kad buvo padaryta – pažymio nemažinsiu.

@aurelijusb
Copy link
Owner Author

Savišvietai apie git'ą:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants