Customizing AuthenticationService to ZfcUser - php

I have a custom implementation of AuthenticationService that I would like to use in ZfcUser Module but I am able to set this class into the module. The implementation seems to be fixed.
vendor\zf-commons\zfc-user\Module.php
'zfcuser_auth_service' => function ($sm) {
return new \Zend\Authentication\AuthenticationService(
$sm->get('ZfcUser\Authentication\Storage\Db'),
$sm->get('ZfcUser\Authentication\Adapter\AdapterChain')
);
}
The original requirement is to keep a unique active session per user that is implemented in my CustomAuthenticationService. Any ideas to solve this problem?

Your use case is unclear; normally the authentication adapter is the class that you would normally customise, rather than the actual authentication service.
Nevertheless, you can override the default service with your own providing you register the service with the same name and the module is loaded after the ZfcUser module.
Say your custom authentication service is in your own Auth namespace/module, with the class Auth\Service\CustomAuthenticationService.
Register the service in Auth\Module.php (or depending on the type of factory the module.config.php of that module).
class Module
{
public function getServiceConfig()
{
return [
'aliases' => [
'MyAuthenticationService' => 'zfcuser_auth_service',
],
'factories' => [
'zfcuser_auth_service' => function($sm) {
return new \Auth\Service\CustomAuthenticationService(
$sm->get('ZfcUser\Authentication\Storage\Db'),
$sm->get('ZfcUser\Authentication\Adapter\AdapterChain')
);
},
],
];
}
}
Lastly, ensure the module is loaded after ZfcUser in application.config.php.
return [
'modules' => [
//...
'ZfcUser',
'Auth',
// ...
],
];

The auth adapter is just trigged when the login action is performed. To handle each request you can override the storage adapter that allow validate the identify in every request. In your configuration file add a 'ZfcUser\Authentication\Storage\Db' attribute poiting to your custom storage class.
'service_manager' => array(
'invokables' => array(
'ZfcUser\Authentication\Storage\Db' => 'MyCustom\Authentication\Storage'),
...

Related

In Laravel can I set a default context for the Log facade

I'm using the Log:: facade a lot and have a helper class called LogHelper which provide me with a static method LogHelper::context() which include many key values I need to track the requests. But having to type it every time for each usage make it error prune and fill not so efficient.
I'm looking for a way to inject the values by default, and allow me to overwrite them if needed specifically.
At the moment this is how I use it,
Log::debug('Request Started', LogHelper::context());
what I'm looking for is to inject the context by default
Log::debug('Request Started');
and have the option to overwrite it, if need it:
Log::debug('Request Started', ['more' => 'context'] + LogHelper::context());
PS, the LogHelper::context() return a simple key => value array which include some staff i need to debug requests, and the reason it do not use the values directly in the message is because i log to graylog as structured data, and this way i can filter by any key.
I have solved this issue by using the tap functionality and $logger->withContext() (note: the latter was added in Laravel 8.49).
You want to create a new class which contains your context logic. I've created an extra Logging folder in app/ in which my logging customizations sit.
app/Logging/WithAuthContext.php:
<?php
namespace App\Logging;
use Illuminate\Log\Logger;
class WithAuthContext
{
public function __invoke(Logger $logger)
{
$logger->withContext([
'ip' => request()?->ip(),
'ua' => request()?->userAgent(),
]);
}
}
Depending on which logging channel(s) you use, you will have to add the class to each one you want to add context to. So in app/config/logging.php:
<?php
use App\Logging\WithAuthContext;
use Monolog\Handler\NullHandler;
use Monolog\Handler\StreamHandler;
use Monolog\Handler\SyslogUdpHandler;
return [
// ...
'channels' => [
// ...
'single' => [
'driver' => 'single',
'path' => storage_path('logs/laravel.log'),
'level' => env('LOG_LEVEL', 'debug'),
'tap' => [WithAuthContext::class],
],
// ...
],
];
There is a way, but it is not pretty. You can create a custom monolog logger driver. The process is described at https://laravel.com/docs/8.x/logging#creating-monolog-handler-channels.
Here's a possible implementation:
class ContextEnrichingLogger extends \Monolog\Handler\AbstractHandler {
private $logger;
public function __construct($level = Monolog\Logger::DEBUG, bool $bubble = true, $underlyingLogger = 'single') {
$this->logger = Log::driver($underlyingLogger);
}
public function handle(array $record) {
$record['context'] += LogHelper::context();
return $this->logger->handle($record);
}
}
Then register this as a custom logger in your config/logging.php:
return [
'default' => 'enriched',
//...
'channels' => [
// ...
'enriched' => [
'driver' => 'monolog',
'handler' => ContextEnrichingLogger::class,
'level' => env('APP_LOG_LEVEL', 'debug'),
"with" => [
"underlyingLogger" => env('LOG_CHANNEL', 'single')
]
]
]
];
I haven't tested this particular one but this is how I've defined other custom loggers.
Note, this is probably also achievable via a custom formatter though I think it's probably the same trouble.

Zend2 Dependency injection factory to service

Im trying to implement Dependency injection Using the Zend2 service manager. I want to inject a PDO instance into Service (Im not using the Zend Db).
Im following the tutorial here: https://framework.zend.com/manual/2.4/en/in-depth-guide/services-and-servicemanager.html
I have it working for another service, but the when injecting the PDO instance Im getting this error:
Catchable fatal error: Argument 1 passed to Application\Service\DataService::__construct() must be an instance of Application\Service\DbConnectorService, none given, called in /srv/www/shared-apps/approot/apps-dev/ktrist/SBSDash/vendor/zendframework/zendframework/library/Zend/ServiceManager/ServiceManager.php on line 1077 and defined in /srv/www/shared-apps/approot/apps-dev/ktrist/SBSDash/module/Application/src/Application/Service/DataService.php on line 24
From the tutorial this seems to be related to my invokeables in the module.config. But I cannot work out what the issue is.
Any advice is appreciated.
Here is my code:
DataService:
class DataService {
protected $dbConnectorService;
public function __construct(DbConnectorService $dbConnectorService) {
$this->dbConnectorService = $dbConnectorService;
}
......
DataServiceFactory:
namespace Application\Factory;
use Application\Service\DataService;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class DataServiceFactory implements FactoryInterface {
function createService(ServiceLocatorInterface $serviceLocator) {
$realServiceLocator = $serviceLocator->getServiceLocator();
$dbService = $realServiceLocator->get('Application\Service\DbConnectorService');
return new DataService($dbService);
}
}
Module.Config:
'controllers' => array(
'factories' => array(
'Application\Controller\Index' => 'Application\Factory\IndexControllerFactory',
'Application\Service\DataService' => 'Application\Factory\DataServiceFactory',
)
),
'service_manager' => array(
'invokables' => array(
'Application\Service\DataServiceInterface' => 'Application\Service\DataService',
'Application\Service\DbConnectorService' => 'Application\Service\DbConnectorService',
)
),
You are trying to create the service as an 'invokable' class. ZF2 will treat this service as a class without dependencies (and not create it using the factory).
You should update your service configuration to register under the 'factories' key, pointing to the factory class name.
'service_manager' => [
'invokables' => [
'Application\\Service\\DbConnectorService'
=> 'Application\\Service\\DbConnectorService',
],
'factories' => [
'Application\\Service\\DataServiceInterface'
=> 'Application\\Factory\\DataServiceFactory',
],
],
You will need to make the same change for DbConnectorService if that also has a factory.

Yii2 REST API as Module route configuration

I have an existing Yii2 application and have been trying to implement a REST API as an additional module (Maybe a module isn't the correct way to go about this?) But I'm having some trouble configuring the route structures. It doesn't quite work and doesn't follow the expected results, based of the following guide.
I've built an additional module that looks like this:
module
api
controllers
UserController.php
Module.php
UserController.php
<?php
namespace app\modules\api\controllers;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'app\models\User';
}
Module.php
<?php
namespace app\modules\api;
/**
* onco module definition class
*/
class Module extends \yii\base\Module
{
public $defaultController = 'user';
/**
* #inheritdoc
*/
public $controllerNamespace = 'app\modules\api\controllers';
/**
* #inheritdoc
*/
public function init()
{
parent::init();
// custom initialization code goes here
}
}
In my config file I have the added following:
'request' => [
...
'parsers' => [
'application/json' => 'yii\web\JsonParser',
]
],
...
'urlManager' => [
'enablePrettyUrl' => true,
'showScriptName' => false,
'enableStrictParsing' => false, // have tried as true also
'rules' => [
...
['class' => 'yii\rest\UrlRule', 'controller' => '\app\modules\api\controllers\user'],
],
],
...
'modules' => [
...
'api' => [ // module for RESTful API
'class' => 'app\modules\api\Module',
],
]
When I run the following urls through postman I get the following:
http://localhost/site1/web/api/users -> 404
http://localhost/site1/web/api/users/index -> 404
http://localhost/site1/web/api/user/index -> returns json repsonse
http://localhost/site1/web/api/user/2 -> 404
I'm unsure as to why the predicted routes of noted in the docs as:
Trying it Out With the above minimal amount of effort, you have
already finished your task of creating the RESTful APIs for accessing
the user data. The APIs you have created include:
GET /users: list all users page by page;
HEAD /users: show the overview information of user listing;
POST /users: create a new user;
GET /users/123: return the details of the user 123;
HEAD /users/123: show the overview information of user 123;
PATCH /users/123 and PUT /users/123: update the user 123;
DELETE /users/123: delete the user 123;
OPTIONS /users: show the supported verbs regarding endpoint /users;
OPTIONS /users/123: show the supported verbs regarding endpoint /users/123
What have I likely done wrong in this setup? Is there a better way to implement an API into an existing website, whilst maintaining DRY practices?
try this:
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => ['api/user'],
]
]
],
...
'modules' => [
...
'api' => [
'basePath' => '#app/modules/api',
'class' => 'app\modules\api\Module',
],
]
Also be sure to implement prettyUrl's related server server configs.

Zend framework 2 restful api not working for me

I am using zf2 restful api in my web services.
This is my code -
module.config.php -
'login' => array(
'type' => 'segment',
'options' => array(
'route' => '/ws/login[/:id]',
'defaults' => array(
'__NAMESPACE__' => 'Webservices\Controller',
'controller' => 'Login',
),
),
),
This is my controller -
<?php
namespace Webservices\Controller;
use Zend\Mvc\Controller\AbstractRestfulController;
use Zend\View\Model\JsonModel;
class LoginController extends AbstractRestfulController {
public function getList() {
return new JsonModel(array(
'data' => '',
));
}
/**
* params time, language
* listing category details
* return category details
*/
public function get($id) {
return new JsonModel(array(
'data' => '',
));
}
public function create($requestData) {
print_r($requestData);
die();
}
}
When I post some data into this controller then it redirects into create function.
But requestData variable is NULL.
Raw data method is used for posting. This is my request data
{"reqType":"2","verNo":"test","userName":"test==","deviceIdentifier":"DKZWcdvB50+test+test","password":"test=="}
For some technical reasons I am still using php 5.3.3 and zf2.0.
Sorry I don't have privileges to do comment your question so I just write this as an answer even if this is not.
I'm not sure if your routing is well configured in module.config.php.
There is no action defined in the routing.
Also in your controller the name of the create function shouldn't be createAction ??
I haven't tried but I guess if we're talking about actions in the controller then you're not able to use the parameter list of the action function (only if you defined them in your routing properly). Use instead the following: $this->params()->fromPost(parameterName)
If I misunderstood anything please let me know but I think this problem is not actually related with the zf2 restful API instead how to do routing in zf2 and how to get the parameters inside the actions.

ZF2 Routing configuration via RouteProviderInterface

I have been trying to configure our Module.php to use the Module Manager Listeners for configuration (i.e interfaces that are available under Zend\ModuleManager\Feature\*). Specifically, I want to be able to configure the routes of my module outside of the main module.config.php. I have not been able to find any actual examples of this.
What I have found, if I have read the documentation correctly, is that the method getRouteConfig() should merge in my routes into the array provided by getConfig()?
Module.php
class Module implements Feature\RouteProviderInterface
{
//...
public function getRouteConfig()
{
return include __DIR__ . '/config/route.config.php';
}
//...
}
/config/route.config.php
return array(
'route_manager' => array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
),
),
),
);
I can see the array returned via getRouteConfig() so I know the method is being called correctly.
Perhaps I am misunderstanding the purpose of the above interface, or I have not provided the correct "key" (route_manager) for this to be merged correctly, as I'm getting 404 for my routes.
Any help would be appreciated!
I haven't done this in the way you mentioned yet, but the key route_manager is not required within the getRouteConfig() Method.
This is due to the fact that all of the get{$specificManager}Config()-Methods are called directly from their respective Manager-Classes. Therefore the initial key is not required. Using another terminology, when using getRouteConfig() you are already in the scope of route_manager. Same as when you use getServiceConfig() you're already in the scope of service_manager. However getConfig() is within the application-scope and therefore accessing configuration of application-parts, you need to address tose specificaly.
One thing to note is: the configuration of getConfig() can be cached to increase performance, whereas all the other get{$specificManager}Config() methods are not. Especially in the case of the RouteConfiguration I'd highly suggest to use the getConfig()-Method for your RouteConfig.
If you really need to separate the configuration, then I'd suggest the way that #Hendriq displayed for you.
Well I have it working but I only use the getConfig(). What is do is I use an array_merge in the getConfig().
public function getConfig()
{
return array_merge(
require_once 'path_to_config/module.config.php',
require_once 'path_to_config/routes.config.php'
);
}
My router.config.php looks then like:
return [
'router' => [
'routes' => [
// routes
]
]
];
This way I also got some other config files seperated (ACL).
Edit
Thanks to the article Understanding ZF2-Configuration, I got an idea. I think your array should not be:
return array(
'route_manager' => array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
)
)
)
);
but rather be
return array(
'router' => array (
'routes' => array(
//.. routes that were working correctly when added to module.config.php
),
),
);
The getRouteConfig is similar to the other providers it is there so you're able to create some custom routes. I guess what you're trying to do is most appropiate through hendriq's method.
An example of getRouteConfigcan be found at http://zf2cheatsheet.com/
public function getRouteConfig()
{
return array(
'factories' => array(
'pageRoute' => function ($routePluginManager) {
$locator = $routePluginManager->getServiceLocator();
$params = array('defaults' => array('controller' => 'routeTest','action' => 'page','id' => 'pages'));
$route = Route\PageRoute::factory($params);
$route->setServiceManager($locator);
return $route;
},
),
);
}
In our Module\Route namespace we create the class PageRoute which implements Zend\Mvc\Http\RouteInterface and, in our specific case for the example, Zend\ServiceManager\ServiceManagerAwareInterface. Now just implement the functions of the interface... In the sample he uses Doctrine to load the pages from the database.
Finally we can add our new custom route to our module.config.php so it can be used:
'page' => array(
'type' => 'pageRoute',
),
As you can see in this last step we go back to Hendriq's solution as the intended use is not to load the routes into the router, but creating custom routes.
Hope this helps

Categories