WIP: Saving Local Branch

This commit is contained in:
R. Eric Wheeler 2017-02-13 09:56:18 -08:00
parent e46f6e0810
commit a7d172a937
23 changed files with 953 additions and 137 deletions

View File

@ -30,7 +30,11 @@ use Monolog\{
Handler\StreamHandler,
Logger
};
use Sikofitt\App\Controller\DefaultController;
use Sikofitt\App\Entity\User;
use Sikofitt\App\Traits\EntityManagerTrait;
use Sikofitt\App\Traits\FlashTrait;
use Sikofitt\Security\MySqlUserProvider;
use Silex\Application;
use Silex\Application\{
FormTrait,
@ -72,6 +76,7 @@ use Symfony\Component\Translation\Translator;
*/
class Kernel extends Application
{
use EntityManagerTrait;
use FlashTrait;
use FormTrait;
use MonologTrait;
@ -81,6 +86,7 @@ class Kernel extends Application
use TwigTrait;
use UrlGeneratorTrait;
/**
* Kernel constructor.
*
@ -95,13 +101,19 @@ class Kernel extends Application
if (true === $debug) {
$this->setDebug();
}
$this->setUpProviders();
$this->setUpDatabase();
$this->setUpView();
$this->setUpLogger();
$this->setUpMailer();
}
public function setUpRoutes(\Kernel $app)
{
$app->match('/login', DefaultController::class.'::loginAction')
->method('GET|POST');
}
/**
* @param array $values
*
@ -266,8 +278,6 @@ class Kernel extends Application
* Closure supports \Twig_Environment and Silex\Application as a second
* parameter, but we never use Silex\Application so we leave it out.
*/
$r = new \Symfony\Component\HttpFoundation\RequestStack();
$this->extend('twig', function (\Twig_Environment $twig) {
$twig->addGlobal('session', $this['session']);
$twig->addExtension(new TranslationExtension(new Translator('en')));
@ -281,19 +291,57 @@ class Kernel extends Application
*/
protected function setUpProviders()
{
/*$this['app.mysql_authenticator'] = function($app) {
return new Sikofitt\Security\MysqlAuthenticator($app['security.encoder_factory'], $app->getEntityManager());
};
$this['security.firewalls'] = array(
'login' => [
'pattern' => '^/login$',
'anonymous' => true,
],
'secured' => [
'pattern' => '^/rsvp$',
'guard' => [
'authenticators' => [
'app.mysql_authenticator',
],
'form' => [
'login_path' => '/login',
'check_path' => '/login',
]
],
'users' => $this['users'] = function() {
return new MySqlUserProvider($this['orm.em']);
},
],
// configure where your users come from. Hardcode them, or load them from somewhere
// http://silex.sensiolabs.org/doc/providers/security.html#defining-a-custom-user-provider
// 'anonymous' => true
);*/
$this['protected_pages'] = function() {
return [
'gallery',
'rsvp/update'
];
};
$this
->register(new CsrfServiceProvider())
->register(new FormServiceProvider())
->register(new SecurityServiceProvider(), [
'security.firewalls' => [
'admin' => [
'pattern' => '^/admin',
'http' => true,
],
],
])
//->register(new SecurityServiceProvider())
;
$this->extend('form.extensions', function ($extensions) {
return $extensions;
});

View File

@ -1,4 +1,6 @@
{% if form is defined %}
{% form_theme form with [_self, 'form_errors.html.twig'] %}
{% endif %}
<!DOCTYPE html>
<html lang="en">
<head>

View File

@ -0,0 +1,22 @@
{# form_errors.html.twig #}
{% block form_errors %}
{% spaceless %}
{% if errors|length > 0 %}
{% if compound %}
<div class="uk-alert-danger" uk-alert>
<a href="#" class="uk-close"></a>
{% for error in errors %}
<p>{{ error.message }}</p>
{% endfor %}
</div>
{% else %}
<div class="uk-alert-danger" uk-alert>
<a href="#" class="uk-text-danger uk-alert-close" uk-close></a>
{% set error = errors|first %}
<p>{{ error.message }}</p>
</div>
{% endif %}
{% endif %}
{% endspaceless %}
{% endblock form_errors %}

38
app/views/login.html.twig Normal file
View File

@ -0,0 +1,38 @@
{% extends 'base.html.twig' %}
{% block body %}
{{ form_start(form) }}
<fieldset class="uk-fieldset">
<legend class="uk-legend">Login</legend>
<div class="uk-margin uk-width-1-1@m">
{{ form_errors(form.email_username) }}
{{ form_errors(form.password) }}
</div>
<div class="uk-margin">
{{ form_label(form.email_username) }}
<div class="uk-inline uk-width-2-3@m">
<span class="uk-form-icon" uk-icon="icon: user"></span>
{{ form_widget(form.email_username) }}
</div>
</div>
<div class="uk-margin">
{{ form_label(form.password) }}
<div class="uk-inline uk-width-2-3@m">
<span class="uk-form-icon " uk-icon="icon: lock"></span>
{{ form_widget(form.password) }}
</div>
</div>
<div class="uk-margin">
{{ form_row(form.submit) }}
</div>
<div class="uk-form-controls uk-hidden">
{{ form_rest(form) }}
</div>
</fieldset>
{{ form_end(form) }}
{% endblock %}

View File

@ -0,0 +1,39 @@
{% extends 'base.html.twig' %}
{% block body %}
{% if token.valid == false %}
<h3>
Sorry your token ({{ token.value }}) is invalid.
</h3>
<p>
Please see <a href="{{ url('rsvp_password_reset') }}">{{ url('rsvp_password_reset') }}</a>.
</p>
{% else %}
{{ form_start(form) }}
<fieldset class="uk-fieldset uk-margin-small-bottom">
<legend class="uk-legend">Choose a new password .</legend>
</fieldset>
{{ form_label(form.password.children.first) }}
<div class="uk-form-controls uk-form-controls-text">
{{ form_errors(form.password.children.first) }}
{{ form_widget(form.password.children.first) }}
</div>
{{ form_label(form.password.children.second) }}
<div class="uk-form-controls uk-form-controls-text ">
{{ form_errors(form.password.children.second) }}
{{ form_widget(form.password.children.second) }}
</div>
<div class="uk-form-controls uk-align-right@m uk-margin">
{{ form_row(form.submit) }}
</div>
<div class="form-controls uk-form-blank">
{{ form_rest(form) }}
</div>
{{ form_end(form) }}
{% endif %}
{% endblock %}

View File

@ -2,6 +2,7 @@
{% block body %}
{{ dump(app.session.get('user')) }}
{{ form_start(form) }}
<fieldset class="uk-fieldset uk-margin-large-bottom">
<legend class="uk-legend">RSVP!</legend>

50
bin/console.php Executable file
View File

@ -0,0 +1,50 @@
#!/usr/bin/env php
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use Doctrine\ORM\Tools\Console\Command\ClearCache\EntityRegionCommand;
use Doctrine\ORM\Tools\Console\Command\MappingDescribeCommand;
use Knp\Provider\ConsoleServiceProvider;
use PhpCsFixer\Console\Command\DescribeCommand;
use PhpCsFixer\Console\Command\SelfUpdateCommand;
use Sikofitt\App\Provider\DoctrineConsoleProvider;
use Symfony\Component\Console\Application;
use Symfony\Component\Yaml\Command\LintCommand;
$loader = require __DIR__.'/../vendor/autoload.php';
$app = new \Kernel($loader, true);
$consoleConfig = [
'console.name' => 'Doughnut Wedding',
'console.version' => '0.0.2',
'console.project_directory' => __DIR__.'/..',
];
$app
->register(new ConsoleServiceProvider(), $consoleConfig)
->register(new DoctrineConsoleProvider());
/**
* @var Application $console
*/
$console = $app['console'];
$console->add(new Symfony\Bridge\Twig\Command\LintCommand());
$console->add(new Symfony\Bridge\Twig\Command\DebugCommand());
$console->add(new PhpCsFixer\Console\Command\FixCommand());
$console->add(new Symfony\Component\Yaml\Command\LintCommand());
$app['console']->run();

View File

@ -1,21 +0,0 @@
<?php
use Doctrine\DBAL\Tools\Console\ConsoleRunner;
use Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Symfony\Component\Console\Helper\HelperSet;
/**
* @var EntityManager $em
*/
$em = require 'doctrine.php';
$helperSet = new HelperSet([
'db' => new ConnectionHelper($em->getConnection()),
'em' => new EntityManagerHelper($em),
]);
return $helperSet;

View File

@ -15,6 +15,7 @@
"google/recaptcha": "^1.1",
"ircmaxell/random-lib": "^1.2",
"ircmaxell/security-lib": "^1.1",
"knplabs/console-service-provider": "^2.0",
"monolog/monolog": "^1.22",
"paragonie/cookie": "^3.1",
"paragonie/csp-builder": "^2.0",

View File

@ -1,30 +0,0 @@
<?php
use Jgut\Slim\Doctrine\ManagerBuilder;
require __DIR__ . '/vendor/autoload.php';
$settings = [
'default' => [
'annotation_autoloaders' => ['class_exists'],
'connection' => [
'driver' => 'pdo_mysql',
'user' => 'doughnut',
'password' => 'doughnut',
'dbname' => 'doughnut',
'host' => 'mysql',
],
'metadata_mapping' => [
[
'type' => ManagerBuilder::METADATA_MAPPING_ANNOTATION,
'path' => [__DIR__ . '/src/Sikofitt/App/Entity'],
],
],
],
];
$managerBuilder = new ManagerBuilder([ManagerBuilder::RELATIONAL_MANAGER_KEY => 'default']);
$managerBuilder->loadSettings($settings);
return $managerBuilder->getManager('entityManager');

View File

@ -28,20 +28,21 @@ $loader = require __DIR__.'/../vendor/autoload.php';
$app = new Kernel($loader, true);
// Controllers
// Default
$app->get('/', DefaultController::class.'::indexAction')
->bind('index');
$app->setUpRoutes($app);
//$app->match('/login', DefaultController::class.'loginAction')
// ->bind('login');
// RSVP Actions
$app->match('/rsvp', RsvpController::class.'::indexAction')
->method('GET|POST')
->bind('rsvp');
$app->match('/rsvp/reset', RsvpController::class.'::resetAction')
->method('GET|POST')
->bind('rsvp_password_reset');
$app->get('/rsvp/reset/{token}', RsvpController::class.'::tokenAction')
->bind('rsvp_token');
$app->match('/rsvp/reset/{token}', RsvpController::class.'::tokenAction')
->bind('rsvp_token')
->method('GET|POST');
//->before(new MysqlAuthenticatorMiddleware());
$app->match('/rsvp/edit', RsvpController::class.'::editAction')
->method('GET|POST')
@ -52,4 +53,5 @@ $app->before(new CspMiddleware(), \Kernel::EARLY_EVENT);
$app->before(new HeaderMiddleware(), \Kernel::EARLY_EVENT);
// Run the app
$app->run();

View File

@ -20,12 +20,10 @@
namespace Sikofitt\App\Controller;
use Sikofitt\App\Form\RsvpType;
use Symfony\Component\Form\Extension\Csrf\CsrfExtension;
use Symfony\Component\Form\Extension\HttpFoundation\Type\FormTypeHttpFoundationExtension;
use Symfony\Component\Form\Forms;
use Sikofitt\App\Entity\User;
use Sikofitt\App\Form\UserLoginType;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Csrf\CsrfTokenManager;
class DefaultController
{
@ -34,42 +32,47 @@ class DefaultController
return $app->render('index.html.twig', ['request' => $request]);
}
public function rsvpAction()
public function loginAction(Request $request, \Kernel $app)
{
/* $app = $this->app;
$rsvp = new Rsvp();
$rsvp
->setGuests(2)
->setCreated(new \DateTime('now'))
->setUpdated(new \DateTime('now'));
if ($app->session()->has('user')) {
//return $app->redirect($app->url('rsvp_edit'));
}
$loginForm = $app->getFormFactory()->create(UserLoginType::class);
if ($request->isMethod('POST')) {
$loginForm->handleRequest($request);
if ($loginForm->isValid() && $loginForm->isSubmitted()) {
$user = $app->getEntityManager()->getRepository(User::class)->findByEmail($loginForm->get('email_username')->getData());
if (null !== $user && true === password_verify($loginForm->get('password')->getData(), $user[0]->getPassword())) {
$userSession = [
'firstName' => $user[0]->getFirstName(),
'lastName' => $user[0]->getLastName(),
'fullName' => sprintf('%s %s', $user[0]->getFirstName(), $user[0]->getLastName()),
'familySide' => $user[0]->getFamilySide(),
'email' => $user[0]->getEmail(),
'family' => $user[0]->getFamily(),
'created' => $user[0]->getCreated()->format('U'),
'updated' => $user[0]->getUpdated()->format('U'),
'guests' => $user[0]->getRsvp()->getGuests(),
];
$app->getSession()->set('user', $userSession);
$app->redirect($app->url('rsvp'));
} else {
$error = new FormError('Your password or email is incorrect.');
$error->setOrigin($loginForm);
$loginForm->get('password')->addError($error);
$user = new User();
$user->setFirstName('Eric')
->setLastName('Wheeler')
->setFamily(true)
->setEmail('sikofitt@gmail.com')
->setCreated(new \DateTime('now'))
->setUpdated(new \DateTime('now'))
->setFamilySide(User::ERIC_SIDE)
->setRsvp($rsvp);
$app['em']->persist($user);
$app['em']->flush(); */
$bytes = \ParagonIE_Sodium_Compat::randombytes_buf(22);
$password = new Password(new ScryptPassword());
// dump($password->hash('password'));
$blake = \ParagonIE_Sodium_Compat::crypto_generichash($bytes);
$blake2b = \ParagonIE_Sodium_Core_BLAKE2b::bin2hex($blake);
$formFactory = Forms::createFormFactoryBuilder()
->addTypeExtension(new FormTypeHttpFoundationExtension())
->addExtension(new CsrfExtension(new CsrfTokenManager()))
->getFormFactory();
$form = $formFactory->create(RsvpType::class);
// dump($form->createView());
return 'hello';
//return $this->container->get('view')->render('RsvpForm.html.twig', ['form' => $form->createView()]);
return $app->render('login.html.twig', ['form' => $loginForm->createView()]);
}
}
}
return $app->render('login.html.twig', ['form' => $loginForm->createView()]);
}
public function logoutAction(Request $request, \Kernel $app)
{
$app->session()->remove('user');
return $app->render('logout.html.twig');
}
}

View File

@ -22,7 +22,7 @@ namespace Sikofitt\App\Controller;
use Doctrine\ORM\EntityManager;
use Sikofitt\{
App\Entity\Rsvp, App\Entity\User, App\Form\ResetType, App\Form\RsvpType, App\Repository\RsvpRepository, App\Repository\UserRepository
App\Entity\Rsvp, App\Entity\User, App\Form\ResetPasswordType, App\Form\ResetType, App\Form\RsvpType, App\Repository\RsvpRepository, App\Repository\UserRepository
};
use Symfony\Component\Form\FormFactory;
@ -144,6 +144,7 @@ class RsvpController
]);
}*/
$app->addInfo('Message', 'message 2');
return $app->render(
'reset_password.html.twig',
[
@ -156,5 +157,25 @@ class RsvpController
public function tokenAction(Request $request, \Kernel $app, string $token = null)
{
$user = $app['orm.em']->getRepository('Sikofitt\App\Entity\User')->getUserByToken($token);
if (null === $user) {
return $app->render('reset_password_token.html.twig', ['token' => ['valid' => false, 'value' => $token]]);
} else {
$passwordForm = $app->getFormFactory()->create(ResetPasswordType::class);
if ($request->isMethod('POST')) {
$passwordForm->handleRequest($request);
if ($passwordForm->isValid() && $passwordForm->isSubmitted()) {
$hash = $app->encodePassword($user, $passwordForm->get('password')->getData());
$user->setPassword($hash);
$app['orm.em']->getRepository('Sikofitt\App\Entity\User')->updatePassword($user);
$app->addSuccess('Successfully changed your password!');
return $app->redirect($app->url('rsvp'));
}
}
return $app->render('reset_password_token.html.twig', ['token' => ['valid' => true, 'value' => $token], 'form' => $passwordForm->createView(), 'user' => $user]);
}
}
}

View File

@ -21,7 +21,7 @@
namespace Sikofitt\App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\Encoder\BCryptPasswordEncoder;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints as Assert;
/**
@ -30,7 +30,7 @@ use Symfony\Component\Validator\Constraints as Assert;
* @ORM\Entity(repositoryClass="Sikofitt\App\Repository\UserRepository")
* @ORM\Table(name="users")
*/
class User
class User implements UserInterface
{
const KATRINA_SIDE = 'Katrina';
@ -92,9 +92,15 @@ class User
/**
* @var string
* @ORM\Column(name="token", type="string", length=255, nullable=true)
* @ORM\Column(name="reset_token", type="string", length=255, nullable=true)
*/
private $token;
private $resetToken;
/**
* @var string
* @ORM\Column(name="user_token", type="string", length=255, nullable=true)
*/
private $userToken;
/**
* @var int
@ -177,6 +183,16 @@ class User
return $this->lastName;
}
/**
* @return string
*/
public function getUsername()
{
$email = explode('@', $this->email);
return $email[0];
}
/**
* Set family.
*
@ -256,10 +272,7 @@ class User
*/
public function setPassword($password)
{
$encoder = new BCryptPasswordEncoder(14);
$salt = bin2hex(random_bytes(16));
$this->password = $encoder->encodePassword($password, $salt);
$this->password = $password;
return $this;
}
@ -275,14 +288,10 @@ class User
/**
* @return string
*/
public function getPlainPassword(): string
public function getPlainPassword()
{
if (null === $this->plainPassword) {
return '';
} else {
return $this->plainPassword;
}
}
/**
* @param string $plainPassword
@ -297,13 +306,13 @@ class User
}
/**
* @param string $token
* @param string $userToken
*
* @return $this
*/
public function setToken($token)
public function setUserToken($userToken)
{
$this->token = $token;
$this->userToken = $userToken;
return $this;
}
@ -311,9 +320,29 @@ class User
/**
* @return string
*/
public function getToken()
public function getUserToken()
{
return $this->token;
return $this->userToken;
}
/**
* @param string $resetToken
*
* @return $this
*/
public function setResetToken($resetToken)
{
$this->resetToken = $resetToken;
return $this;
}
/**
* @return string
*/
public function getResetToken()
{
return $this->resetToken;
}
/**
@ -387,4 +416,18 @@ class User
{
return $this->rsvp;
}
public function getRoles()
{
return ['ROLE_USER'];
}
public function eraseCredentials()
{
}
public function getSalt()
{
return null;
}
}

View File

@ -0,0 +1,83 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Length;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\NotNull;
/**
* Class ResetPasswordType.
*/
class ResetPasswordType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => 'Passwords do not match.',
'invalid_message_parameters' => [
'class' => 'uk-text-danger',
],
'required' => true,
'options' => [
'always_empty' => false,
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
],
'label_attr' => [
'class' => 'uk-form-label',
],
'constraints' => [
new NotBlank(),
new NotNull(),
],
],
'first_options' => [
'label' => 'New password',
'constraints' => [
new Length(['min' => 8, 'minMessage' => 'Password must be at least 8 characters.']),
],
],
'second_options' => [
'label' => 'Repeat your password',
],
])
->add('submit', SubmitType::class, [
'attr' => [
'class' => 'uk-button uk-button-primary uk-button-large',
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('attr', ['class' => 'uk-form uk-form-horizontal']);
}
}

View File

@ -0,0 +1,79 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Constraints\NotBlank;
class UserLoginType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('email_username', TextType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'Email address',
],
'label' => 'Email address',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
'constraints' => [
new NotBlank(),
new Email([
'strict' => true,
'checkMX' => true,
'checkHost' => true,
'message' => 'Invalid email address.',
]),
],
])->add('password', PasswordType::class, [
'attr' => [
'class' => 'uk-input uk-form-large uk-padding-small uk-box-shadow-hover-small',
'placeholder' => 'Password',
],
'label' => 'Password',
'label_attr' => [
'class' => 'uk-form-label uk-text-primary',
],
'constraints' => [
new NotBlank(),
],
])->add('submit', SubmitType::class, [
'attr' => [
'class' => 'uk-button uk-button-primary',
],
])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefault('attr', ['class' => 'uk-form uk-form-horizontal']);
}
}

View File

@ -0,0 +1,33 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Middleware;
use Symfony\Component\HttpFoundation\Request;
class AuthenticatorMiddleware
{
public function __invoke(Request $request, \Kernel $app)
{
if ($app->session()->has('user')) {
return true;
}
}
}

View File

@ -0,0 +1,106 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Provider;
use Doctrine\DBAL\Tools\Console\Command\{
ImportCommand,
ReservedWordsCommand,
RunSqlCommand
};
use Doctrine\ORM\Tools\Console\Command\{
ClearCache\EntityRegionCommand, ClearCache\MetadataCommand,
ClearCache\QueryCommand, ClearCache\ResultCommand,
ConvertDoctrine1SchemaCommand, ConvertMappingCommand,
EnsureProductionSettingsCommand, GenerateEntitiesCommand,
GenerateProxiesCommand, GenerateRepositoriesCommand, InfoCommand,
MappingDescribeCommand, RunDqlCommand, SchemaTool\CreateCommand,
SchemaTool\DropCommand, SchemaTool\UpdateCommand, ValidateSchemaCommand
};
use Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Symfony\Component\Console\Helper\HelperSet;
/**
* Class DoctrineConsoleProvider.
*/
class DoctrineConsoleProvider implements ServiceProviderInterface
{
/**
* Registers services on the given container.
*
* This method should only be used to configure services and parameters.
* It should not get services.
*
* @param Container $pimple A container instance
*/
public function register(Container $pimple)
{
if (false === isset($pimple['console'])) {
throw new \LogicException('You must enable the Knp\Provider\ConsoleServiceProvider service provider to be able to use the DoctrineConsoleProvider.');
}
if (false === isset($pimple['db.options'])) {
throw new \LogicException('You must enable the DoctrineServiceProvider to use the DoctrineConsoleProvider.');
}
if(false === isset($pimple['orm.em'])) {
throw new \LogicException('You must enable the Dflydev\Provider\DoctrineOrm\DoctrineOrmServiceProvider to use the DoctrineConsoleProvider.');
}
$console = $pimple['console'];
$console->setHelperSet(new HelperSet(array(
'em' => new EntityManagerHelper($pimple['orm.em'])
)));
$updateCommand = new UpdateCommand();
$updateCommand->setName('orm:schema:update');
$schemaValidateCommand = (new ValidateSchemaCommand())
->setName('orm:schema:validate')
->setAliases(['orm:validate']);
$schemaDropCommand = (new DropCommand())
->setName('orm:schema:drop');
$schemaCreateCommand = (new CreateCommand())
->setName('orm:schema:create');
$console->addCommands([
new ConvertDoctrine1SchemaCommand(),
new ConvertMappingCommand(),
new EnsureProductionSettingsCommand(),
new EntityRegionCommand(),
new GenerateEntitiesCommand(),
new GenerateProxiesCommand(),
new GenerateRepositoriesCommand(),
new ImportCommand(),
new InfoCommand(),
new MappingDescribeCommand(),
new MetadataCommand(),
new QueryCommand(),
new RunDqlCommand(),
new RunSqlCommand(),
new ReservedWordsCommand(),
new ResultCommand(),
$schemaCreateCommand,
$schemaDropCommand,
$schemaValidateCommand,
]);
}
}

View File

@ -23,6 +23,7 @@ namespace Sikofitt\App\Repository;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Sikofitt\App\Entity\User;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Constraints\Email;
use Symfony\Component\Validator\Validation;
@ -33,6 +34,17 @@ use Symfony\Component\Validator\Validation;
*/
class UserRepository extends EntityRepository
{
public function findByEmail(string $email)
{
return $this->findBy(['email' => $email]);
/*return $this->createQueryBuilder('u')
->select('u')
->where('u.email = :email')
->setParameter('email', $email)
->getQuery()
->getOneOrNullResult(Query::HYDRATE_OBJECT);*/
}
public function getKatrinaCount()
{
return $this->createQueryBuilder('u')
@ -78,9 +90,21 @@ class UserRepository extends EntityRepository
->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
public function getUserByToken(string $token)
{
return $this->createQueryBuilder('u')
->select(['u'])
->where('u.token = :token')
->setParameter('token', $token)
->getQuery()
->getOneOrNullResult();
}
public function setResetToken(string $email)
{
$token = bin2hex(random_bytes(22));
$bytes = \ParagonIE_Sodium_Compat::randombytes_buf(22);
$blake = \ParagonIE_Sodium_Compat::crypto_generichash($bytes);
$token = \ParagonIE_Sodium_Core_BLAKE2b::bin2hex($blake);
return (bool) $this->createQueryBuilder('u')
->update()
@ -91,4 +115,11 @@ class UserRepository extends EntityRepository
->getQuery()
->getOneOrNullResult(Query::HYDRATE_SINGLE_SCALAR);
}
public function updatePassword(UserInterface $user)
{
$user->setToken(null);
$this->getEntityManager()->persist($user);
$this->getEntityManager()->flush();
}
}

View File

@ -0,0 +1,38 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\App\Traits;
use Doctrine\ORM\EntityManager;
trait EntityManagerTrait
{
/**
* @return null|EntityManager
*/
public function getEntityManager()
{
if (false === isset($this['orm.em']) || false === isset($this['db.options'])) {
return null;
}
return $this['orm.em'];
}
}

View File

@ -20,6 +20,9 @@
namespace Sikofitt\App\Traits;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
use Symfony\Component\HttpFoundation\Session\Session;
/**
* Trait FlashTrait.
*
@ -30,12 +33,12 @@ trait FlashTrait
/**
* @param \string[] ...$messages
*
* @return $this
* @return $this|null
*/
public function addInfo(string ...$messages)
{
if (false === isset($this['session'])) {
return;
return null;
}
foreach ($messages as $message) {
@ -53,7 +56,7 @@ trait FlashTrait
public function addError(string ...$messages)
{
if (false === isset($this['session'])) {
return;
return null;
}
foreach ($messages as $message) {
@ -71,7 +74,7 @@ trait FlashTrait
public function addSuccess(string ...$messages)
{
if (false === isset($this['session'])) {
return;
return null;
}
foreach ($messages as $message) {
@ -84,7 +87,7 @@ trait FlashTrait
public function addWarning(string ...$messages)
{
if (false === isset($this['session'])) {
return;
return null;
}
foreach ($messages as $message) {
@ -93,4 +96,95 @@ trait FlashTrait
return $this;
}
/**
* @return array|null
*/
public function peekAll()
{
if (false === isset($this['session'])) {
return null;
}
return $this['session']->getFlashBag()->peekAll();
}
/**
* @param string $name
* @param array $default
*
* @return array|null
*/
public function peekFlash(string $name, array $default = [])
{
if (false === isset($this['session'])) {
return null;
}
if (true === $this['session']->getFlashBag()->has($name)) {
return $this['session']->getFlashBag()->peek($name, $default);
}
return [];
}
public function clearFlashes()
{
$this['session']->getFlashBag()->clear();
}
/**
* @param string $name
* @param array $default
*
* @return array
*/
public function getFlash(string $name, array $default = [])
{
if (false === isset($this['session'])) {
return null;
}
if (true === $this['session']->getFlashBag()->has($name)) {
return $this['session']->getFlashBag()->get($name, $default);
}
return [];
}
/**
* @return FlashBag
*/
public function getFlashBag()
{
if (false === isset($this['session'])) {
return null;
}
return $this['session']->getFlashBag();
}
/**
* @return Session
*/
public function session()
{
if (false === isset($this['session'])) {
if (class_exists(Session::class)) {
return new Session();
} else {
return null;
}
}
return $this['session'];
}
/**
* @return Session
*/
public function getSession()
{
return $this->session();
}
}

View File

@ -0,0 +1,100 @@
<?php
/*
* doughnutwedding.com
* Copyright (C) 2017 http://doughnutwedding.com eric@doughnutwedding.com
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Sikofitt\Security;
use Doctrine\ORM\EntityManager;
use Sikofitt\App\Entity\User;
use Symfony\Component\Security\Core\Exception\UnsupportedUserException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
class MySqlUserProvider implements UserProviderInterface
{
private $entityManager;
public function __construct(EntityManager $entityManager)
{
$this->entityManager = $entityManager;
}
/**
* Loads the user for the given username.
*
* This method must throw UsernameNotFoundException if the user is not
* found.
*
* @param string $username The username
*
* @throws UsernameNotFoundException if the user is not found
*
* @return UserInterface
*/
public function loadUserByUsername($username)
{
if (null === $username) {
$username = '';
}
$userArray = $this->entityManager->getRepository(User::class)->findByEmail($username);
$user = new User();
$user->setCreated($userArray['created'])
->setUpdated($userArray['updated'])
->setFamily($userArray['family'])
->setFamilySide($userArray['familySide'])
->setFirstName($userArray['firstname'])
->setLastName($userArray['lastname'])
->setRsvp($userArray['rsvp'])
->setPassword($userArray['password'])
->setEmail($userArray['email']);
return $user;
}
/**
* Refreshes the user for the account interface.
*
* It is up to the implementation to decide if the user data should be
* totally reloaded (e.g. from the database), or if the UserInterface
* object can just be merged into some internal array of users / identity
* map.
*
* @param UserInterface $user
*
* @throws UnsupportedUserException if the account is not supported
*
* @return UserInterface
*/
public function refreshUser(UserInterface $user)
{
}
/**
* Whether this provider supports the given user class.
*
* @param string $class
*
* @return bool
*/
public function supportsClass($class)
{
return get_class($class) === self::class;
}
}

View File

@ -20,9 +20,12 @@
namespace Sikofitt\Security;
use Doctrine\ORM\EntityManager;
use Sikofitt\App\Entity\User;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
@ -30,6 +33,15 @@ use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class MysqlAuthenticator extends AbstractGuardAuthenticator
{
private $encoderFactory;
private $entityManager;
public function __construct(EncoderFactoryInterface $encoderFactory, EntityManager $entityManager)
{
$this->encoderFactory = $encoderFactory;
$this->entityManager = $entityManager;
}
/**
* Returns a response that directs the user to authenticate.
*
@ -88,7 +100,17 @@ class MysqlAuthenticator extends AbstractGuardAuthenticator
*/
public function getCredentials(Request $request)
{
// TODO: Implement getCredentials() method.
$password = $request->request->get('password');
$email = $request->request->get('email');
if (isset($user[0])) {
$user = $user[0];
}
return [
'email' => $email,
'password' => $password,
];
}
/**
@ -108,7 +130,11 @@ class MysqlAuthenticator extends AbstractGuardAuthenticator
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
// TODO: Implement getUser() method.
if (null === $userProvider->loadUserByUsername($credentials['email'])) {
return new User();
} else {
return $userProvider->loadUserByUsername($credentials['email']);
}
}
/**
@ -129,7 +155,14 @@ class MysqlAuthenticator extends AbstractGuardAuthenticator
*/
public function checkCredentials($credentials, UserInterface $user)
{
// TODO: Implement checkCredentials() method.
$encoder = $this->encoderFactory->getEncoder($user);
return $encoder
->isPasswordValid(
$user->getPassword(),
$credentials['password'],
null
);
}
/**
@ -151,7 +184,7 @@ class MysqlAuthenticator extends AbstractGuardAuthenticator
Request $request,
AuthenticationException $exception
) {
// TODO: Implement onAuthenticationFailure() method.
return new Response($exception->getMessage(), 403);
}
/**
@ -174,7 +207,7 @@ class MysqlAuthenticator extends AbstractGuardAuthenticator
TokenInterface $token,
$providerKey
) {
// TODO: Implement onAuthenticationSuccess() method.
$request->getSession()->set('user', $token.':'.$providerKey);
}
/**
@ -193,6 +226,6 @@ class MysqlAuthenticator extends AbstractGuardAuthenticator
*/
public function supportsRememberMe()
{
// TODO: Implement supportsRememberMe() method.
return false;
}
}