SAML IDP in Laravel - Logging out - php

I have a Laravel application where users can access a third party application via my application (acting as an Identity Provider).
The logging in was achieved by taking inspiration from: https://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-make-Response/
<?php
namespace App\Http\Controllers\Saml;
use App\Http\Controllers\Controller;
use CodeGreenCreative\SamlIdp\Traits\PerformsSingleSignOn;
class LoginController extends Controller
{
use PerformsSingleSignOn;
/**
* Initialise a new constructotr instance.
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('verified');
$this->middleware('investor.eligible');
$this->init();
}
/**
* Display the login form for accessing SAML
*
* #return void
*/
public function showLoginForm()
{
return view('user.custodian');
}
/**
* Attempt to log into a given Service Provider.
*
* #return void
*/
public function login()
{
$this->setDestination();
return $this->samlResponse();
}
/**
* Send a SAML message to the intended Service Provider with security assertions and other bearer assertions.
* In this case we're explicitly sending email and name id
*
* #link https://www.lightsaml.com/LightSAML-Core/Cookbook/How-to-make-Response/
*
* #return void
*/
public function samlResponse()
{
// Create a new SAML Response instance
$this->response = new \LightSaml\Model\Protocol\Response();
// Create a response object that we'll send later.
$this->response
->addAssertion($assertion = new \LightSaml\Model\Assertion\Assertion())
->setStatus(
new \LightSaml\Model\Protocol\Status(
new \LightSaml\Model\Protocol\StatusCode(
\LightSaml\SamlConstants::STATUS_SUCCESS
)
)
)
->setID(\LightSaml\Helper::generateID())
->setIssueInstant(new \DateTime())
->setDestination($this->destination)
->setIssuer(new \LightSaml\Model\Assertion\Issuer($this->issuer));
// Build out our Assertion instance
$assertion
->setId(\LightSaml\Helper::generateID())
->setIssueInstant(new \DateTime())
->setIssuer(new \LightSaml\Model\Assertion\Issuer($this->issuer))
->setSignature(new \LightSaml\Model\XmlDSig\SignatureWriter($this->certificate, $this->private_key))
->setSubject(
(new \LightSaml\Model\Assertion\Subject())
->setNameID(new \LightSaml\Model\Assertion\NameID(
auth()->user()->email,
\LightSaml\SamlConstants::NAME_ID_FORMAT_EMAIL
))
->addSubjectConfirmation(
(new \LightSaml\Model\Assertion\SubjectConfirmation())
->setMethod(\LightSaml\SamlConstants::CONFIRMATION_METHOD_BEARER)
->setSubjectConfirmationData(
(new \LightSaml\Model\Assertion\SubjectConfirmationData())
->setNotOnOrAfter(new \DateTime('+1 MINUTE'))
->setRecipient($this->destination)
)
)
)
->setConditions(
(new \LightSaml\Model\Assertion\Conditions())
->setNotBefore(new \DateTime())
->setNotOnOrAfter(new \DateTime('+1 MINUTE'))
->addItem(
new \LightSaml\Model\Assertion\AudienceRestriction([$this->destination])
)
)
->addItem(
(new \LightSaml\Model\Assertion\AttributeStatement())
->addAttribute(new \LightSaml\Model\Assertion\Attribute(
\LightSaml\ClaimTypes::EMAIL_ADDRESS,
auth()->user()->email,
))
->addAttribute(new \LightSaml\Model\Assertion\Attribute(
\LightSaml\ClaimTypes::NAME_ID,
auth()->user()->id,
))
)
->addItem(
(new \LightSaml\Model\Assertion\AuthnStatement())
->setAuthnInstant(new \DateTime('-10 MINUTE'))
->setSessionIndex(\LightSaml\Helper::generateID())
->setAuthnContext(
(new \LightSaml\Model\Assertion\AuthnContext())
->setAuthnContextClassRef(\LightSaml\SamlConstants::AUTHN_CONTEXT_WINDOWS)
)
);
return $this->sendSAMLResponse();
}
/**
* Send a SAML response to the Service Provider and display the end result to the user.
* If this is successful it should log the user into the SP.
*
* #param \LightSaml\Model\Protocol\Response $response
*
* #return void
*/
private function sendSAMLResponse()
{
$bindingFactory = new \LightSaml\Binding\BindingFactory();
$postBinding = $bindingFactory->create(\LightSaml\SamlConstants::BINDING_SAML2_HTTP_POST);
$messageContext = new \LightSaml\Context\Profile\MessageContext();
$messageContext->setMessage($this->response)->asResponse();
$httpResponse = $postBinding->send($messageContext);
return $httpResponse->getContent();
}
/**
* Set the destination.
*
* #return void
*/
private function setDestination()
{
$destination = config('samlidp.sp.aHR0cHM6Ly9zZXJ2aWNlcy11ay5zdW5nYXJkZHguY29tL1NpbmdsZVNpZ25Pbi9TZXJ2aWNlUHJvdmlkZXI=.destination');
$this->destination = $destination;
}
}
I run into a snag however when I try to perform a SAML Single Log Out from an SP.
The steps are as follows:
SP sends a logout request
IDP responds
So I have a logout controller similar to the above.
<?php
namespace App\Http\Controllers\Saml;
use App\Http\Controllers\Controller;
use App\Jobs\SamlSlo as JobsSamlSlo;
use CodeGreenCreative\SamlIdp\Traits\PerformsSingleSignOn;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use LightSaml\Helper;
use LightSaml\Model\Assertion\Issuer;
use LightSaml\Model\Assertion\NameID;
use LightSaml\Model\Context\DeserializationContext;
use LightSaml\Model\Protocol\LogoutRequest;
use LightSaml\SamlConstants;
use Log;
class LogoutController extends Controller
{
use PerformsSingleSignOn;
private $sp;
/**
* #param [type] $sp [description]
*/
public function __construct()
{
$this->sp = config('samlidp.sp.aHR0cHM6Ly9zZXJ2aWNlcy11ay5zdW5nYXJkZHguY29tL1NpbmdsZVNpZ25Pbi9TZXJ2aWNlUHJvdmlkZXI=');
$this->init();
}
public function index()
{
$this->setDestination();
$this->setDestination();
return redirect($this->request());
}
/**
* [request description]
* #return [type] [description]
*/
public function request()
{
$this->response = (new LogoutRequest)
->setIssuer(new Issuer($this->issuer))
->setNameID((new NameID(Helper::generateID(), SamlConstants::NAME_ID_FORMAT_TRANSIENT)))
->setID(Helper::generateID())
->setIssueInstant(new \DateTime)
->setDestination($this->destination);
return $this->send(SamlConstants::BINDING_SAML2_HTTP_REDIRECT);
}
private function setDestination()
{
$destination = $this->sp['logout'];
$parsed_url = parse_url($destination);
parse_str($parsed_url['query'] ?? '', $parsed_query_params);
$parsed_query_params['idp'] = config('app.url');
$this->destination = strtok($destination, '?') . '?' . http_build_query($parsed_query_params);
Log::info($this->destination);
}
}
The main issue is that in every example I've seen, the SPs have a dedicated logging out URL.
If I set the logout URL as a route within my IDP would this flow still work?

Related

Symfony oauth2 provider, Remove repeating code by 'stashing' new class variables within

I'm working on a big project that implements OAuth2 -> Specifically Oauth2-EveOnline
im using
PHP 8
Symfony 6
EveLabs/Oauth2
and as part of usage when creating the provider class the following code is required
$provider = new EveOnline([
'clientId' => $this->getParameter('eve.client_id'),
'clientSecret' => $this->getParameter('eve.client_secret'),
'redirectUri' => $this->getParameter('eve.redirect_uri'),
]);
What I would like to do is 'stash' this array within the EveOnline provider class so that in future all I have to call is
$provider = new EveOnline();
Unfortunate searching for an answer hasn't been fruitful, perhaps my searches are too vague, my problem is something that nobody has tried before or more likely it's simple and I am not experienced enough to see the solution
I have tried using a Service class to setup the provider but calling $this-> gets a little messy and I have been unable to work that out as $this calls the document your working on and to get .env details it should be done from the controller
I know that if I copy the provider from vendor/src to my App/src then it will override the vendor/src, if I can make changes to include my array and pull the info it needs then I can save myself passing the array on almost every page as I require the provider to manage tokens and communication with the EVE API (ESI).
Is this possible or is there a way I can move this to a Service?
When I learned how to create services I was rather happy and have created a few to handle all my API calls I just pass in my $provider and return the $response to my controller.
I was hoping to do something similar with building the provider, create a page builder service that requires 2 lines in the controller and handles setting up the provider and populating the session with info that I need to call on my pages, or use in DB/API queries.
I have already implemented a service for refreshing tokens but that is a very small inefficient feat.
App\Controller
<?php
namespace App\Controller;
use App\Service\TokenRefresh;
use Evelabs\OAuth2\Client\Provider\EveOnline;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\Routing\Annotation\Route;
class Controller extends AbstractController
{
#[Route('/', name: 'page_homepage')]
public function homepage()
{
$session = new Session();
$session->start();
$provider = new EveOnline([
'clientId' => $this->getParameter('eve.client_id'),
'clientSecret' => $this->getParameter('eve.client_secret'),
'redirectUri' => $this->getParameter('eve.redirect_uri'),
]);
if($_SESSION['token']->hasExpired()){
$token = new TokenRefresh();
$token->newToken($provider);
}
if (isset($_SESSION)) {
dump($_SESSION);
}
return $this->render('homepage.html.twig');
}
#[Route('/login', name: 'page_login')]
public function login()
{
$session = new Session();
$session->start();
$scopes = [
'scope' => ['publicData', 'esi-markets.read_character_orders.v1', 'esi-markets.structure_markets.v1'] // array or string
];
$provider = new EveOnline([
'clientId' => $this->getParameter('eve.client_id'),
'clientSecret' => $this->getParameter('eve.client_secret'),
'redirectUri' => $this->getParameter('eve.redirect_uri'),
]);
if (!isset($_GET['code'])) {
// here we can set requested scopes but it is totally optional
// make sure you have them enabled on your app page at
// https://developers.eveonline.com/applications/
// If we don't have an authorization code then get one
$authUrl = $provider->getAuthorizationUrl($scopes);
$_SESSION['oauth2state'] = $provider->getState();
unset($_SESSION['token']);
header('Location: ' . $authUrl);
exit;
// Check given state against previously stored one to mitigate CSRF attack
} elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) {
unset($_SESSION['oauth2state']);
exit('Invalid state');
} else {
// In this example we use php native $_SESSION as data store
if (!isset($_SESSION['token'])) {
// Try to get an access token (using the authorization code grant)
$_SESSION['token'] = $provider->getAccessToken('authorization_code', [
'code' => $_GET['code']
]);
} elseif ($_SESSION['token']->hasExpired()) {
$token = new TokenRefresh();
$token->newToken($provider);
}
// Optional: Now you have a token you can look up a users profile data
try {
// We got an access token, let's now get the user's details
$user = $provider->getResourceOwner($_SESSION['token']);
// Use these details to create a new profile
} catch (\Exception $e) {
// Failed to get user details
exit('Oh dear...');
}
$user = $user->toArray();
$owner = [
'character_id' => $user['CharacterID'],
'character_name' => $user['CharacterName']
];
$session->set('owner', $owner);
$_SESSION['owner'] = $owner;
dump($user);
if (isset($_SESSION)) {
dump($_SESSION);
dump($session->get('owner'));
}
return $this->render('login.html.twig');
}
}
// TODO: Remove debug session clear
#[Route('/clrsession', name: 'app_clrsession')]
public function clrsession(){
$session = new Session();
$session->invalidate();
return $this->redirectToRoute('page_homepage');
}
}
Provider\EveOnline
<?php
namespace Evelabs\OAuth2\Client\Provider;
use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
use League\OAuth2\Client\Token\AccessToken;
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
use Psr\Http\Message\ResponseInterface;
class EveOnline extends AbstractProvider
{
use BearerAuthorizationTrait;
/**
* Default scopes
*
* #var array
*/
public $defaultScopes = [];
/**
* Get the string used to separate scopes.
*
* #return string
*/
protected function getScopeSeparator()
{
return ' ';
}
/**
* Returns the base URL for authorizing a client.
*
* Eg. https://oauth.service.com/authorize
*
* #return string
*/
public function getBaseAuthorizationUrl()
{
return 'https://login.eveonline.com/oauth/authorize';
}
/**
* Returns the base URL for requesting an access token.
*
* Eg. https://oauth.service.com/token
*
* #param array $params
* #return string
*/
public function getBaseAccessTokenUrl(array $params)
{
return 'https://login.eveonline.com/oauth/token';
}
/**
* Returns the URL for requesting the resource owner's details.
*
* #param AccessToken $token
* #return string
*/
public function getResourceOwnerDetailsUrl(AccessToken $token)
{
return 'https://login.eveonline.com/oauth/verify';
}
/**
* Returns the default scopes used by this provider.
*
* This should only be the scopes that are required to request the details
* of the resource owner, rather than all the available scopes.
*
* #return array
*/
protected function getDefaultScopes()
{
return $this->defaultScopes;
}
/**
* Checks a provider response for errors.
*
* #throws IdentityProviderException
* #param ResponseInterface $response
* #param array|string $data Parsed response data
* #return void
*/
protected function checkResponse(ResponseInterface $response, $data)
{
//not throwing anything for 2xx responses
if (intval(substr($response->getStatusCode(), 0, 1)) === 2) {
return;
}
$message = $this->safeRead($data, 'error_description') || $this->safeRead($data, 'message');
throw new IdentityProviderException(
$message ?: $response->getReasonPhrase(),
$response->getStatusCode(),
$response
);
}
/**
* Generates a resource owner object from a successful resource owner
* details request.
*
* #param array $response
* #param AccessToken $token
* #return ResourceOwnerInterface
*/
protected function createResourceOwner(array $response, AccessToken $token)
{
return new EveOnlineResourceOwner($response);
}
/**
* Internal helper function to safe read from an array
* #param mixed $array
* #param string|int $key
* #return null
*/
private function safeRead($array, $key)
{
return !empty($array[$key]) ? $array[$key] : null;
}
}

How do I extend Laravel Sanctum's functionality?

I am specifically trying to get Sanctum's Guard class to look for the API token in a JSON request body if it can't find it in the Authorization header. I simply need to add an elseif after it checks for the bearer token.
So question is: What is the best way to override this method (or class) with my own, without touching the original Sanctum files?
<?php
namespace Laravel\Sanctum;
use Illuminate\Contracts\Auth\Factory as AuthFactory;
use Illuminate\Http\Request;
class Guard
{
/**
* The authentication factory implementation.
*
* #var \Illuminate\Contracts\Auth\Factory
*/
protected $auth;
/**
* The number of minutes tokens should be allowed to remain valid.
*
* #var int
*/
protected $expiration;
/**
* Create a new guard instance.
*
* #param \Illuminate\Contracts\Auth\Factory $auth
* #param int $expiration
* #return void
*/
public function __construct(AuthFactory $auth, $expiration = null)
{
$this->auth = $auth;
$this->expiration = $expiration;
}
/**
* Retrieve the authenticated user for the incoming request.
*
* #param \Illuminate\Http\Request $request
* #return mixed
*/
public function __invoke(Request $request)
{
if ($user = $this->auth->guard('web')->user()) {
return $this->supportsTokens($user)
? $user->withAccessToken(new TransientToken)
: $user;
}
if ($token = $request->bearerToken()) {
$model = Sanctum::$personalAccessTokenModel;
$accessToken = $model::where('token', hash('sha256', $token))->first();
if (! $accessToken ||
($this->expiration &&
$accessToken->created_at->lte(now()->subMinutes($this->expiration)))) {
return;
}
return $this->supportsTokens($accessToken->tokenable) ? $accessToken->tokenable->withAccessToken(
tap($accessToken->forceFill(['last_used_at' => now()]))->save()
) : null;
}
}
/**
* Determine if the tokenable model supports API tokens.
*
* #param mixed $tokenable
* #return bool
*/
protected function supportsTokens($tokenable = null)
{
return in_array(HasApiTokens::class, class_uses_recursive(
$tokenable ? get_class($tokenable) : null
));
}
}
I don't know if you've already figured out but I think you need to add an entry in your AppServiceProvider boot method and override configureGuard functionality placed in SanctumServiceProvider at line 94.
app/Providers/AppServiceProvider.php
Auth::resolved(function ($auth) {
$auth->extend('sanctum', function ($app, $name, array $config) use ($auth) {
return tap($this->createGuard($auth, $config), function ($guard) {
$this->app->refresh('request', $guard, 'setRequest');
});
});
});
You will also need to override createGuard function to specify your custom Guard class with the functionality you require.

Adding Captcha to Symfony2 Login

I need to add Captcha to my login page, I am using GregwarCaptchaBundle and FosUserBundle.
For the moment I have get show the captcha on the login using the following code:
<?php
/*
* This file is part of the FOSUserBundle package.
*
* (c) FriendsOfSymfony <http://friendsofsymfony.github.com/>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace FOS\UserBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\SecurityContextInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Gregwar\Captcha\CaptchaBuilder;
class SecurityController extends Controller
{
public function loginAction(Request $request)
{
$builtCaptcha = new CaptchaBuilder();
$builtCaptcha->build();
$builtCaptcha->save('captcha.jpg');
/** #var $session \Symfony\Component\HttpFoundation\Session\Session */
$session = $request->getSession();
if (class_exists('\Symfony\Component\Security\Core\Security')) {
$authErrorKey = Security::AUTHENTICATION_ERROR;
$lastUsernameKey = Security::LAST_USERNAME;
} else {
// BC for SF < 2.6
$authErrorKey = SecurityContextInterface::AUTHENTICATION_ERROR;
$lastUsernameKey = SecurityContextInterface::LAST_USERNAME;
}
// get the error if any (works with forward and redirect -- see below)
if ($request->attributes->has($authErrorKey)) {
$error = $request->attributes->get($authErrorKey);
} elseif (null !== $session && $session->has($authErrorKey)) {
$error = $session->get($authErrorKey);
$session->remove($authErrorKey);
} else {
$error = null;
}
if (!$error instanceof AuthenticationException) {
$error = null; // The value does not come from the security component.
}
// last username entered by the user
$lastUsername = (null === $session) ? '' : $session->get($lastUsernameKey);
if ($this->has('security.csrf.token_manager')) {
$csrfToken = $this->get('security.csrf.token_manager')->getToken('authenticate')->getValue();
} else {
// BC for SF < 2.4
$csrfToken = $this->has('form.csrf_provider')
? $this->get('form.csrf_provider')->generateCsrfToken('authenticate')
: null;
}
$t = $request->get('_captcha');
if($t!=$builtCaptcha){
echo 'error';
}
var_dump($t);
return $this->renderLogin(array(
'last_username' => $lastUsername,
'error' => $error,
'csrf_token' => $csrfToken,
'captcha' => $builtCaptcha,
));
}
/**
* Renders the login template with the given parameters. Overwrite this function in
* an extended controller to provide additional data for the login template.
*
* #param array $data
*
* #return \Symfony\Component\HttpFoundation\Response
*/
protected function renderLogin(array $data)
{
return $this->render('FOSUserBundle:Security:login.html.twig', $data);
}
public function checkAction($builtCaptcha)
{
return $this->redirect($this->generateUrl('fos_user_login'));
}
throw new \RuntimeException('You must configure the check path to be handled by the firewall using form_login in your security firewall configuration.');
}
public function logoutAction()
{
throw new \RuntimeException('You must activate the logout in your security firewall configuration.');
}
}
And I overrided the login and register template as the documentation explain (the registration is working fine with the captcha)
The problem is that I don't know how to validate the captcha's code.
I guess that I should do it into the checkAction()
But I am not sure, I am very caught with this problem.
If someone could help me I would be very grateful, Thanks in advance.
I had the same problem when trying to implement Google ReCaptcha into a simple login-form with database provider. This is a working solution based on a listener triggered by SecurityEvents::INTERACTIVE_LOGIN. This event is fired after verification of credentials but before redirecting to default_target_path defined in security.yml.
Note: The example is mixed with some other functionality, because I am also capturing failed login attempts.
<?php
namespace ExampleBundle\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\AuthenticationEvents;
use Symfony\Component\Security\Core\Event\AuthenticationFailureEvent;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
use Symfony\Component\Security\Http\SecurityEvents;
use ExampleBundle\Google\GoogleReCaptcha;
use Doctrine\ORM\EntityManager;
use ExampleBundle\Entity\User;
/**
* Class AuthenticationAttemptListener
* #package ExampleBundle\EventListener
*/
class AuthenticationAttemptListener implements EventSubscriberInterface
{
/**
* #var EntityManager
*/
private $entityManager;
/**
* #var GoogleReCaptcha
*/
private $googleReCaptcha;
/**
* #var integer
*/
private $maxFailedLoginAttempts;
/**
* AuthenticationAttemptListener constructor.
*
* #param EntityManager $entityManager
* #param GoogleReCaptcha $googleReCaptcha
* #param integer $maxFailedLoginAttempts
*/
public function __construct(EntityManager $entityManager, GoogleReCaptcha $googleReCaptcha, $maxFailedLoginAttempts)
{
$this->entityManager = $entityManager;
$this->googleReCaptcha = $googleReCaptcha;
$this->maxFailedLoginAttempts = $maxFailedLoginAttempts;
}
/**
* #return array
*/
public static function getSubscribedEvents()
{
return array(
AuthenticationEvents::AUTHENTICATION_FAILURE => 'onAuthenticationFailure',
AuthenticationEvents::AUTHENTICATION_SUCCESS => 'onAuthenticationSuccess',
SecurityEvents::INTERACTIVE_LOGIN => 'onInteractiveLogin'
);
}
/**
* Count failed login attempts and save to database on existing usernames
*
* #param AuthenticationFailureEvent $event
*/
public function onAuthenticationFailure(AuthenticationFailureEvent $event)
{
if ($event->getAuthenticationException() instanceof BadCredentialsException) {
$databaseUser = $this->searchUserinDatabase($event);
// increase failed attempt counter or lock-up account
if ($databaseUser !== null) {
if (!$databaseUser->isEnabled()) {
throw new CustomUserMessageAuthenticationException('user_deactivated');
} else {
$databaseUser->increaseFailedLoginAttempts();
$databaseUser->setFailedLoginLastTimestamp(new \DateTime());
if ($databaseUser->getFailedLoginAttempts() == $this->maxFailedLoginAttempts) {
$databaseUser->setIsActive(0);
}
$this->entityManager->persist($databaseUser);
$this->entityManager->flush();
}
}
}
}
/**
* #param AuthenticationSuccessEvent $event
*/
public function onAuthenticationSuccess(AuthenticationEvent $event)
{
// Attention: Event will be thrown on every request if you have session-based authentication!
// Reset of attempt counter may occur if user is logged in while brute force attack is running
}
/**
* Check incoming google recaptcha token and reset attempt-counter on success or throw exception
*
* #param InteractiveLoginEvent $event
*/
public function onInteractiveLogin(InteractiveLoginEvent $event)
{
$reCaptchaResponse = $event->getRequest()->get('g-recaptcha-response');
$captchaRequest = $this->googleReCaptcha->checkReCaptcha($reCaptchaResponse);
if (is_array($captchaRequest) && $captchaRequest['success'] === true) {
// reset attempt counter because of successful login and positive recaptcha response
$databaseUser = $this->searchUserinDatabase($event);
if ($databaseUser !== null && $databaseUser->isEnabled()) {
$databaseUser->setFailedLoginAttempts(null);
$databaseUser->setFailedLoginLastTimestamp(null);
$this->entityManager->persist($databaseUser);
$this->entityManager->flush();
}
} else {
// on all other recaptcha related errors throw exception
throw new CustomUserMessageAuthenticationException('recaptcha_error');
}
}
/**
* Retrieve user from database
*
* #param AuthenticationFailureEvent|AuthenticationEvent $event
*
* #return User|null
*/
private function searchUserinDatabase($event)
{
$token = $event->getAuthenticationToken();
$username = $token->getUsername();
$databaseUser = null;
if (!$token instanceof AnonymousToken) {
// get user from database
$databaseUser = $this->entityManager->getRepository($entity)->findOneBy(array(
"username" => $username
));
}
return $databaseUser;
}
}
Hope that helps ...

How to use method injection with App::call in Laravel

I am using Laravel 5.2 and wrote my own Service Provider. I want to inject a Request object into the register method.
The base problem is that I want to call different service container depending on a special request param - all the service container implementing the same interface/contract of course.
The error message I am getting is:
ReflectionException in Container.php line 559:
Function registerService() does not exist
My service provider looks like that:
<?php
namespace App\Providers;
use Illuminate\Http\Request;
use Illuminate\Support\ServiceProvider;
use App\Contracts\Extractor;
class ExtractorServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* #var bool
*/
protected $defer = true;
/**
* Available services for channels
*
* #var array
*/
protected $availableServices = ['AExtractor', 'ZExtractor'];
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->call('registerService');
}
/**
* #param Request $request
* #return void
*/
protected function registerService(Request $request)
{
$tag = DB::table('channels')->where('id', $request->channel)->value('tag')->first()->tag;
$selectedExtractor = $tag . 'Extractor';
$extractor = 'AExtractor';
if(in_array($selectedExtractor, $this->availableServices)) {
$extractor = $selectedExtractor;
}
$this->app->bind('App\Contracts\Extractor', "App\\Helpers\\{$extractor}");
}
/**
* Get the services provided by the provider.
*
* #return array
*/
public function provides()
{
return [Extractor::class];
}
}
How can I use $this->app->call('registerService'); to call my registerService function and inject the Request object?
The problem is you're calling App:call in a wrong way: you have to specify the object on which you want to call the method and the method, like this :
$this->app->call( [ $this, 'registerService' ] );

Internal Client on Thruway WAMP2

I am working on a sample for Internal Client + Authentication model like the one below.
Now I need to retrieve a list of connected sessions and intercept the close event of a session from Internal Client.
I want to ask if there's any method to archive that task? I was thinking about saving that list in redis, but it means I would have to re-write Thruway\Peer\Router classes, because the needed variables are now private, we don't have access to them to extends.
File server.php
<?php
/**
* server.php
*/
require "../bootstrap.php";
require 'InternalClient.php';
require 'SimpleAuthProviderClient.php';
use Thruway\Peer\Router;
use Thruway\Transport\RatchetTransportProvider;
use React\EventLoop\Factory;
use Thruway\Manager\ManagerClient;
use Thruway\Transport\InternalClientTransportProvider;
$manager = new ManagerClient();
$loop = Factory::create();
$router = new Router($loop, $manager);
$router->addTransportProvider(new InternalClientTransportProvider($manager));
$internalTransportProvider = new InternalClientTransportProvider(new \InternalClient());
$router->addTransportProvider($internalTransportProvider);
$authMgr = new \Thruway\Authentication\AuthenticationManager();
$router->setAuthenticationManager($authMgr);
$router->addTransportProvider(new InternalClientTransportProvider($authMgr));
//Provide authentication for the realm: 'somerealm'
$authProvClient = new SimpleAuthProviderClient(["somerealm"]);
$router->addTransportProvider(new InternalClientTransportProvider($authProvClient));
$transportProvider = new RatchetTransportProvider("127.0.0.1", 9090);
$router->addTransportProvider($transportProvider);
$router->start();
File SimpleAuthProviderClient.php
<?php
/**
* SimpleAuthProviderClient.php
*/
require "../bootstrap.php";
/**
* Class SimpleAuthProviderClient
*/
class SimpleAuthProviderClient extends \Thruway\Authentication\AbstractAuthProviderClient
{
/**
* #return string
*/
public function getMethodName()
{
return 'simplysimple';
}
/**
* #param mixed $signature
* #param null $extra
* #return array
*/
public function processAuthenticate($signature, $extra = null)
{
if ($signature == "letMeIn") {
return ["SUCCESS"];
} else {
return ["FAILURE"];
}
}
}
File InternalClient.php
<?php
/**
* InternalClient.php
*/
require "../bootstrap.php";
/**
* Class InternalClient
*/
class InternalClient extends Thruway\Peer\Client
{
function __construct()
{
parent::__construct("realm1");
}
/**
* #param \Thruway\AbstractSession $session
* #param \Thruway\Transport\TransportInterface $transport
*/
public function onSessionStart($session, $transport)
{
echo "--------------- Hello from InternalClient ------------";
$this->getCallee()->register($this->session, 'com.example.getphpversion', [$this, 'getPhpVersion']);
}
function start()
{
}
/**
* #return array
*/
function getPhpVersion()
{
return [phpversion()];
}
}
For reference purposes, this question was answered on Github.
If you subscribe to the events wamp.metaevent.session.on_join and wamp.metaevent.session.on_leave you will get notified. The event returns 1 argument similar to this:
{
"realm":"realm1",
"authprovider":null,
"authid":"username",
"authrole":"none",
"authmethod":"simplysimple",
"session":6016528494456948
}

Categories