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

[AdminBundle] New authentication bugfixes and improvements #2940

Merged
merged 1 commit into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 40 additions & 7 deletions src/Kunstmaan/AdminBundle/Entity/BaseUser.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,18 @@

namespace Kunstmaan\AdminBundle\Entity;

use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use FOS\UserBundle\Model\GroupInterface;
use Kunstmaan\AdminBundle\Validator\Constraints\PasswordRestrictions;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface as BaseUserInterface;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Mapping\ClassMetadata;

abstract class BaseUser implements UserInterface
abstract class BaseUser implements UserInterface, EquatableInterface
{
/**
* @ORM\Id
Expand Down Expand Up @@ -295,7 +296,7 @@ public function isAccountNonLocked()
return $this->isEnabled();
}

public function getEmail(): string
public function getEmail(): ?string
{
return $this->email;
}
Expand All @@ -307,7 +308,7 @@ public function setEmail($email)
return $this;
}

public function getPassword(): string
public function getPassword(): ?string
{
return $this->password;
}
Expand Down Expand Up @@ -397,7 +398,7 @@ public function isEnabled()
/**
* @return string The username
*/
public function getUsername(): string
public function getUsername(): ?string
{
return $this->username;
}
Expand Down Expand Up @@ -570,7 +571,7 @@ public function setConfirmationToken($confirmationToken)
return $this;
}

public function setPasswordRequestedAt(DateTime $date = null)
public function setPasswordRequestedAt(\DateTime $date = null)
{
//TODO: check if this propery is usefull?
// NEXT_MAJOR remove method
Expand All @@ -584,7 +585,7 @@ public function getLastLogin()
return $this->lastLogin;
}

public function setLastLogin(?DateTime $lastLogin = null)
public function setLastLogin(?\DateTime $lastLogin = null)
{
$this->lastLogin = $lastLogin;

Expand Down Expand Up @@ -636,6 +637,38 @@ public function isPasswordRequestNonExpired($ttl)
return false;
}

/**
* {@inheritdoc}
*/
public function isEqualTo(BaseUserInterface $user): bool
{
if (!$user instanceof self) {
return false;
}

if ($this->id !== $user->getId()) {
return false;
}

if ($this->password !== $user->getPassword()) {
return false;
}

if ($this->salt !== $user->getSalt()) {
return false;
}

if ($this->username !== $user->getUsername()) {
return false;
}

if ($this->isEnabled() !== $user->isEnabled()) {
return false;
}

return true;
}

/**
* {@inheritdoc}
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Kunstmaan/AdminBundle/Resources/config/authentication.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,5 @@ services:
arguments: [ '@kunstmaan_admin.user_manager' ]
tags:
- { name: console.command }

Kunstmaan\AdminBundle\Security\UserChecker: ~
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<link rel="stylesheet" href="{{ asset('bundles/kunstmaanadmin/default-theme/ckeditor/plugins/notification/css/notification.css') }}">
<link rel="stylesheet" href="{{ asset('bundles/kunstmaanadmin/default-theme/ckeditor/plugins/wordcount/css/wordcount.css') }}">
<link rel="stylesheet" href="{{ asset('bundles/kunstmaanadmin/css/style.css') }}">
<link rel="stylesheet" href="{{ asset('bundles/kunstmaanadmin/cssnext/style.css') }}">

<!-- Customizable Brand Color -->
{% if titlecolor is not defined or titlecolor is empty %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<!-- JS -->
{% include "@KunstmaanAdmin/Default/_js_header.html.twig" %}

{# NEXT_MAJOR: Remove #}
{% if google_signin_enabled() %}
<script src="https://apis.google.com/js/platform.js" async defer></script>
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,76 +1,102 @@
{% extends '@KunstmaanAdmin/layout.html.twig' %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="robots" content="noindex, nofollow">

{% block extrabodyclasses %}login app-login-page{% endblock %}
<title>{% block title %}{{ websitetitle | trans }}{% endblock %}</title>

{% block body %}
<div class="app-login">
<div class="app-login__title-holder">
<header class="app-login__header">
{% if login_logo is defined %}
<img class="app-login__brand-logo" src="{{ asset(login_logo) }}" alt="{{ websitetitle | trans }}">
{% endif %}
<h1 class="app-login__title">
{{ websitetitle | trans }}
</h1>
</header>
<!-- Favicons -->
{% include "@KunstmaanAdmin/Default/_favicons.html.twig" %}

<figure class="app-login__image-holder">
{% set login_image_url = asset('/bundles/kunstmaanadmin/default-theme/img/kunstmaan/default-login-bg.jpg') %}
{% if custom_login_image_url is defined %}
{% set login_image_url = asset(custom_login_image_url) %}
{% endif %}
<img src="{{ login_image_url }}" alt="">
</figure>
</div>
<aside class="app-login__aside">
<div class="app-login__aside__content">
<div class="app-login__alert-holder">
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
<div class="alert alert-warning">
<strong>{{ 'form.warning' | trans }}: </strong>
{{ 'security.general.logged_in_as'|trans({'%username%': app.user.username}) }}
<a href="{{ path('kunstmaan_admin_logout') }}" class="btn btn-warning alert__action">
{{ 'security.general.logout'|trans }}
</a>
</div>
<!-- Styles -->
{% include "@KunstmaanAdmin/Default/_css.html.twig" %}
<link rel="stylesheet" href="{{ asset('bundles/kunstmaanadmin/cssnext/style.css') }}">

<!-- Extra CSS -->
{% block extracss %}{% endblock %}

<!-- JS -->
{% include "@KunstmaanAdmin/Default/_js_header.html.twig" %}

{% if google_signin_enabled() %}
<script src="https://apis.google.com/js/platform.js" async defer></script>
{% endif %}
</head>

<body class="app login app-login-page">
{% block body %}
<div class="app-login">
<div class="app-login__title-holder">
<header class="app-login__header">
{% if login_logo is defined %}
<img class="app-login__brand-logo" src="{{ asset(login_logo) }}" alt="{{ websitetitle | trans }}">
{% endif %}
<h1 class="app-login__title">
{{ websitetitle | trans }}
</h1>
</header>

{% for key, messages in app.session.flashbag.all() %}
<div class="alert alert-{{ key }} {{ key }}">
<button type="button" class="close" data-dismiss="alert">
<i class="fa fa-times"></i>
</button>
{% for message in messages %}
{{ message|trans }}
{% endfor %}
</div>
{% endfor %}
</div>
{% block login_content %}{% endblock %}
<figure class="app-login__image-holder">
{% set login_image_url = asset('/bundles/kunstmaanadmin/default-theme/img/kunstmaan/default-login-bg.jpg') %}
{% if custom_login_image_url is defined %}
{% set login_image_url = asset(custom_login_image_url) %}
{% endif %}
<img src="{{ login_image_url }}" alt="">
</figure>
</div>
<aside class="app-login__aside">
<div class="app-login__aside__content">
<div class="app-login__alert-holder">
{% if is_granted("IS_AUTHENTICATED_REMEMBERED") %}
<div class="alert alert-warning">
<strong>{{ 'form.warning' | trans }}: </strong>
{{ 'security.general.logged_in_as'|trans({'%username%': app.user.username}) }}
<a href="{{ path('kunstmaan_admin_logout') }}" class="btn btn-warning alert__action">
{{ 'security.general.logout'|trans }}
</a>
</div>
{% endif %}

{% block login_footer %}
<footer class="app-login__footer">
<p class="app-login__footer__text">
<img src="{{ asset('/bundles/kunstmaanadmin/default-theme/img/kunstmaan/logo-cms-color.svg') }}" class="app-login__footer__logo">
Powered by <a href="https://cms.kunstmaan.be" target="_blank" rel="noopener">Kunstmaan CMS</a>
</p>
</footer>
{% endblock %}
</aside>
</div>
{% for key, messages in app.session.flashbag.all() %}
<div class="alert alert-{{ key }} {{ key }}">
<button type="button" class="close" data-dismiss="alert">
<i class="fa fa-times"></i>
</button>
{% for message in messages %}
{{ message|trans }}
{% endfor %}
</div>
{% endfor %}
</div>
{% block login_content %}{% endblock %}
</div>

{% block login_footer %}
<footer class="app-login__footer">
<p class="app-login__footer__text">
<img src="{{ asset('/bundles/kunstmaanadmin/default-theme/img/kunstmaan/logo-cms-color.svg') }}" width="20px" height="20px" class="app-login__footer__logo">
Powered by <a href="https://cms.kunstmaan.be" target="_blank" rel="noopener">Kunstmaan CMS</a>
</p>
</footer>
{% endblock %}
</aside>
</div>

{% block footer %}{% endblock %}
{% endblock %}
{% block footer %}{% endblock %}
{% endblock %}

{% block admin_link %}
{# NEXT_MAJOR remove empty block #}
{% endblock %}
{% block admin_link %}
{# NEXT_MAJOR remove empty block #}
{% endblock %}

{% block forgot_password_link %}
{# NEXT_MAJOR remove empty block #}
{% endblock %}
{% block forgot_password_link %}
{# NEXT_MAJOR remove empty block #}
{% endblock %}

{% block fos_user_content %}
{# NEXT_MAJOR remove empty block #}
{% endblock %}
{% block fos_user_content %}
{# NEXT_MAJOR remove empty block #}
{% endblock %}
</body>
Original file line number Diff line number Diff line change
Expand Up @@ -22,33 +22,33 @@
<label for="username" class="form-control-label">
{{ 'security.login.username'|trans }}
</label>
<input class="form-control" id="username" type="text" name="_username" value="{{ last_username }}" placeholder="{{ 'security.login.username'|trans }}" autocomplete="username" required autofocus>
<input class="form-control" id="username" type="text" name="_username" value="{{ last_username }}" placeholder="{{ 'security.login.username'|trans }}" autocomplete="username" required autofocus tabindex="1">
</div>

<div class="form-group">
<label for="password" class="form-control-label">
{{ 'security.login.password'|trans }}
</label>
<input class="form-control" id="password" type="password" name="_password" value="" placeholder="{{ 'security.login.password'|trans }}" autocomplete="current-password" required>
<input class="form-control" id="password" type="password" name="_password" value="" placeholder="{{ 'security.login.password'|trans }}" autocomplete="current-password" required tabindex="2">
</div>

<div class="app-login__form__alt">
<div class="app-login__form__alt__remember-me">
<label class="checkbox login__form__remember-me__check">
<input type="checkbox" id="remember_me" name="_remember_me" checked>
<input type="checkbox" id="remember_me" name="_remember_me" checked tabindex="3">
{{ 'security.login.remember_me' | trans }}
</label>
</div>
<div class="app-login__form__alt__forgot-pw">
<a href="{{ path('kunstmaan_admin_reset_password') }}" class="login__form__forgot-pw__link">
<a href="{{ path('kunstmaan_admin_reset_password') }}" class="login__form__forgot-pw__link" tabindex="5">
{{ 'kuma_admin.login.forgot_password'|trans }}
</a>
</div>
</div>
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">

<div class="form-group app-login__form__submit-holder">
<button type="submit" id="_submit" name="_submit" class="btn btn--md btn--inverted">
<button type="submit" id="_submit" name="_submit" class="btn btn--md btn--inverted" tabindex="4">
{{ 'security.login.submit'|trans }}
</button>
</div>
Expand Down
43 changes: 43 additions & 0 deletions src/Kunstmaan/AdminBundle/Security/UserChecker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Kunstmaan\AdminBundle\Security;

use Symfony\Component\Security\Core\Exception\AccountExpiredException;
use Symfony\Component\Security\Core\Exception\CredentialsExpiredException;
use Symfony\Component\Security\Core\Exception\DisabledException;
use Symfony\Component\Security\Core\Exception\LockedException;
use Symfony\Component\Security\Core\User\UserCheckerInterface;
use Symfony\Component\Security\Core\User\UserInterface;

final class UserChecker implements UserCheckerInterface
{
public function checkPreAuth(UserInterface $user)
{
if (!$user->isAccountNonLocked()) {
$ex = new LockedException('User account is locked.');
$ex->setUser($user);
throw $ex;
}

if (!$user->isEnabled()) {
$ex = new DisabledException('User account is disabled.');
$ex->setUser($user);
throw $ex;
}

if (!$user->isAccountNonExpired()) {
$ex = new AccountExpiredException('User account has expired.');
$ex->setUser($user);
throw $ex;
}
}

public function checkPostAuth(UserInterface $user)
{
if (!$user->isCredentialsNonExpired()) {
$ex = new CredentialsExpiredException('User credentials have expired.');
$ex->setUser($user);
throw $ex;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ security:
firewalls:
main:
pattern: .*
user_checker: Kunstmaan\AdminBundle\Security\UserChecker
form_login:
login_path: kunstmaan_admin_login
check_path: kunstmaan_admin_login
Expand Down