zend-authentication - setting identity to custom object with rbac roles loaded - zend-framework2

In a ZF2 project i am using the AuthenticationService to validate a users log in credentials. This is working fine, except it only stores in the session a string containing the users name.
What i would like would be for subsequent calls to AuthenticationService::getIdentity to return a custom Identity object, that is populated with the users database id, roles and permissions (popualted from an RBAC service), so that the object in the session is a bit more useful.
I am able to create this object, but am unsure of the best way to keep it in the session; ideally i would like to override the entry with the key Zend_Auth, but this does not seem to be working.
My code so far:
<?php
namespace Authentication\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
use Zend\Authentication\AuthenticationService;
use Authentication\Form\Login\LoginForm;
use Zend\Form\Form;
use Authentication\Model\Identity\AuthenticatedIdentity;
class AuthenticationController extends AbstractActionController
{
/**
*
* #var AuthenticationService
*/
protected $authenticationService;
/**
*
* #var LoginForm
*/
protected $loginForm;
/**
*
* #param AuthenticationService $authenticationService
* #param LoginForm $loginForm
*/
public function __construct(AuthenticationService $authenticationService, LoginForm $loginForm){
$this->authenticationService = $authenticationService;
$this->loginForm = $loginForm;
}
public function indexAction(){
$form = $this->loginForm;
$viewModel = new ViewModel();
$viewModel->setVariables([
'loginForm' => $form
]);
if($this->getRequest()->isPost() === false){
return $viewModel;
}
$form->setData($this->getRequest()->getPost());
if($form->isValid() === false){
return $viewModel;
}
$data = $form->getData();
$authenticationAdapter = $this->authenticationService->getAdapter();
$authenticationAdapter->setIdentity($data['credentials']['username'])
->setCredential($data['credentials']['password']);
$authenticationResult = $this->authenticationService->authenticate($authenticationAdapter);
if($authenticationResult->isValid() === false){
$viewModel->setVariable('validCredentials', false);
return $viewModel;
}
/**
* Create a user model and save it to the session.
*/
$authenticationResultRow = $authenticationAdapter->getResultRowObject(null, ['password']);
$permissions = $this->rbacService->getPermissionsForUser($authenticationResultRow->user_id);
$roles = $this->rbacService->getRolesForUser($authenticationResultRow->user_id);
$identity = new AuthenticatedIdentity(
$authenticationResult->getIdentity(),
'admin',
$permissions,
$roles
);
$identity->setUserId($authenticationResultRow->user_id);
//how to store this Identity object in session so AuthenticationService will return it?
return $this->redirect()->toRoute('dashboard');
}
}

Check out https://github.com/zendframework/zend-authentication/blob/master/src/AuthenticationService.php#L75 and https://github.com/zendframework/zend-authentication/blob/master/src/Storage/StorageInterface.php
You can write the AuthenticatedIdentity object directly to the storage like so:
$this->authenticationService->getStorage()->write($identity);
However, I would advice against doing so because:
If the user's permissions/roles change during the session he/she would have to log out and back in to see any changes which is not very user-friendly.
Your AuthenticatedIdentity object and all objects it contains need to be serializable, which can become problematic to maintain.
I would (and do) fetch the user object and/or roles when needed, either from DB or some form of cache but don't store it in the session.

Related

Problem to get logged in with Laravel and docker on production

I have a Laravel application and I am currently working on integrating docker. The app runs perfectly locally but on production I simply can't log in. Every time I submit the log in form I get redirected to the log in form without any message of success nor failure.
I have realized that the request it reaches the controller it should but it does not reach the action. I put a die command in the constructor and it worked but it didn't when I did the same in the first line of the controller's action.
<?php
namespace App\Http\Controllers\Auth;
use App\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Monolog\Logger;
use Validator;
use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
class AuthController extends Controller
{
/*
|---------------------------------------------------------------------
| Registration & Login Controller
|---------------------------------------------------------------------
|
| This controller handles the registration of new users, as well as the
| authentication of existing users. By default, this controller uses
| a simple trait to add these behaviors. Why don't you explore it?
|
*/
use AuthenticatesUsers;
/**
* Where to redirect users after login / registration.
*
* #var string
*/
protected $redirectTo = '/';
/**
* Create a new authentication controller instance.
*
* #return void
*/
public function __construct()
{
// die('something') works here
$this->middleware('guest', ['except' => ['logout', 'register', 'showRegistrationForm']]);
// die('something') works here too
}
/**
* Get a validator for an incoming registration request.
*
* #param array $data
* #return \Illuminate\Contracts\Validation\Validator
*/
protected function validator(array $data)
{
return Validator::make($data, [
'rut' => 'required|max:30',
'apellidos' => 'required|max:255',
'name' => 'required|max:255',
'email' => 'required|email|max:255|unique:users',
'password' => 'required|confirmed|min:6',
]);
}
/**
* Create a new user instance after a valid registration.
*
* #param array $data
* #return User
*/
protected function create(array $data)
{
}
public function login(Request $request)
{
//die('something'); doesn't work here
$this->validateLogin($request);
// If the class is using the ThrottlesLogins trait, we can automatically throttle
// the login attempts for this application. We'll key this by the username and
// the IP address of the client making these requests into this application.
if ($this->hasTooManyLoginAttempts($request)) {
$this->fireLockoutEvent($request);
return $this->sendLockoutResponse($request);
}
if ($this->attemptLogin($request)) {
return $this->sendLoginResponse($request);
}
// If the login attempt was unsuccessful we will increment the number of attempts
// to login and redirect the user back to the login form. Of course, when this
// user surpasses their maximum number of attempts they will get locked out.
$this->incrementLoginAttempts($request);
return $this->sendFailedLoginResponse($request);
}
}
I don't know if you still need it, but I had the same problem. You need to modify the paths in bootstrap/cache/config.php with the path that you set it for WORKDIR in Dockerfile.

How to getData() from a Zend\Form before the validation in Zend Framework 2/3?

I have a complex nested (order) Zend\Form, that can be edited multiple times. The user first creates an order, but doesn't need to place it immediately. He can just save the order (or more exact: its data) and edit it later. In this case the application loads an Order object (with all its nested structure) and binds it to the form. The important steps are:
get ID of the order from the request
get the Order object by ID
$orderForm->bind($orderObject)
...
Now I want to catch the data and serialize it to JSON. (The background: Forms cloning -- in the next step a empty new form should created and the should be passed to it; after saving we'll get a clone.) It should happen between 2 and 3. So I'm trying
$formData = $this->orderForm->getData();
$formJson = json_encode($formData, JSON_UNESCAPED_SLASHES);
and getting the error:
Zend\Form\Form::getData cannot return data as validation has not yet occurred
Well, I could try to work around it and validate the form:
$formIsValid = $this->orderForm->isValid();
but it only leads to further troubles:
Zend\InputFilter\BaseInputFilter::setData expects an array or Traversable argument; received NULL
Is there a way to get the form data before the validation?
Okay, the comment space is way too small to say everything about what you try to archive. Let 's refactor every single step you mentioned in the starting post. This will lead us to your goal. It 's all about hydration.
This will be a small example, how an order entity with products in it could look like. After the order entity follows the product entity, which we need for this example.
namespace Application\Entity;
class Order implements \JsonSerializable
{
/**
* ID of the order
* #var integer
*/
protected $orderID;
/**
* Array of \Application\Entity\Product
* #var array
*/
protected $products;
public function getOrderID() : integer
{
return $this->orderID;
}
public function setOrderID(integer $orderID) : Order
{
$this->orderID = $orderID;
return $this;
}
public function getProducts()
{
if ($this->products == null) {
$this->products = [];
}
return $this->products;
}
public function setProducts(array $products) : Order
{
$this->products = $products;
return $this;
}
/**
* #see \JsonSerializable::jsonSerialize()
*/
public function jsonSerialize()
{
return get_object_vars($this);
}
}
The following entity represents a product.
class Product implements \JsonSerializable
{
protected $productID;
protected $name;
public function getProductID() : integer
{
return $this->productID;
}
public function setProductID(integer $productID) : Product
{
$this->productID = $productID;
return $this;
}
public function getName() : string
{
return $this->name;
}
public function setName(string $name) : Product
{
$this->name = $name;
return $this;
}
/**
* #see \JsonSerializable::jsonSerialize()
*/
public function jsonSerialize()
{
return get_object_vars($this);
}
}
Above you see our entity, wich represents a single order with several possible products in it. The second member products can be an array with Product entities. This entity represents the data structure of our simple order.
At this point we need a form, which uses this entites as objects for the data it contains. A possible factory for our form could look like this.
namespace Application\Form\Factory;
class OrderFormFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator)
{
$parentLocator = $serviceLocator->getServiceLocator();
$inputFilter = $parentLocator->get('InputFilterManager')->get(OrderInputFiler::class);
$hydrator = new ClassMethods(false);
$entity = new OrderEntity();
return (new OrderForm())
->setInputFilter($inputFilter)
->setHydrator($hydrator)
->setObject($entity);
}
}
This is the factory for our form. We set a hydrator, an input filter and an entity for the form. So you don 't have to bind something. The following code shows, how to handle data with this form.
// retrieve an order from database by id
// This returns a order entity as result
$order = $this->getServiceLocator()->get(OrderTableGateway::class)->fetchById($id);
// Extract the order data from object to array assumed the
// retrieved data from data base is an OrderEntity object
// the hydrator will use the get* methods of the entity and returns an array
$data = (new ClassMethods(false))->extract($order);
// fill the form with the extracted data
$form = $this->getServiceLocator()->get('FormElementManager')->get(OrderForm::class);
$form->setData($data);
if ($form->isValid()) {
// returns a validated order entity
$order = $form->getData();
}
It is absolutely not possible to get data from a form, that is not validated yet. You have to validate the form data and after that you can get the filtered / validated data from the form. Hydrators and entities will help you a lot when you have to handle a lot of data.

Middleware in laravel does not works properly

I am trying to apply middleware on my routes like this:
Route::get('twitterlogin','TwitterController#gettwitterlogin');
Route::post('twitterlogin','TwitterController#posttwitterlogin');
Route::group(['middleware'=>'auth'],function()
{
Route::get('twitternewsfeed','TwitterController#gettwitternewsfeed');
Route::post('postimage','TwitterController#postimage');
Route::post('posttweet','TwitterController#posttweet');
Route::get('twitterlogout','TwitterController#gettwitterlogout');
Route::post('editprofilepic','TwitterController#posteditprofilepic');
Route::post('searchuser','TwitterController#postsearchuser');
Route::post('edittweet{id}','TwitterController#postedittweet');
Route::get('deletetweet{id}','TwitterController#getdeletetweet');
Route::post('editprofile','TwitterController#posteditprofile');
Route::get('userprofile{email}','TwitterController#getuserprofile');
});
And when i am trying to submit my login form it does not bring me on next page. It again opens the login page....what is the exact problem in this? My authenticate middleware is as follows....
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Auth\Guard;
class Authenticate
{
/**
* The Guard implementation.
*
* #var Guard
*/
protected $auth;
/**
* Create a new middleware instance.
*
* #param Guard $auth
* #return void
*/
public function __construct(Guard $auth)
{
$this->auth = $auth;
}
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
if ($this->auth->guest()) {
if ($request->ajax()) {
return response('Unauthorized.', 401);
} else {
return redirect()->guest('twitterlogin');
}
}
return $next($request);
}
}
I assume you are using the Route::post('twitterlogin','TwitterController#posttwitterlogin') route to perform your login operation - please ensure that you're logging the user in with Laravel's Auth correctly and redirecting the user to the appropriate route in the posttwitterlogin method
I feel you also may also be using a different table to store user data - in which case, kindly ensure that you have your auth.php config file setup appropriately
In case you're using a custom authentication mechanism for some reason, you'll need to replace $this->auth->guest() in your middleware with the appropriate alternative check for the user not being logged-in
For eg: if you're setting a Session variable called is_authenticated to true with \Session::put('is_authenticated', true) upon successfully logging in, you'll check for it with if(\Session::get('is_authenticated') === true) in your Middleware instead of relying on the Laravel Auth method if ($this->auth->guest())

ZFcuser get information with inner join

I have a user table with a store id column (store) that correspondent with a store table.
I'm retrieving this store id with a custom entity object.
Application/Entity/User.php
namespace Thuiswinkelen\Entity;
class User extends \ZfcUser\Entity\User
{
/**
* #var int
*/
protected $store;
public function getStore()
{
return $this->store;
}
/**
* Set store.
*
* #param int $store
* #return UserInterface
*/
public function setStore($store)
{
$this->store = (int) $store;
return $this;
}
}
My question is: How to get the store name in the store table (with an inner join?)
It would be great when I can use something like:
<?php echo $this->zfcUserStoreId() ?>
<?php echo $this->zfcUserStoreName() ?>
If you're looking to map entity relationships; you will need an object relational mapper (ORM), such as Doctrine to accomplish this.
This will convert your foreign identifiers into objects in which you can then transverse.
$storeName = $user->getStore()->getName();
I'm guessing that the examples you have suggested $this->zfcUserStoreId() you have invented due to the already existing ZfcUser\View\Helper\ZfcUserDisplayName
This however is a view helper and simplify aids the rendering of a user's name based on other configuration.

access entityManager in XML RPC web API

Hello Stackoverflow Community.
I am currently developing a XML-RPc Server with Zend Framework 2.
I have a ServiceController which is responsible for creating the Server
class ServiceController extends AbstractActionController{
public function xmlAction()
{
$this->handleXML();
$response = $this->getResponse();
return $response;
}
private function handleXML()
{
$xmlServer = new Zend\XmlRpc\Server();
$xmlServer->setClass('Beam\Model\service\Service', 'service');
echo $xmlServer->handle();
}
}
The ServiceClass is my Webapi
class Service{
/**
* getAvailablePackages
*
* getAvailablePackages is responsible for returning all packages which reference a given licensenumber
*
* #param string $licenseNumber
* #return String
*/
public function getAvailablePackages($licenseNumber){
//need to access the entityManager
$em = $this->getServiceLocator->get('Doctrine\ORM\EntityManager');
return "testresponse";
}
}
The XML RPC server works fine. I can call the getAvailablePackages method with a client and i get "testrespons" as a response.
However the problem is, that i would like to select some data from my database in the getAvailablePackages but i'm not sure how to access the entityManager in a non Controller class.
Does anyone have an idea on how to solve this problem ? Thankx for your responses.
Possible solution would be to have a factory.
<?php
namespace ....;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;
class ServiceFactory implements FactoryInterface
{
public function createService (ServiceLocatorInterface $serviceLocator)
{
return new Service ($serviceLocator->get ('Doctrine\ORM\EntityManager'));
}
}
thus, you would be able to have an access to Entity manager.

Resources