1

I have an action to make an 'autologin' based in a id that the system gets from $_SERVER['AUTH_USER']. In my business server that value is always set for authenticated user. Now, I am trying test my autologin (and so many other things that depends the autologin to work) so I need to set some user to that global (just a string).

What I tryed

$_SERVER['AUTH_USER'] = 'someUser';
$I->amOnPage('some-route'); // this page redirects to autologin action where $_SERVER is used to get the user logged.

But when the action autologin is loaded that value is no more inside $_SERVER global and my test crashes.

What I would like to know

Where or how I can set that global value so that my page could behave normally, reading the value and just going on.

I will appreciate any help. Thank you.

sdlins
  • 2,195
  • 1
  • 23
  • 31
  • Variable settings don't persist from one script to another. `$_SERVER` is filled in fresh each time a script is started by the webserver. – Barmar Jun 22 '18 at 22:14
  • Thank you. I got to workaround it but in a not a very 'elegant' way. So I will wait some more answers. – sdlins Jun 22 '18 at 22:52
  • You can post your solution as an answer. If no one posts anything better, you can accept your own answer. – Barmar Jun 22 '18 at 22:54
  • Yeah, I will wait a bit more just in case somebody has a 'right' approach, not a workaround like mine. – sdlins Jun 22 '18 at 22:56

2 Answers2

2

It looks like lack of proper abstraction. You should avoid accessing $_SERVER['AUTH_USER'] directly in your app and do it in at most in one place - in component which will provide abstraction for this. So you should probably extend yii\web\Request and add related method for $_SERVER['AUTH_USER'] abstraction:

class MyRequest extends \yii\web\Request {

    private $_myAuthUser;

    public function getMyAuthUser() {
        if ($this->_myAuthUser === null) {
            $this->_myAuthUser = $_SERVER['AUTH_USER'];
        }

        return $this->_myAuthUser;
    }

    public function setMyAuthUser($value) {
        $this->_myAuthUser = $value;
    }
}

Use new class in your config:

return [
    'id' => 'app-web',
    // ...
    'components' => [
        'request' => [
            'class' => MyRequest::class,
        ],
        // ...
    ],
];

And use abstraction in your action:

$authUser = explode('\\', Yii::$app->request->getMyAuthUser())[0];

In your tests you can set value using setter in MyRequest:

 Yii::$app->request->setMyAuthUser('domain\x12345');

Or configure this at config level:

return [
    'id' => 'app-test',
    // ...
    'components' => [
        'request' => [
            'class' => MyRequest::class,
            'myAuthUser' => 'domain\x12345',
        ],
        // ...
    ],
];

UPDATE:

According to slinstj comments, Codeception may loose state of request component, including myAuthUser value. In that case it may be a good idea to implement getMyAuthUser() and setMyAuthUser() on different component (for example Yii::$app->user) or create separate component for that:

return [
    'id' => 'app-web',
    // ...
    'components' => [
        'authRequest' => [
            'class' => MyRequest::class,
        ],
        // ...
    ],
];
rob006
  • 21,383
  • 5
  • 53
  • 74
  • Very nice approach! I will try it ! Thank you ! – sdlins Jun 23 '18 at 19:24
  • I did not forget this but had not time to test since my workaround is working and I am getting near to a deadline. – sdlins Jul 01 '18 at 20:57
  • 1
    I just had to make two adjusts: 1 - change the method name because `getAuthUser()` is already used in `\yii\web\Request` to http auth; and 2 - an adaptation because I need a default authUser to simplify tests, so I added a new public attribute so that I can change it in my configs for tests. --- Thank you so much @rob006! – sdlins Jul 11 '18 at 17:28
  • **Sorry**... It should works fine but, Yii2 Codeception Module will recreate the `request` component inside a test when we call `amOnRoute()` or similar methods so that if I set the auth user before call the route, inside the route it is not available because it is recreated. Right now I am trying `requestCleanMethod` Yii2 Module config (without success for now). – sdlins Jul 11 '18 at 18:00
  • Did you tried to add setter (like in mine example) and set property at configuration level? – rob006 Jul 11 '18 at 19:07
  • Yeah, I had tryed. But I got *resolve* this by abstract `\yii\web\User` using the adjusts that I pointed in the comments above instead of to use `\yii\web\Request` because Codeception will not touch that component (i.e., User) and it seems well related (semanthically) to that situation. Could you please update your answer? Thank you code mate! Your answer helped a lot and I will choose it after updated! – sdlins Jul 11 '18 at 19:32
  • Honestly I don't think that user component should keep this value, it clearly belongs to the request. I've done something similar some time ago and never had a problem with Codeception. – rob006 Jul 11 '18 at 19:35
  • 1
    Maybe the things have been updated on codeception since that "time ago". About `User`, I dont see any problem because the only thing I need is *to discover which user is ldap logged*... that information could come not only from `$_SERVER`, but from a ldap component, or a webservice or from any other place. I think user is a good candidate to manage the recovering of that information. – sdlins Jul 11 '18 at 19:52
  • Thank you so much for your collaboration, @rob006! – sdlins Jul 11 '18 at 21:23
0

For now, I am using a workaround because there is only one place where that variable value it is checked:

//Inside my action autologin:
$authUser = explode('\\', ($_SERVER['AUTH_USER'] ?? (YII_ENV_TEST ? 'domain\x12345' : 'domain\xInvalid')))[1];

The only relevant point here is YII_ENV_TEST that is true when testing. Using this I can set get an specific value that is enough to that simple test.

However I hope to see any other better idea here! Thanks.

sdlins
  • 2,195
  • 1
  • 23
  • 31