turk porno porno escort rokettube
Ergebnis 1 bis 8 von 8

Thema: ZF3 Auth PHPUnit StatusCode 302

  1. #1
    Neuer Benutzer
    Registriert seit
    01.09.2016
    Beiträge
    6
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Frage ZF3 Auth PHPUnit StatusCode 302

    Hallo miteinander,

    ich habe folgenden simplen Unit Test für meinen IndexController:

    PHP-Code:
    public function testIndexActionCanBeAccessed()
    {
        
    $this->dispatch('/''GET');
        
    $this->assertResponseStatusCode(200);
        
    $this->assertModuleName('main');
        
    $this->assertControllerName(IndexController::class);
        
    $this->assertControllerClass('IndexController');
        
    $this->assertMatchedRouteName('main');} 
    Um hier nicht den Status Code 302 vom Redirect (wenn der Nutzer nicht angemeldet ist, wird er auf /login weitergeleitet) beim ResponseStatusCode zu erhalten nutze ich folgendes Auth-mocking:

    PHP-Code:
    public function setUp()
    {
        
    // override default configuration values
        
    $configOverrides = [];

        
    $this->setApplicationConfig(ArrayUtils::merge(
            include 
    __DIR__ '/../../../../config/application.config.php',
            
    $configOverrides
        
    ));

        
    parent::setUp();

        
    $this->authMock();
    }

    private function 
    authMock()
    {
        
    $employeeObject = new Employee();
        
    $employeeObject->exchangeArray(['id' => 1'system_name' => 'admin''role' => 'Admin']);
        
    $mockIdentity $this->getMockBuilder(AuthenticationService::class)->disableOriginalConstructor()->getMock();
        
    $mockIdentity->expects($this->any())->method('hasIdentity')->willReturn($this->returnValue(true));
        
    $mockIdentity->expects($this->any())->method('getIdentity')->willReturn($this->returnValue($employeeObject));

        
    $this->getApplicationServiceLocator()->setAllowOverride(true);
        
    $this->getApplicationServiceLocator()->setService('AuthenticationService'$mockIdentity);
        
    $this->getApplicationServiceLocator()->setAllowOverride(false);

    Leider funktioniert das ganze nicht und ich laufe trotzdem in einen StatusCode 302 rein.

    Kann es damit etwas zu tun haben, wie ich in meiner Module.php mein Bootstrapping aufbaue?

    PHP-Code:
    public function onBootstrap(MvcEvent $mvcEvent)
    {
        
    $this->bootstrapSession($mvcEvent);

        
    $mvcEvent->getApplication()->getEventManager()->attach(MvcEvent::EVENT_DISPATCH, [$this'loadServices'], 10001);
    }

    public function 
    loadServices(MvcEvent $mvcEvent)
    {
        
    $this->auth $mvcEvent->getApplication()->getServiceManager()->get(AuthenticationService::class);
        
    $aclService $mvcEvent->getApplication()->getServiceManager()->get(AclService::class);
        
    $this->acl $aclService->init();
        
    $mvcEvent->getViewModel()->setVariable('acl'$this->acl);

        
    // store user and role in global viewmodel
        
    if ($this->auth->hasIdentity()) {
            
    $mvcEvent->getViewModel()->setVariable('authIdentity'$this->auth->getIdentity());
            
    $mvcEvent->getViewModel()->setVariable('acl'$this->acl);
            
    $this->checkPermission($mvcEvent);
        } else {
            
    $this->authRedirect($mvcEvent);
        }
    }

    private function 
    authRedirect(MvcEvent $mvcEvent)
    {
        
    $route $mvcEvent->getRouteMatch()->getMatchedRouteName();

        
    // if tables dropped redirect to migrate section
        
    if (empty($this->acl->getRoles())) {
            if (
    $route === 'migrate') {
                return;
            }

            
    $response $mvcEvent->getResponse();
            
    $response->setStatusCode(302);
            
    $response->getHeaders()->addHeaderLine('Location''/migrate');
            
    $mvcEvent->stopPropagation();

            return;
        }

        
    // no user force to login
        
    if ($route === 'login') {
            return;
        }

        
    $url $mvcEvent->getRouter()->assemble([], ['name' => 'login']);

        
    $response $mvcEvent->getResponse();
        
    $response->setStatusCode(302);
        
    $response->getHeaders()->clearHeaders()->addHeaderLine('Location'$url);
        
    $mvcEvent->stopPropagation();

        return;
    }

    private function 
    checkPermission(MvcEvent $mvcEvent)
    {
        
    $route $mvcEvent->getRouteMatch()->getMatchedRouteName();

        
    $userRole $this->auth->getIdentity()['role'];

        
    // resource does not exist or user has not sufficient rights
        
    if (!$this->acl->hasResource($route) ||
            !(
    $this->acl->isAllowed($userRole$routeAccessRight::READ) || $this->acl->isAllowed($userRole$routeAccessRight::WRITE))
        ) {
            
    $response $mvcEvent->getResponse();
            
    $response->setStatusCode(404);
            
    $mvcEvent->stopPropagation();

            return;
        }


    So wie ich die Doc verstanden habe, wird das EVENT_DISPATCH geladen, wenn ein Controller/Action aufgerufen wird.
    Genau in diesem Moment möchte ich doch prüfen ob ein Nutzer angemeldet ist und ihn ansonsten zur Login Seite weiterleiten.

    Für den Unittest will ich das umgehen und dem Test "vorgaukeln", dass wir einen angemeldeten Nutzer haben (das ist doch der Sinn vom Unittest, dass man es isoliert?)

    Für Denkanstöße bin ich sehr dankbar.

  2. #2
    Super-Moderator Avatar von Kaiuwe
    Registriert seit
    30.12.2006
    Beiträge
    5.520
    Thanks
    4
    Thanked 356 Times in 287 Posts

    Standard

    Der Anfang sieht soweit gut aus.
    Zitat Zitat von Rogaa Beitrag anzeigen
    Für den Unittest will ich das umgehen und dem Test "vorgaukeln", dass wir einen angemeldeten Nutzer haben…
    Einfach mal mit „Zend\Log“ prüfen, ob deinen Annahmen an den verschiedenen Stellen stimmen. Zum Beispiel bei „hasIdentity()“ und „getIdentity()“ in der Modul-Klasse.



    Ein paar Hinweise noch:
    Zitat Zitat von Rogaa Beitrag anzeigen
    PHP-Code:
    $this->dispatch('/''GET'); 
    „GET“ musst du nicht angeben, wenn du den „Request“ nicht anderweitig geändert hast und dieser HTTP ist, dann wird der Standardwert verwendet und dies ist „GET“.

    Zitat Zitat von Rogaa Beitrag anzeigen
    PHP-Code:
    $mockIdentity->expects($this->any())->method('hasIdentity')->willReturn($this->returnValue(true));
    $mockIdentity->expects($this->any())->method('getIdentity')->willReturn($this->returnValue($employeeObject)); 
    „returnValue“ weglassen, denn das erledigt bereits „willReturn“.

    Zitat Zitat von Rogaa Beitrag anzeigen
    PHP-Code:
    public function loadServices(MvcEvent $mvcEvent)
    {
    }

    private function 
    authRedirect(MvcEvent $mvcEvent)
    {
    }

    private function 
    checkPermission(MvcEvent $mvcEvent)
    {

    Alles in eine eigene Klasse („Listener“) auslagern! Halte die Modul-Klasse so schlank wie möglich, denn dann bist du unabhängiger vom Framework und das Testen fällt auch leichter.

    Zitat Zitat von Rogaa Beitrag anzeigen
    PHP-Code:
    $mvcEvent->getViewModel()->setVariable('authIdentity'$this->auth->getIdentity()); 
    Warum verwendest du nicht den „View“-Helfer „Identity“, dann würde diese Weitergabe nicht benötigt?! Ausserdem wird die „View“-Schicht bereits gefüllt, obwohl nicht mal klar ist, ob der aktuelle Benutzer die Seite aufrufen darf und ob die Seite selbst überhaupt gerendert wird. Verschiebe solche Sachen eher auf das Ereignis „MvcEvent::EVENT_RENDER“.
    Zum Zend Framework stehen jedem folgende Quellen zum Nachschlagen zur Verfügung:

  3. #3
    Neuer Benutzer
    Registriert seit
    01.09.2016
    Beiträge
    6
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Standard

    Zitat Zitat von Kaiuwe Beitrag anzeigen
    Der Anfang sieht soweit gut aus.

    Einfach mal mit „Zend\Log“ prüfen, ob deinen Annahmen an den verschiedenen Stellen stimmen. Zum Beispiel bei „hasIdentity()“ und „getIdentity()“ in der Modul-Klasse.



    Ein paar Hinweise noch:

    „GET“ musst du nicht angeben, wenn du den „Request“ nicht anderweitig geändert hast und dieser HTTP ist, dann wird der Standardwert verwendet und dies ist „GET“.



    „returnValue“ weglassen, denn das erledigt bereits „willReturn“.



    Alles in eine eigene Klasse („Listener“) auslagern! Halte die Modul-Klasse so schlank wie möglich, denn dann bist du unabhängiger vom Framework und das Testen fällt auch leichter.


    Warum verwendest du nicht den „View“-Helfer „Identity“, dann würde diese Weitergabe nicht benötigt?! Ausserdem wird die „View“-Schicht bereits gefüllt, obwohl nicht mal klar ist, ob der aktuelle Benutzer die Seite aufrufen darf und ob die Seite selbst überhaupt gerendert wird. Verschiebe solche Sachen eher auf das Ereignis „MvcEvent::EVENT_RENDER“.
    Danke Dir für die Hinweise/Tipps.

    Das mit dem ViewHelper wusste ich tatsächlich nicht, auch wenn mir gerade selbst nicht ganz klar ist, warum ich bei der Suche "wie ich sowas im View" abrufen kann, nicht darauf gekommen bin.. eifok

    Ich werde mich jetzt erstmal um den Custom Listener kümmern, um dann das Unit Test Thema zu debuggen. tbc.

    Sollte dann ein Session Handling ebenfalls als Listener eingebunden werden, da sowas ja auch beim Bootstrap passiert?

  4. #4
    Neuer Benutzer
    Registriert seit
    01.09.2016
    Beiträge
    6
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Standard

    Hm also so ganz ist der Knoten nicht geplatzt...

    Ich habe nun einen AuthListenerer geschrieben, um die Logik von meiner Module.php auszulagern.

    PHP-Code:
    <?php

    namespace Main\Listener;

    use 
    Acl\Service\AclService;
    use 
    Zend\Authentication\AuthenticationService;
    use 
    Zend\EventManager\EventManagerInterface;
    use 
    Zend\EventManager\ListenerAggregateInterface;
    use 
    Zend\EventManager\ListenerAggregateTrait;
    use 
    Zend\Mvc\MvcEvent;
    use 
    Zend\Permissions\Acl\Acl;

    class 
    AuthenticationListener implements ListenerAggregateInterface
    {
        use 
    ListenerAggregateTrait;


        private 
    $auth;


        private 
    $acl;

     
        public function 
    __construct(AuthenticationService $authenticationServiceAclService $aclService)
        {
            
    $this->auth $authenticationService;
            
    $this->acl $aclService->init();
        }


        public function 
    attach(EventManagerInterface $events$priority 1)
        {
            
    $this->listeners[] = $events->attach(MvcEvent::EVENT_DISPATCH, [$this'checkAuthentication']);
        }


        public function 
    checkAuthentication($event)
        {
    //        $event->getViewModel()->setVariable('acl', $this->acl);

            
    if ($this->auth->hasIdentity()) {
                
    $this->checkPermission($event);
            } else {
                
    $this->authRedirect($event);
            }
        }


        private function 
    authRedirect(MvcEvent $event)
        {
            
    $route $event->getRouteMatch()->getMatchedRouteName();

            
    // if tables dropped redirect to migrate section
            
    if (empty($this->acl->getRoles())) {
                if (
    $route === 'migrate') {
                    return;
                }

                
    $response $event->getResponse();
                
    $response->setStatusCode(302);
                
    $response->getHeaders()->addHeaderLine('Location''/migrate');
                
    $event->stopPropagation();

                return;
            }

            
    // no user force to login
            
    if ($route === 'login') {
                return;
            }

            
    $url $event->getRouter()->assemble([], ['name' => 'login']);

            
    $response $event->getResponse();
            
    $response->setStatusCode(302);
            
    $response->getHeaders()->clearHeaders()->addHeaderLine('Location'$url);
            
    $event->stopPropagation();

            return;
        }


        private function 
    checkPermission(MvcEvent $event)
        {
            
    $route $event->getRouteMatch()->getMatchedRouteName();

            
    $userRole $this->auth->getIdentity()['role'];

            
    // resource does not exist or user has not sufficient rights
            
    if (!$this->acl->hasResource($route) ||
                !(
    $this->acl->isAllowed($userRole$routeAccessRight::READ) || $this->acl->isAllowed($userRole$routeAccessRight::WRITE))
            ) {
                
    //TODO: implement no permission page
                
    $response $event->getResponse();
                
    $response->setStatusCode(404);
                
    $event->stopPropagation();

                return;
            }
        }
    }
    In der onBootstrap selber steht dann nur noch:

    PHP-Code:
    /** @var AuthenticationListener $authListener */
    $authListener $event->getApplication()->getServiceManager()->get(AuthenticationListener::class);
    $authListener->attach($event->getApplication()->getEventManager()); 
    In meinem Unit-Test erkennt er aber trotzdem mein auth mock nicht, sprich in meinem AuthenticationListener ist während des Unit-Tests hasIdentity === false, ergo redirect mit 302.

    Wenn ich den EVENT_DISPATCH auf EVENT_BOOTSTRAP stelle, ist der Test erfolgreich, da er erst gar nicht den Listener aufruft.

  5. #5
    Super-Moderator Avatar von Kaiuwe
    Registriert seit
    30.12.2006
    Beiträge
    5.520
    Thanks
    4
    Thanked 356 Times in 287 Posts

    Standard

    Zitat Zitat von Rogaa Beitrag anzeigen
    In meinem Unit-Test erkennt er aber trotzdem mein auth mock nicht, sprich in meinem AuthenticationListener ist während des Unit-Tests hasIdentity === false, ergo redirect mit 302.
    Ich denke, du setzt den Service falsch:
    Zitat Zitat von Rogaa
    PHP-Code:
    $this->getApplicationServiceLocator()->setService('AuthenticationService'$mockIdentity); 
    vs.
    Zitat Zitat von Rogaa
    PHP-Code:
    $authListener $event->getApplication()->getServiceManager()->get(AuthenticationListener::class); 
    Zum Zend Framework stehen jedem folgende Quellen zum Nachschlagen zur Verfügung:

  6. #6
    Neuer Benutzer
    Registriert seit
    01.09.2016
    Beiträge
    6
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Standard

    Also in meiner module.config.php setze ich den AuthenticationService im ServiceManager:

    PHP-Code:
    'service_manager' => [
            
    'factories' => [
                \
    Zend\Authentication\AuthenticationService::class => \Authentication\Factory\AuthenticationServiceFactory::class,
                
    AuthenticationListener::class => AuthenticationListenerFactory::class,
            ],
        ], 
    Der AuthenticationListener wird über eine Factory instanziert, da im Konstruktor sowohl die ACL als auch der Zend\AuthenticationService mitgegeben wird.

    In der Module-Klasse muss ich im onBootstrap meinen Listener dem Event bekannt geben.

    Mein Listener selbst teilt dem EventManager mit, dass er beim Dispatch meine checkAuthentication ausführen soll, der dann schaut ob der Zend\AuthenticationService eine Identität hat und entsprechendes redirecting ausführt.

    Wenn ich nun im Unittest der Applikation via setService den Mock vom Zend\AuthenticationService übergebe, sollte er doch bei der Abfrage $this->auth->hasIdentity() true zurückgeben?

    Dies tut er aber nicht und das verstehe ich schlicht nicht.
    Geändert von Rogaa (14.09.2016 um 16:44 Uhr)

  7. #7
    Super-Moderator Avatar von Kaiuwe
    Registriert seit
    30.12.2006
    Beiträge
    5.520
    Thanks
    4
    Thanked 356 Times in 287 Posts

    Standard

    Zitat Zitat von Rogaa Beitrag anzeigen
    Dies tut er aber nicht und das verstehe ich schlicht nicht.
    Schau mal genau die Schreibweisen von deinem „AuthenticationService“ bei „setService“ und „get“ an. (Siehe in den Zitaten von deinen Code-Auszüge in meinem vorherigen Beitrag!)
    Zum Zend Framework stehen jedem folgende Quellen zum Nachschlagen zur Verfügung:

  8. #8
    Neuer Benutzer
    Registriert seit
    01.09.2016
    Beiträge
    6
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Daumen hoch Lösung

    Ich habe es gelöst mit folgenden Anpassungen:

    Module.php:
    PHP-Code:
    public function onBootstrap(MvcEvent $event)
    {
        
    $authListener = new AuthenticationListener();
        
    $authListener->attach($event->getApplication()->getEventManager());

    Das Bootstrapping findet statt, bevor PHPUnit die Mocks setzen kann, daher wurde mein AuthenticationService-Mock nie verwendet.

    Daher konnte ich die Factory für meinen AuthenticationListener weglassen und rufe die Services erst in meiner Listener-Methode via ServiceManager auf.

    PHP-Code:
    public function checkAuthentication($event)
    {
        
    $this->auth $event->getApplication()->getServiceManager()->get(AuthenticationService::class);
        
    $aclService $event->getApplication()->getServiceManager()->get(AclService::class);
        
    $this->acl $aclService->init();

        
    $event->getViewModel()->setVariable('acl'$this->acl);
        if (
    $this->auth->hasIdentity()) {
            
    $this->checkPermission($event);
        } else {
            
    $this->authRedirect($event);
        }

    Ich werde mir dann wohl noch einen ViewHelper für Acl schreiben.

    Zum Abschluss noch der Mock im Unittest:

    PHP-Code:
    private function authMock()
    {
        
    $mockAuth $this->getMockBuilder(AuthenticationService::class)->disableOriginalConstructor()->getMock();
        
    $mockAuth->expects($this->any())->method('hasIdentity')->willReturn(true);
        
    $mockAuth->expects($this->any())->method('getIdentity')->willReturn(['id' => 1'systemName' => 'admin''role' => 'Admin']);

        
    $mockAcl $this->getMockBuilder(Acl::class)->disableOriginalConstructor()->getMock();

        
    /** @var ViewModel $viewModel */
        
    $viewModel $this->getApplicationServiceLocator()->get('ViewManager')->getViewModel();
        
    $viewModel->setVariable('acl'$mockAcl);

        
    $this->getApplicationServiceLocator()->setAllowOverride(true);
        
    $this->getApplicationServiceLocator()->setService(AuthenticationService::class, $mockAuth);
        
    $this->getApplicationServiceLocator()->setAllowOverride(false);

    Geändert von Rogaa (15.09.2016 um 12:49 Uhr) Grund: attach vereinfacht

Ähnliche Themen

  1. PHPUnit 4.x - Aufrufen?
    Von rainman1983 im Forum Testing & Deployment
    Antworten: 1
    Letzter Beitrag: 06.05.2014, 21:43
  2. StatusCode bei Ajax-Validierung verändern
    Von stefan1989 im Forum ZF2 Einsteigerfragen
    Antworten: 0
    Letzter Beitrag: 15.08.2013, 13:31
  3. Stilfrage: Statuscode 401 vs 403
    Von KingCrunch im Forum PHP X-Talk
    Antworten: 20
    Letzter Beitrag: 27.07.2009, 19:43
  4. PHPUnit shell_exec
    Von ice-breaker im Forum Testing & Deployment
    Antworten: 4
    Letzter Beitrag: 31.07.2008, 20:13
  5. PHPUNIT install
    Von TeeJay im Forum Testing & Deployment
    Antworten: 13
    Letzter Beitrag: 07.07.2008, 13:06

Stichworte

Lesezeichen

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •