Drupal 8 Class 'Drupal\Core\Session\AccountInterface' not found - drupal

I am trying to write a custom php script in my Drupal site root that checks if the user is logged in. To check this I import bootstrap.inc. However when I do this it throws me this error
This is the code of the php script in my site root:
<?php
require_once './core/includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
global $user;
var_dump($user->uid);
?>
Anyone has a solution to this?

To bootstrap Drupal 8, you need different code. Drupal 8 doesn't have any drupal_bootstrap() function, so the code you are using would throw a PHP error.
You can use authorize.php as guideline to write your own script.
use Drupal\Core\DrupalKernel;
use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
$autoloader = (require_once 'autoload.php');
try {
$request = Request::createFromGlobals();
$kernel = DrupalKernel::createFromRequest($request, $autoloader, 'prod');
$kernel->prepareLegacyRequest($request);
} catch (HttpExceptionInterface $e) {
$response = new Response('', $e->getStatusCode());
$response
->prepare($request)
->send();
exit;
}
\Drupal::moduleHandler()
->addModule('system', 'core/modules/system');
\Drupal::moduleHandler()
->addModule('user', 'core/modules/user');
\Drupal::moduleHandler()
->load('system');
\Drupal::moduleHandler()
->load('user');
$account = \Drupal::service('authentication')
->authenticate($request);
if ($account) {
\Drupal::currentUser()
->setAccount($account);
if (\Drupal::currentUser()->isAuthenticated() {
// The user is logged-in.
}
}

I fixed this by using a complete different approach. I wrote a module which sets a cookie on the moment that the user logs in to drupal (I use the hook_user_login for this). When the user logs out I delete that cookie (I use the hook_user_logout for this). This is the code of my test.module:
/**
* #param $account
*/
function intersoc_content_user_login($account)
{
setcookie("user", "loggedin", time() + (86400 * 30),"/");
}
/**
* #param $account
*/
function intersoc_content_user_logout($account)
{
if (isset($_COOKIE['user']))
{
unset($_COOKIE['user']);
setcookie('user', '', time() - 3600, '/'); //Clearing cookie
}
}
Then in my custom script in the site root I check if the cookie is set. When the cookie exists => The user is logged in. If the cookie doesn't exist then the user isn't logged in. The isLoggedIn() function below:
/**
* #return bool which indicates if the user is logged in or not
*/
private function isLoggedIn()
{
if(isset($_COOKIE["user"]))
{
return TRUE;
}
else
{
return FALSE;
}
}
It isn't the most beautiful solution, but it works!!!

Related

Laravel Unit Testing - add cookie to request?

I want to send a cookie with json POST:
public function testAccessCookie()
{
$response = $this->json('POST', route('publications'))->withCookie(Cookie::create('test'));
//some asserts
}
publications route has some middleware:
public function handle($request, Closure $next)
{
Log::debug('cookie', [$request->cookies]);
//cookie validation
return $next($request);
}
But while running testAccessCookie(), there is [null] inside log. No cookies attached.
What's wrong?
There is no such problem with real (in-browser) requests.
You can add cookies to calls in tests:
$cookies = ['test' => 'value'];
$response = $this->call('POST', route('publications'), [], $cookies);
See https://laravel.com/api/5.4/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.html#method_call
However you will run into a cookie encryption problem. You can temporarily disable cookies during testing with:
use Illuminate\Cookie\Middleware\EncryptCookies;
/**
* #param array|string $cookies
* #return $this
*/
protected function disableCookiesEncryption($name)
{
$this->app->resolving(EncryptCookies::class,
function ($object) use ($name)
{
$object->disableFor($name);
});
return $this;
}
Adding $this->disableCookiesEncryption('test'); at the start of the test.
You may need to add headers to specify a json response.
This should work in recent versions (Laravel 6):
Either:
$this->disableCookieEncryption();
or:
$cookies = ['test' => encrypt('value', false)];
$response = $this->call('POST', route('publications'), [], $cookies);
Since Laravel 5.2 you get the \App\Http\Middleware\EncryptCookies::class middleware defined by default in the web middleware group and it will set all unencrypted cookies to null.
Unfortunately all cookies you send with $request->call(), $request->get() and $request->post() in unit testing are usually unencrypted and nothing in the official documentation tells you they need to be encrypted.
If you don't want to call $request->disableCookieEncryption() everytime, as a permanent solution you can simply redefine the isDisabled() method in App\Http\Middleware\EncryptCookies.php to ignore cookies encryption during unit testing.
Here is the implementation I made for Laravel 6.x, it should work on earlier versions too.
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* #var array
*/
protected $except = [
//
];
public function isDisabled($name)
{
if (app()->runningUnitTests()) {
return true; // Disable cookies encryption/decryption during unit testing
}
return parent::isDisabled($name);
}
}

Symfony2 - redirect logged in users when entering anonymous areas

I created an action that handles redirection to respected areas depending on user's type and ROLE (trainee, company or university let's say). If user is not logged in, it redirects to homepage (anonymous area), and if logged in - to their profile pages. I use it in homepage and many other cases, for example, as sign up and login success redirection.
public function authorizationAction(Request $request)
{
$user = $this->getUser();
$authorizationChecker = $this->get('security.authorization_checker');
$request->cookies->remove('action');
if ($user) {
if ($user->getType() == 'company' && $authorizationChecker->isGranted('ROLE_COMPANY_GUEST')) {
/** #var Company $company */
$company = $user->getCompany();
if ($user->getState() == 'active' && $company->getState() == 'active') {
$response = $this->redirectToRoute('company');
} else {
$response = $this->redirectToRoute('company_profile');
}
} elseif ($user->getType() == 'university' && $authorizationChecker->isGranted('ROLE_UNIVERSITY_GUEST')) {
/** #var University $university */
$university = $user->getUniversity();
if ($user->getState() == 'active' && $university->getState() == 'active') {
$response = $this->redirectToRoute('university');
} else {
$response = $this->redirectToRoute('university_profile');
}
} elseif ($user->getType() == 'trainee' && $authorizationChecker->isGranted('ROLE_TRAINEE')) {
/** #var Trainee $trainee */
$trainee = $user->getTrainee();
if ($user->getState() == 'active' && $trainee->getState() == 'active') {
$response = $this->redirectToRoute('trainee');
} else {
$response = $this->redirectToRoute('trainee_profile');
}
} else {
$response = $this->redirectToRoute('homepage');
}
} else {
$response = $this->redirectToRoute('homepage');
}
return $response;
}
I have seen some examples recommending using symfony events (kernel.request) to handle it minimizing controller code. But in this case I will not be able to use this action as sign up and login success path.
I am not using FOS, because of lack of user customization. I prefer handling User my self.
Is my approach wrong and something to be worried about?
Some things that I am concerned:
Redirection count:
For example. I am logged in user and I go to homepage and am redirected to my action where I am checked whether I am logged in or not and depending on user type I am redirected to respected page.
public function indexAction(Request $request)
{
if ($this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
return $this->redirectToRoute('authorization');
}
// ...
}
Slowing website:
In the future I will be using this action in more pages and website will definatelly slow down everytime executing same code on each page.
I am not using FOS, because of lack of user customization. I prefer handling User my self.
Blockquote
You really shouldn't reinvent the wheel here but even if you want to your assessment that extending FOS is hard is wrong, take a look at this gist how many of us Symfony Devs extend FOS to enable social login via HWIOauthBundle. Extending it for other purposes is equally trivial.
Update Since OP's Comments Below
....
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use AppBundle\Form\Type\LoginType;
....
....
$login_form = $this->createForm(LoginType::class);
$login_form->handleRequest($request);
if (!$login_form->isValid()){
//throw error
}
$email = strip_tags($login_form->get('email')->getData());
$password = $login_form->get('password')->getData();
// Call the User Manager
$userManager = $this->get('fos_user.user_manager');
$user = $userManager->findUserByUsernameOrEmail($email);
if(!$user){
//throw error : User With Submitted Credentials Does Not Exist
}
$user_password = $user->getPassword();
$encoded_password = $this->get('security.password_encoder')->encodePassword($user, $password);
if($encoded_password != $user_password){
//throw error : Wrong Password, Please Check Your Details & Re-submit
}
// Successful query of username/email and password now we log in user
// Create new token
$token = new UsernamePasswordToken($user, $user->getPassword(), 'main', $user->getRoles());
// Login New User
$tokenStorage = $this->get('security.token_storage');
$tokenStorage->setToken($token);
// User now logged in
$user_id = $user->getId();
....
Redirection count:
In your case each redirection would cause at least 1 database query to verify user session, so yes many them could lead to a bad use and server experience. If you use FOSUserBundle with Doctrine's Second Level Cache, you can avoid this query everytime you call $this->getUser();
Slowing website:
This very small bit of code with hardly be you bottleneck at scale but for arguments sake let's assume it is. I would solve this problem by introducing client side sessions. A framework like Angular or my personal favorite Ember would allow you to store user sessions on the client so as not to even bother going back to your server all the time for such a menial task as checking authentication or roles. I would still advise you to keep some server side code for those cheeky users who want to poke holes in your code.
The Access Control section in the Symfony documentation might offer easier solutions to restrict access. In my time using Symfony I have always been able to use it for redirection and access control.

fosuserbundle ldap configuration for strange use case

I'm trying to create a fosuserbundle for a quite strange use case, which is mandatory requirement, so no space to diplomacy.
Use case is as follow:
users in a mongo db table populated by jms messages -no registration form
users log in by ldap
user record not created by ldap, after a successful login username is checked against mongodb document
Considering that ldap could successfully log in people that exhist in ldap but cannot access site (but login is still successful), what could be the best way to perform such authentication chain?
I was thinking about some possible options:
listen on interactive login event, but imho there's no way to modify an onSuccess event
create a custom AuthenticationListener to do another check inside onSuccess method
chain authentication using scheb two-factor bundle
any hint?
I've used Fr3DLdapBundle which can be incorporate with FOSUserBundle quite easily (I'm using the 2.0.x version, I have no idea if the previous ones will do the same or be as easy to set up).
In the LdapManager (by default) it creates a new user if one is not already on the database which is not what I wanted (and doesn't seem to be what you want) so I have added my own manager that checks for the presence of the user in the database and then deals with the accordingly.
use FR3D\LdapBundle\Ldap\LdapManager as BaseLdapManager;
.. Other use stuff ..
class LdapManager extends BaseLdapManager
{
protected $userRepository;
protected $usernameCanonicalizer;
public function __construct(
LdapDriverInterface $driver,
$userManager,
array $params,
ObjectRepository $userRepository,
CanonicalizerInterface $usernameCanonicalizer
) {
parent::__construct($driver, $userManager, $params);
$this->userRepository = $userRepository;
$this->usernameCanonicalizer = $usernameCanonicalizer;
}
/**
* {#inheritDoc}
*/
public function findUserBy(array $criteria)
{
$filter = $this->buildFilter($criteria);
$entries = $this->driver->search(
$this->params['baseDn'], $filter, $this->ldapAttributes
);
if ($entries['count'] > 1) {
throw new \Exception('This search can only return a single user');
}
if ($entries['count'] == 0) {
return false;
}
$uid = $entries[0]['uid'][0];
$usernameCanonical = $this->usernameCanonicalizer->canonicalize($uid);
$user = $this->userRepository->findOneBy(
array('usernameCanonical' => $usernameCanonical)
);
if (null === $user) {
throw new \Exception('Your account has yet to be set up. See Admin.');
}
return $user;
}

Check if a role is granted for a specific user in Symfony2 ACL

I want to check if a role is granted for a specific user in Symfony2 (not the logged user).
I know that I can check it for the logged user by:
$securityContext = $this->get('security.context');
if (false === $securityContext->isGranted('VIEW', $objectIdentity)) {
//do anything
}
but if I'm the logged user and I wand to check other user if isGranted ??
The "VIEW" is a permission, not a role.
The best way to check if a user has a right (be it a role or permission) would be to access the AccessDecisionManager. Something like:
$token = new UsernamePasswordToken($user, 'none', 'none', $user->getRoles());
$attributes = is_array($attributes) ? $attributes : array($attributes);
$this->get('security.access.decision_manager')->decide($token, $attributes, $object);
See original answer here: https://stackoverflow.com/a/22380765/971254 for details.
You just need to create a custom security context that will take a user object and generate a UserSecurityIdentity out of it. Here are the steps:
Create a new service in YourApp/AppBundle/Resources/config.yml
yourapp.security_context:
class: YourApp\AppBundle\Security\Core\SecurityContext
arguments: [ #security.acl.provider ]
Create a custom Security Context Class like this:
namespace YourApp\AppBundle\Security\Core;
use Symfony\Component\Security\Acl\Model\MutableAclProviderInterface;
use Symfony\Component\Security\Acl\Domain\ObjectIdentity;
use Symfony\Component\Security\Acl\Domain\UserSecurityIdentity;
use Symfony\Component\Security\Acl\Permission\MaskBuilder;
use Symfony\Component\Security\Acl\Exception\AclNotFoundException;
use Symfony\Component\Security\Acl\Exception\NoAceFoundException;
use YourApp\AppBundle\Document\User;
/**
* Allows ACL checking against a specific user object (regardless of whether that user is logged in or not)
*
*/
class SecurityContext
{
public function __construct(MutableAclProviderInterface $aclProvider)
{
$this->aclProvider = $aclProvider;
}
public function isGranted($mask, $object, User $user)
{
$objectIdentity = ObjectIdentity::fromDomainObject($object);
$securityIdentity = UserSecurityIdentity::fromAccount($user);
try {
$acl = $this->aclProvider->findAcl($objectIdentity, array($securityIdentity));
} catch (AclNotFoundException $e) {
return false;
}
if (!is_int($mask)) {
$builder = new MaskBuilder;
$builder->add($mask);
$mask = $builder->get();
}
try {
return $acl->isGranted(array($mask), array($securityIdentity), false);
} catch (NoAceFoundException $e) {
return false;
}
}
}
Now you can inject that service where needed, or use it from a controller like this:
$someUser = $this->findSomeUserFromYourDatabase();
if ($this->get('yourapp.security_context')->isGranted('VIEW', $article, $someUser) {
// ...
}
Checking roles for another user can not be done via the SecurityContext as this will always hold the current user's session token. Your task can be achieved for example via the getRoles method, if the user you need to check implements the UserInterface.
$otherUser = $this->get('doctrine')->... // fetch the user
if( $otherUser instanceof \Symfony\Component\Security\Core\User\UserInterface )
{
$roles = $otherUser->getRoles();
// your role could be VIEW or ROLE_VIEW, check the $roles array above.
if ( in_array( 'VIEW' , $roles ) )
{
// do something else
}
}
If your user entity implement the FosUserBundle UserInterFace, that has a dedicated method hasRole. In that case you could use a one-liner:
$otherUser = $this->get('doctrine')->... // fetch the user
if( $otherUser instanceof \FOS\UserBundle\Model\UserInterface )
{
// your role could be VIEW or ROLE_VIEW, check the proper role names
if ( $otherUser->hasRole( 'VIEW' ) )
{
// do something else
}
}

Set permission for creating nodes in code

Can I set permissions to allow authenticated users to add nodes of a custom type? I need to do that in module i'm trying to create. As I can see hook_permission it's actually just for creating new permissions.
If your module name is say mymodule, then defien a your "create node" permission in hook_permission. Then implement hook_node_access to check and return permission for the content type you have implemented.
SAMPLE CODE.
NB: It won't work out of the box, you got to replace your module name, permission name and the content type name to get it to work. And don't forget to clear your cache, TWICE!.
/**
* Implements hook_permission().
*
*/
function mymodule_permission() {
// define your add permission.
// Naming of "array key" is important. We use that later.
return array(
'YOUR CONTENT NAME: add' => array(
'title' => t('Add Project Management Team'),
),
);
}
/**
* Implements hook_node_access().
*/
function mymodule_node_access($node, $op, $account = NULL) {
$type = is_string($node) ? $node : $node->type;
// make sure you are responding to content type defined by your module only.
if ($type == 'YOUR_CONTENT_TYPE_NAME_HERE') {
// If no account is specified, assume that the check is against the current logged in user
if (is_null($account)) {
global $user;
$account = $user;
}
if ($op == 'create' AND user_access('YOUR CONTENT NAME: add', $account)) {
return NODE_ACCESS_ALLOW;
}
}
return NODE_ACCESS_IGNORE;
}
References:
hook_permission().
hook_node_access().

Resources