Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RTM] Use Symfony Security for authentication purposes in Contao #685

Merged
merged 170 commits into from
Dec 12, 2017

Conversation

bytehead
Copy link
Member

@bytehead bytehead commented Jan 24, 2017

This is a working draft to replace the current simple_preauth mechanism to login backend users and frontend members with the form_login from the Symfony Security Component.

Please review and mention all missed topics.

ToDos:

  • Keep redirect on login
  • Implement role switcher / impersonate any BackendUser
  • Session Life Time (framework.session.cookie_lifetime)
  • Rename firewalls (prefix with contao_)
  • Implement Security for FrontendUser
  • Replace postAuthenticate
  • Replace postLogin
  • Replace checkCredentials (needs a custom AuthenticationProvider)
  • Replace importUser
  • Replace postLogout
  • Replace current accountLocked stuff
  • Test Frontend Preview
  • Write tests
  • Check Code style
  • Deprecate all old hooks
  • Provide Event for legacy postAuthenticate hook
  • Provide Event for legacy checkCredentials hook
  • Provide Event for legacy importUser hook
  • Provide Event for legacy postLogout hook
  • Show backend login errors

Fixes needed:

  • Backend login redirect: Simple test: login, go somewhere, delete your session cookie.
  • UserProvider refreshUser without credentials
  • Provide exception messages
  • Replace usage of static legacy class methods (e.g. Config, Environment)
  • Call importUser only on POST request
  • Additional check with UserChecker in Frontend
  • Implement the EquatableInterface on User to compare the token object and DB object
  • on BackendUser::getInstance check if there is a valid _security_contao_backend token in the actual session to restore the user from
  • on FrontendUser::getInstance check if there is a valid _security_contao_frontend token in the actual session to restore the user from

Nice to have (possibly future PRs):

  • use DoctrineTokenProvider for persistent remember me tokens

Possible new features (future PRs):

  • Allow login also with the email address (not only username)
  • Allow some user action via command like activate, change-password or similar

@bytehead bytehead mentioned this pull request Jan 24, 2017
7 tasks
@bytehead bytehead force-pushed the feature/symfony-authentication-2 branch from bf65103 to b215304 Compare January 24, 2017 20:47
@leofeyer leofeyer added this to the 4.4.0 milestone Jan 24, 2017
@bytehead bytehead force-pushed the feature/symfony-authentication-2 branch from 8ac4255 to f3c619d Compare January 24, 2017 23:47
@leofeyer leofeyer removed this from the 4.4.0 milestone Jan 25, 2017
@leofeyer leofeyer changed the title [WIP] Feature/symfony authentication [refactored] [WIP] Feature/symfony authentication refactored Jan 25, 2017
@bytehead bytehead changed the title [WIP] Feature/symfony authentication refactored [RFC] Feature/symfony authentication refactored Jan 25, 2017
@leofeyer leofeyer force-pushed the develop branch 2 times, most recently from c8f2f0d to 09536d6 Compare January 27, 2017 16:49
@bytehead bytehead changed the title [RFC] Feature/symfony authentication refactored [WIP] Feature/symfony authentication refactored Feb 3, 2017
*/
public function switchUser($row, $href, $label, $title, $icon)
{
@trigger_error('Using tl_user->switchUser() has been deprecated and will no longer work in Contao 5.0. Use the switch_user_button_generator service instead', E_USER_DEPRECATED);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest of this method should be removed or replaces by the new switch user stuff.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

/** @var BackendUser $targetUser */
$targetUser = $event->getTargetUser();

$this->logger->info(sprintf("User %s has switched to user %s.", $user->username, $targetUser->username));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only purpose of this listener is to log the action? If that's a replacement of an existing log, we should log with a ContaoContext to make it appear in the backend log.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be the replacement of the existing log, but I didn't get it how it's done to make it appear in the backend.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$logger->log($level, $strText, array('contao' => new ContaoContext($strFunction, $strCategory)));

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, thank you! I'll update it later.

- "@contao.framework"

contao.security.switch_user_button_generator:
class: Contao\CoreBundle\Security\User\SwitchUserButtonGenerator
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me this is an event (callback) listener and should be in the according namespace.


if ($authorizationChecker->isGranted('ROLE_PREVIOUS_ADMIN')) {
$logoutLink = $router->generate('contao_backend', [
'_switch_user' => '_exit',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a new feature, right? Previously a login just logged out whatever user was currently in. Might be confusing if we don't change the logout label to "switch back to X".

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point :)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current label is Close the current session and thus not wrong in my opinion.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed in e2bed63.

$stmt->execute();

if (0 === $stmt->rowCount()) {
throw new UserNotFoundException('Invalid user ID' . $row['id']);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should never happen, so I would simply return empty string (remove action) in this case and not add a new exception for a never-happening case :)

'_switch_user' => $user->username
]);

return $this->twig->render('@ContaoCore/Backend/switch_user.html.twig', [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a twig template for this? Generally good, but there are so many places where we currently don't…

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes IMHO we need exactly that. Otherwise we will end up like the DCA classes are right now with template code in the logic parts. And we have to start using (twig) templates at some point.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but then this should be a general button template, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea. Should be discussed with the others probably?

@leofeyer leofeyer force-pushed the feature/symfony-authentication-2 branch from 5cae2cf to 7e9ecad Compare December 11, 2017 14:51
@leofeyer leofeyer force-pushed the feature/symfony-authentication-2 branch from 0424c9f to 88860b8 Compare December 11, 2017 16:01
*
* @param DataContainer $dc
*/
public function checkRemoveSession($dc)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No BC break if you remove a public method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is a BC break for sure. The question is whether out BC promise includes DCA files, which I think they don't.

@@ -1 +0,0 @@
<a href="{{ url }}" title="{{ title }}">{{ image | raw }}</a>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

'title' => $title,
'image' => Image::getHtml($icon, $label),
]);
return sprintf('<a href="%s" title="%s">%s</a>', $url, $title, Image::getHtml($icon, $label));
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm totally against to remove the template. Now we have again html somewhere in the logic.

@leofeyer
Copy link
Member

The PR is almost ready to merge. There are two open issues:

  1. Do we want to log impersonations?
  2. Do we need to change any private variables or methods to protected so people can overwrite certain things?

Regarding 1.: We are logging "switch user" actions but not "log in as member" actions. Is this ok or should we log both or none?

@bytehead
Copy link
Member Author

  1. I would like to have impersonations logged. I'm fine to log frontend impersonations as well.
  2. I'm against to make just everything private. Makes it unnecessary hard to extend existing stuff.

$objTemplate->user = \Input::post('user');
/** @var User $user */
$user = $token->getUser();
$objUser = \MemberModel::findByUsername($user->getUsername());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also something that makes me wonder. Shouldn't $token->getUser() return the user object already? Why are we loading the member model?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getUser() on the token can have a fully loaded user instance, but you cannot be sure.
Normally it holds only the unserialized data from the session storage.

@@ -13,16 +13,19 @@
<?php endif; ?>
<input type="hidden" name="FORM_SUBMIT" value="<?= $this->formId ?>">
<input type="hidden" name="REQUEST_TOKEN" value="{{request_token}}">
<?php if ($this->targetPath): ?>
<input type="hidden" name="<?= $this->targetName ?>" value="<?= $this->targetPath ?>">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also wonder if this is prone to manipulation? @ausi /cc

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should check, if it's an existing page from the installation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well you can only manipulate it through the web inspector. Which means the redirect target after login is manipulated. Can't really see an issue in that, it does not send any data to that URL.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

htmlspecialchars() is missing for $this->targetPath, either in the template or in ModuleLogin. As it is now it’s vulnerable to XSS.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 975070e.

@leofeyer leofeyer force-pushed the feature/symfony-authentication-2 branch from 1c252ad to 94916b8 Compare December 12, 2017 13:02
@leofeyer leofeyer merged commit 01a6845 into contao:develop Dec 12, 2017
@leofeyer
Copy link
Member

Thank you, big time @bytehead.

@bytehead bytehead deleted the feature/symfony-authentication-2 branch March 6, 2018 14:36
richardhj added a commit to richardhj/contao that referenced this pull request Nov 3, 2018
Functionality broke with contao/core-bundle#685 as User->logout uses a RedirectResponseException.
richardhj added a commit to richardhj/contao that referenced this pull request Nov 3, 2018
Functionality broke with contao/core-bundle#685 as User->logout uses a RedirectResponseException.
leofeyer pushed a commit to contao/contao that referenced this pull request Nov 12, 2018
Description
-----------

Functionality broke with contao/core-bundle#685 as `User->logout()` uses a RedirectResponseException.
Fixes #93.

Commits
-------

14f6a80 Respect jump to on close account.
da85783 Use System::getContainer() instead of Controller::getContainer()
leofeyer pushed a commit that referenced this pull request Nov 12, 2018
Description
-----------

Functionality broke with #685 as `User->logout()` uses a RedirectResponseException.
Fixes #93.

Commits
-------

14f6a808 Respect jump to on close account.
da85783f Use System::getContainer() instead of Controller::getContainer()
leofeyer added a commit that referenced this pull request Aug 30, 2019
Description
-----------

This is a follow-up on #657, #677 and #682. It implements the new template loading by looking up the keys in `TL_CTE`, `TL_FFL` and `FE_MOD` as discussed in Mumble on August 29th.

Commits
-------

de3089b7 Hide mod_article_list when searching for custom mod_article templates
7b6ce852 Replace "root template" with "bundle template"
1ee232da Support passing an additional mapper array as second argument
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants