How to access the user Token in an injected service to reencode passwords? - fosuserbundle

I have the below code where I am trying to re-encode passwords as users log in (the database has bee migrated form a legacy website). However, I'm not sure what I'm doing wrong as I keep getting errors:
Attempted to call an undefined method named "forward" of class "AppBundle\Service\HubAuthenticator".
I have set things up as follows:
security.yml
security:
encoders:
AppBundle\Entity\Member:
id: club.hub_authenticator
services.yml
services:
//This should be central service than then calls the second
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
club.password_rehash:
class: AppBundle\Service\PasswordRehash
Hubauthenticator.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
function __construct($cost=13)
{
parent::__construct($cost);
}
function isPasswordValid($encoded, $raw, $salt)
{
// Test for legacy authentication (and conditionally rehash the password stored in the database if true)
if ($this->comparePasswords($encoded, sha1("saltA".$raw."saltB"))) {
$this->forward('club.password_rehash:rehash');
}
// Test for Symfony's Bcrypt authentication (any passwords just rehashed in previous step should work here)
if (parent::isPasswordValid($cost=13, $encoded,$raw,$salt)) return true ;
}
}
PasswordRehash.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
class PasswordRehash extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder
{
// Customises BCryptPasswordEncoder class to use legacy SHA method
function rehash($member, $raw, $salt)
{
//Salt is null as Symfony documentation says it is better to generate a new one
parent::encodePassword($member->getPlainPassword, $salt=null ) ;
}
}
Some other previous attempts for completeness:
My guess is that the problem is that I am misunderstanding what objects are available to me. My understanding is that the user hasn't been authenticated at this point so have tried and removed the below attempts:
Trying to inject the $member into the HubAuthenticator service:
function __construct($cost=13)
{
parent::__construct($cost, \Member $member);
}
When trying to get the plainpassword to rehash:
$this->get('security.context')->getToken()->getUser()->getPlainPassword();

In your services, you can only access what dependencies you've injected.
So, to access the current user object, you need to pass it as argument:
service:
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage" ]
Constructor:
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
private $storage;
function __construct($cost = 13, TokenStorageInterface $storage)
{
parent::__construct($cost);
$this->storage = $storage;
// Now you can use:
// $user = $this->storage->getToken()->getUser();
}
}
Then, to access the second service, same way, inject it.
Add it to the service arguments:
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage", "#club.password_rehash" ]
Add it to your constructor:
private $storage;
private $passwordRehash
function __construct($cost = 13, TokenStorageInterface $storage, PasswordRehash $passwordRehash)
{
parent::__construct($cost);
$this->storage = $storage;
$this->passwordRehash = $passwordRehash;
// Now you can use:
// $this->passwordRehash->rehash(...);
}
Hope this helps you.

Related

in cakephp4 how to access a model within a model

How do i access another model within a model in cakephp4.2? The docs on this issue isnt clear to me and i can then run a query on this ? TableRegistry is deprecated now.
error Unknown method "getTableLocator" called on App\Model\Table\LessonsTable
//none of these no longer work
in model {
use Cake\ORM\Locator\LocatorAwareTrait;
class LessonsTable extends Table
{
..
private function getlessonRevenue(){
//$clients = $this->getTableLocator()->get('Clients');
// $cleints = TableRegistry::get('Clients');
// $this->Table = TableRegistry::get('Clients');
$clients = $this->getTableLocator()->get('Clients');
https://api.cakephp.org/4.0/class-Cake.ORM.TableRegistry.html
Try:
<?php
use Cake\ORM\Locator\LocatorAwareTrait; //<------------ add here
class ArchivesTable extends Table
{
use LocatorAwareTrait; // <--------------------------- and add here
public function myMethod()
{
$clients = $this->getTableLocator()->get('Clients');
}
and read https://book.cakephp.org/4/en/orm/table-objects.html#using-the-tablelocator
and learn how to use php trait https://www.phptutorial.net/php-tutorial/php-traits/

Adding a claim using a custom endpoint with AddLocalApiAuthentication

I'm attempting to employ a custom endpoint that adds a specific claim based on a parameter passed to LocalApiController.
I'm following help at:
http://docs.identityserver.io/en/latest/topics/add_apis.html
I configured startup.cs:
services.AddLocalApiAuthentication(principal =>
{
principal.Identities.First().AddClaim(new Claim("additional_claim", "additional_value"));
return Task.FromResult(principal);
});
and my receiving controller:
[Route("localApi")]
[Authorize(LocalApi.PolicyName)]
public class LocalApiController : ControllerBase
{
public IActionResult Get()
{
var claims = from c in User.Claims select new { c.Type, c.Value };
return new JsonResult(claims);
}
}
[Authorize(LocalApi.PolicyName)] works and fires the helper configured in startup.cs which logically fires before the controllers get method.
I don't see how I can pass the controller's {id} parameter to the helper method (that will become the value of the claim I'm to add, replacing the "additional_value" constant).
Any suggestions?

How to get this code work to store in Laravel. No errors and no storage

I can't store name and IP address to DB. I created a table 'info' with appropriate fields by running php artisan migrate.
A schema
Schema::create('info', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
$table->string('ip');
$table->timestamp('created_at')->nullable();
});
A model for Info
class Info extends Model
{
protected $fillable = ['ip', 'name'];
}
Maybe the problem is in my HomeController where I get those variables?
<?php
namespace App\Http\Controllers;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Info;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Request;
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request) {
Info::create(['info' => $request->input('info')]);
}
public function index()
{
if (Auth::check())
{
$name = Auth::user()->name;
$ip = Request::ip();
\App\Events\eventIp::dispatch($ip,$name);
return view('home');
}
}
}
My routes in web.php
Route::post('/home','HomeController#store');
Route::get('/home', 'HomeController#index')->name('home');
});
But it doesn't work. Gives no errors and no records in DB.
Something make me think that it have to do with my index function. I got info in function index and maybe function store doesn't have a clue what I mean.
A controller action is basically a method that usually gets executed when you open an url (as you connect them to routes).
In your example you have connected two routes to their respective actions:
Route::post('/home','HomeController#store');
Route::get('/home', 'HomeController#index')->name('home');
Now, when you log in succesfully, imagine that you end up in the page with url http://localhost:8000/home in your web browser.
The key difference is the method which you use to call your route (you can get an overview of the differences here), in your case you are using GET method.
The resulting action executed it the one associated to /home route with the GET method, that is the HomeController#index action (or method).
The store method, although is in the same HomeController class, doesn't get triggered unless you execute the /home route, but with the POST method.
You can confirm that if you put a debug message in each of the methods like this:
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function store(Request $request)
{
echo 'I will not be executed';
}
public function index()
{
echo 'I have been executed';
}
}
If you want to simply save a info record when you visit the /home route with the GET method, you can put the save in the index method itself and get rid of the store method:
class HomeController extends Controller
{
public function __construct()
{
$this->middleware('auth');
}
public function index()
{
// You can omit Auth::check() because you are using the auth middleware
// that exactly does this job.
Info::create([
'name' => Auth::user()->name,
'ip' => Request::ip(),
]);
return view('home');
}
}
Keep in mind that doing in this way you will get a new database record for each page view you make to that route (if you keep refreshing the page, you should see new records being added to database).
Update
When you use Eloquent Models, laravel will look for a table named after the pluralized model name (Info model will try to use infos table).
However you created a table named info. To solve that you can either rename the table and rerun the migration with php artisan migrate:refresh (it will delete all the existing data in the database you are using for your laravel app)
Or specify the table name to use for that laravel model:
class Info extends Model
{
protected $table = 'info';
protected $fillable = ['ip', 'name'];
}
How are you calling the functions? There is a couple of things wrong with your code, but you're saying there are no errors at all.
Firstly, your Info::create call does not need the ['info' => $request->input('info')] info. This is because your Info model has no database property called info, but normally you would get an obvious error with the approach, which is why I expect you are also calling the store method incorrectly.
Call the create method like so:
$infoModel = Info::create(['name' => $request->input('name'), 'ip' => $request->input['ip']]);
or, if you can guarantee your $request only contains the needed fields (properly validated), you can just do
$infoModel = Info::create($request->all());
Add a little more info to the question on how you are calling store and we can probably solve the rest of your problem.
Within your store function inside HomeController , use
Info::create([
'name' => Auth::user()->name,
'ip' => Request::ip(),
]);
and make sure Info model is imported.
Also make sure your route has the call to store function while POSTing data .

One Observer not being created, but another identical one is

Using rxjs with Angular 2 RC.1 and I'm creating a few Observer's. I have the following in a service:
taxRates$: Observable<TaxRate[]>;
taxTables$: Observable<TaxTable[]>;
private _taxRatesObserver: Observer<TaxRate[]>;
private _taxTablesObserver: Observer<TaxTable[]>;
constructor(private http: HttpService) {
this.taxRates$ = new Observable<TaxRate[]>(observer =>
this._taxRatesObserver = observer
).share();
this.taxTables$ = new Observable<TaxTable[]>(observer =>
this._taxTablesObserver = observer
).share();
// more stuff here
}
The _taxRatesObserver is created correctly, but _taxTablesObserver is not. When I inspect the dev tools, I see this:
_taxRatesObserver:Subscriber
http:HttpService
taxRates$:RefCountObservable
taxTables$:RefCountObservable
I'd expect to also see _taxTablesObserver:Subscriber but if I log it via the console, it always shows as undefined. TaxTable, if it matters, looks like this:
export class TaxTable {
constructor(
public id: number,
public name: string,
public description: string) {}
}
From docs:
subscribe the function that is called when the Observable is initially subscribed to.
_taxRatesObserver and _taxTablesObserver are initialized after subscription to taxRates$ and taxTables$ correspondingly.
Most probably taxTables$ does not have subscribers.

What is preventing passwords being rehashed with the injected Symfony services (FOSUserBundle)

This is a follow up question to my original question where I am trying to rehash user passwords during authentication to migrate from a legacy database.
After implementing the helpful answer there. I have now hit another problem where I receive no error (with the below code) but the passwords and salt are not being updated in the database:
security.yml
security:
encoders:
AppBundle\Entity\Member:
id: club.hub_authenticator
services.yml
services:
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
arguments: ["#security.token_storage" ,"#club.password_rehash"]
club.password_rehash:
class: AppBundle\Service\PasswordRehash
arguments: [ "#security.token_storage" ]
HubAuthenticator.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class HubAuthenticator extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
private $storage ;
private $passwordRehash ;
function __construct(TokenStorageInterface $storage, PasswordRehash $passwordRehash, $cost = 13)
{
parent::__construct($cost);
$this->storage=$storage ;
$this->passwordRehash = $passwordRehash;
}
function isPasswordValid($encoded, $raw, $salt)
{
// Test for legacy authentication (and conditionally rehash the password in the database)
if ($this->comparePasswords($encoded, sha1("SaltA".$raw."SaltB"))) {
$this->passwordRehash->rehash($raw);
return true ;
}
// Test for Bcrypt authentication
if (parent::isPasswordValid($encoded,$raw,$salt)) return true ;
}
}
PasswordRehash.php
namespace AppBundle\Service;
use Symfony\Component\Security\Core\Encoder\PasswordEncoderInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class PasswordRehash extends \Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder implements PasswordEncoderInterface
{
// private $storage ;
function __construct(TokenStorageInterface $storage , $cost = 13)
{
parent::__construct($cost);
// $this->storage=$storage ;
}
// Customises BCryptPasswordEncoder to use legacy Club SHA method
function rehash($raw)
{
// Commented out as I THINK the $raw is the plainPassword I'm trying to use to reencode the password
// $user=$this->storage->getToken()->getUser();
// $token = $this->storage->getToken();
//Salt left empty as have read this will auto-generate a new one (which is also better practice)
parent::encodePassword($raw, $salt=null ) ;
return true ;
}
}
If you want to store the result of PasswordRehash#rehash() as the value of your user's password, make your method returning the new password:
function rehash($raw)
{
return parent::encodePassword($raw, null);
}
Then, to update the user, you need to set the new password and store the changes.
Inject the doctrine EntityManager in your service:
club.hub_authenticator:
class: AppBundle\Service\HubAuthenticator
arguments: ["#security.token_storage" ,"#club.password_rehash", "#doctrine.orm.entity_manager" ]
And in your class:
use Doctrine\ORM\EntityManager;
function __construct(TokenStorageInterface $storage, PasswordRehash $passwordRehash, EntityManager $em, $cost = 13)
{
parent::__construct($cost);
$this->storage = $storage;
$this->passwordRehash = $passwordRehash;
$this->em = $em;
}
Then use it:
if (!$token = $this->storage->getToken()) {
return;
}
if ($this->comparePasswords($encoded, sha1("SaltA".$raw."SaltB"))) {
// Retrieve the user
$user = $token->getUser();
// Change the user password
$user->setPassword($this->passwordRehash->rehash($raw));
// Save the changes
$em->flush($user);
}
But, I'm really not sure about the logic you are implementing.
I don't see the benefit of extending the BcryptPasswordEncoder.
You should look at this post that shows a quick way to convert the password of your users from a legacy app to FOSUserBundle-compliant passwords in only one time, rather than do it on each authentication.
Then look more at how to work with services in Symfony2+.
Hope this helps you.

Resources