Symfony: проверять подлинность пользователей на сервере LDAP, но разрешать вход только в том случае, если их имя пользователя находится в пользовательской таблице БД
Сначала краткое объяснение моей задачи. Я использую Symfony 2.8 и у меня есть приложение с REST API и SonataAdminBundle. Посетители веб-сайта могут публиковать определенные данные через API REST, которые сохраняются в базе данных. Определенная группа сотрудников должна управлять этими данными через область администратора.
Доступ к административной области должен быть защищен с помощью имени пользователя и пароля. Есть сущность Employee
с собственностью username
, но без пароля. Аутентификация должна выполняться на сервере LDAP, но доступ к административной области должен быть ограничен только теми сотрудниками, которые присутствуют в объекте. Employee
т.е. ссылающаяся таблица базы данных.
Для аутентификации LDAP я использую новый компонент LDAP в Symfony 2.8.
Помимо этого должна быть учетная запись администратора как in_memory
пользователь.
Вот что у меня сейчас:
app/config/services.yml
services:
app.ldap:
class: Symfony\Component\Ldap\LdapClient
arguments: ["ldaps://ldap.uni-rostock.de"]
app.db_user_provider:
class: AppBundle\Security\DbUserProvider
arguments: ["@doctrine.orm.entity_manager"]
app/config/security.yml
security:
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
providers:
chain_provider:
chain:
providers: [db_user, app_users]
in_memory:
memory:
users:
admin: { password: adminpass, roles: 'ROLE_ADMIN' }
app_users:
ldap:
service: app.ldap
base_dn: ou=people,o=uni-rostock,c=de
search_dn: uid=testuser,ou=people,o=uni-rostock,c=de
search_password: testpass
filter: (uid={username})
default_roles: ROLE_USER
db_user:
id: app.db_user_provider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
anonymous: true
pattern: ^/
form_login_ldap:
provider: chain_provider
service: app.ldap
dn_string: "uid={username},ou=people,o=uni-rostock,c=de"
check_path: /login_check
login_path: /login
form_login:
provider: in_memory
check_path: /login_check
login_path: /login
logout:
path: /logout
target: /
access_control:
- { path: ^/admin, roles: ROLE_USER }
encoders:
Symfony\Component\Security\Core\User\User: plaintext
AppBundle\Entity\Employee: bcrypt
src/AppBundle/Entity/Employee.php
namespace AppBundle\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Doctrine\ORM\Mapping as ORM;
class Employee implements UserInterface, EquatableInterface
{
// other properties
private $username;
// getters and setters for the other properties
public function getUsername()
{
return $this->username;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function getPassword()
{
return null;
}
public function getSalt()
{
return null;
}
public function eraseCredentials()
{
}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof Employee) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
}
src/AppBundle/Security/DbUserProvider.php
<?php
namespace AppBundle\Security;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Doctrine\ORM\EntityManager;
use AppBundle\Entity\Employee;
class DbUserProvider implements UserProviderInterface
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function loadUserByUsername($username)
{
$repository = $this->em->getRepository('AppBundle:Employee');
$user = $repository->findOneByUsername($username);
if ($user) {
return new Employee();
}
throw new UsernameNotFoundException(
sprintf('Username "%s" does not exist.', $username)
);
}
public function refreshUser(UserInterface $user)
{
if (!$user instanceof Employee) {
throw new UnsupportedUserException(
sprintf('Instances of "%s" are not supported.', get_class($user))
);
}
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === 'AppBundle\Entity\Employee';
}
}
Аутентификация по LDAP работает как шарм. Когда сотрудник из базы данных пытается войти в систему, он / она перенаправляется на домашнюю страницу ('/'), и вход в систему не удается. Все остальные пользователи, которых нет в базе данных, могут войти без проблем.
Это как раз то, что я хочу!
Если я подключу провайдеров, как это:
chain_provider:
chain:
providers: [app_users, db_user]
тогда метод loadUserByUsername
даже не вызывается, и все пользователи могут войти, те, кто в базе данных и те, кто не.
in_memory
Пользователь admin может войти без проблем в любом случае.
Я ценю любую помощь. Если кто-то думает, что весь мой подход плох и знает лучший способ, пожалуйста, не жалейте критиков.
Я знаю, что есть FOSUserBundle и SonataUserBundle, но я бы предпочел пользовательского провайдера, поскольку я не хочу раздувать сущность Employee, поскольку мне действительно не нужны все эти свойства, такие как пароль, соль, isLocked и т. Д. Не думаю, что настройка SonataUserBundle в моем конкретном случае будет намного проще. Если вы все еще думаете, что есть более элегантный способ выполнить мою задачу с этими двумя связками, я буду благодарен за хороший совет.
1 ответ
Вы можете настроить user-checker для брандмауэра, ваш db-провайдер на самом деле не является user-провайдером, потому что у него нет всей информации, необходимой для аутентификации пользователя (например, пароля), так что я бы сделал, я бы удалил провайдер пользователя БД и вместо него добавьте пользовательскую проверку, основная идея пользовательской проверки заключается в добавлении дополнительных проверок в процессе аутентификации, в вашем случае нам нужно проверить, находится ли пользователь в таблице сотрудников или нет
вам нужно сделать три вещи, чтобы реализовать это
реализовать UserCheckerInterface Symfony\Component\Security\Core\User\UserCheckerInterface
проверить, находится ли пользователь в таблице сотрудников или нет в checkPostAuth()
метод
выставить ваш новый пользователь-контролер как сервис
services:
app.employee_user_checker:
class: Path\To\Class\EmployeeUserChecker
измените конфигурацию брандмауэра, чтобы использовать новую проверку пользователей
security:
firewalls:
admin:
pattern: ^/admin
user_checker: app.employee_user_checker
#...