Skip to content

Commit

Permalink
Merge pull request #307 from NottingHack/#247-email-validation
Browse files Browse the repository at this point in the history
#247 email validation
  • Loading branch information
dpslwk authored Dec 30, 2018
2 parents cf4f984 + 9f51c50 commit 8a66134
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 4 deletions.
6 changes: 4 additions & 2 deletions app/HMS/Entities/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,20 @@
use LaravelDoctrine\ACL\Roles\HasRoles;
use Illuminate\Auth\Passwords\CanResetPassword;
use Doctrine\Common\Collections\ArrayCollection;
use HMS\Traits\Entities\DoctrineMustVerifyEmail;
use LaravelDoctrine\ORM\Notifications\Notifiable;
use Illuminate\Foundation\Auth\Access\Authorizable;
use LaravelDoctrine\ACL\Permissions\HasPermissions;
use LaravelDoctrine\ACL\Contracts\HasRoles as HasRoleContract;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
use LaravelDoctrine\ACL\Contracts\HasPermissions as HasPermissionsContract;

class User implements AuthenticatableContract, CanResetPasswordContract, HasRoleContract, HasPermissionsContract, AuthorizableContract
class User implements AuthenticatableContract, CanResetPasswordContract, HasRoleContract, HasPermissionsContract, AuthorizableContract, MustVerifyEmailContract
{
use CanResetPassword, Notifiable, HasRoles, HasPermissions, SoftDeletable, Timestampable, Authorizable, HasApiTokens;
use CanResetPassword, Notifiable, HasRoles, HasPermissions, SoftDeletable, Timestampable, Authorizable, HasApiTokens, DoctrineMustVerifyEmail;

const MIN_PASSWORD_LENGTH = 3;

Expand Down
4 changes: 4 additions & 0 deletions app/HMS/Mappings/HMS.Entities.User.dcm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ HMS\Entities\User:
type: string
length: 100
nullable: true
emailVerifiedAt:
column: email_verified_at
type: datetime
nullable: true
deletedAt:
type: datetime
nullable: true
Expand Down
69 changes: 69 additions & 0 deletions app/HMS/Traits/Entities/DoctrineMustVerifyEmail.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

namespace HMS\Traits\Entities;

use Carbon\Carbon;
use Illuminate\Auth\Notifications\VerifyEmail;
use LaravelDoctrine\ORM\Facades\EntityManager;

trait DoctrineMustVerifyEmail
{
/**
* @var null|Carbon Has the email address be verified
*/
protected $emailVerifiedAt;

/**
* Determine if the user has verified their email address.
*
* @return bool
*/
public function hasVerifiedEmail()
{
return ! is_null($this->emailVerifiedAt);
}

/**
* Mark the given user's email as verified.
*
* @return bool
*/
public function markEmailAsVerified()
{
$repository = EntityManager::getRepository(get_class($this));
$this->emailVerifiedAt = new Carbon;
$repository->save($this);

return true;
}

/**
* Send the email verification notification.
*
* @return void
*/
public function sendEmailVerificationNotification()
{
$this->notify(new VerifyEmail);
}

/**
* @return null|Carbon Has the email address be verified
*/
public function getEmailVerifiedAt()
{
return $this->emailVerifiedAt;
}

/**
* @param null|Carbon Has the email address be verified $emailVerifiedAt
*
* @return self
*/
public function setEmailVerifiedAt($emailVerifiedAt)
{
$this->emailVerifiedAt = $emailVerifiedAt;

return $this;
}
}
5 changes: 5 additions & 0 deletions app/HMS/User/UserManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use HMS\Repositories\UserRepository;
use HMS\User\Permissions\RoleManager;
use App\Events\Users\UserEmailChanged;
use Illuminate\Contracts\Auth\MustVerifyEmail;

class UserManager
{
Expand Down Expand Up @@ -90,6 +91,10 @@ public function updateFromRequest(User $user, Request $request): User
if ($request['email']) {
$oldEmail = $user->getEmail();
$user->setEmail($request['email']);
if ($user instanceof MustVerifyEmail) {
$user->setEmailVerifiedAt(null);
$user->sendEmailVerificationNotification();
}
event(new UserEmailChanged($user, $oldEmail));
}

Expand Down
2 changes: 2 additions & 0 deletions app/Providers/EventServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Providers;

use Illuminate\Support\Facades\Event;
use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;

class EventServiceProvider extends ServiceProvider
Expand All @@ -14,6 +15,7 @@ class EventServiceProvider extends ServiceProvider
*/
protected $listen = [
'Illuminate\Auth\Events\Registered' => [
SendEmailVerificationNotification::class,
'App\Listeners\Invites\RevokeInviteOnUserRegistered',
'App\Listeners\Membership\ApprovalEmailOnUserRegistered',
],
Expand Down
2 changes: 2 additions & 0 deletions database/factories/UserFactory.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Carbon\Carbon;
use Doctrine\Common\Collections\ArrayCollection;

/*
Expand All @@ -21,5 +22,6 @@
'username' => $faker->unique()->userName,
'rememberToken' => str_random(10),
'roles' => new ArrayCollection(),
'emailVerifiedAt' => Carbon::now(),
];
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Database\Migrations;

use Doctrine\DBAL\Schema\Schema as Schema;
use Doctrine\Migrations\AbstractMigration;

class Version20181223163238_user_table_email_verified extends AbstractMigration
{
/**
* @param Schema $schema
*/
public function up(Schema $schema)
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');

$this->addSql('ALTER TABLE user ADD email_verified_at DATETIME DEFAULT NULL');
}

/**
* @param Schema $schema
*/
public function down(Schema $schema)
{
$this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');

$this->addSql('ALTER TABLE user DROP email_verified_at');
}
}
2 changes: 2 additions & 0 deletions database/seeds/UserTableSeeder.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Carbon\Carbon;
use HMS\Entities\Role;
use HMS\Entities\User;
use HMS\Auth\PasswordStore;
Expand Down Expand Up @@ -92,6 +93,7 @@ public function run()
if ($this->createAdmin === true) {
$admin = new User('Admin', 'Admin', 'admin', '[email protected]');
$admin->getRoles()->add($this->roleRepository->findOneByName(Role::SUPERUSER));
$admin->setEmailVerifiedAt(new Carbon);
$this->passwordStore->add($admin->getUsername(), 'admin');
EntityManager::persist($admin);
}
Expand Down
26 changes: 26 additions & 0 deletions resources/views/auth/verify.blade.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
@extends('layouts.app')

@section('pageTitle', 'Verify email')

@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">{{ __('Verify Your Email Address') }}</div>

<div class="card-body">
@if (session('resent'))
<div class="alert alert-success" role="alert">
{{ __('A fresh verification link has been sent to your email address.') }}
</div>
@endif

{{ __('Before proceeding, please check your email for a verification link.') }}
{{ __('If you did not receive the email') }}, <a href="{{ route('verification.resend') }}">{{ __('click here to request another') }}</a>.
</div>
</div>
</div>
</div>
</div>
@endsection
7 changes: 5 additions & 2 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,21 @@
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::get('register/{token}', 'Auth\RegisterController@showRegistrationForm')->name('register');
Route::post('register', 'Auth\RegisterController@register');
Route::get('email/verify', 'Auth\VerificationController@show')->name('verification.notice');
Route::get('email/verify/{id}', 'Auth\VerificationController@verify')->name('verification.verify');
Route::get('email/resend', 'Auth\VerificationController@resend')->name('verification.resend');

Route::get('home', 'HomeController@index')->name('home');
Route::get('access-codes', 'HomeController@accessCodes')->name('accessCodes');

// Routes in the following group can only be access from inside the hackspace (as defined by the ip range in .env)
Route::group(['middleware' => 'ipcheck'], function () {
Route::middleware(['ipcheck'])->group(function () {
Route::get('/register-interest', 'RegisterInterestController@index')->name('registerInterest');
Route::post('/register-interest', 'RegisterInterestController@registerInterest');
});

// Routes in the following group can only be access once logged-in)
Route::group(['middleware' => 'auth'], function () {
Route::middleware(['auth', 'verified'])->group(function () {
// ROLE
Route::get('/roles', 'RoleController@index')->name('roles.index');
Route::get('/roles/{role}', 'RoleController@show')->name('roles.show');
Expand Down

0 comments on commit 8a66134

Please sign in to comment.