Skip to content

Commit

Permalink
Merge pull request #1777 from Ecodenl/feature/admin/confirm-user-email
Browse files Browse the repository at this point in the history
Allow verifying emails by specific roles
  • Loading branch information
Yinci authored Aug 20, 2024
2 parents 13d2e0f + 9ce6ddb commit f7bcb90
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 64 deletions.
15 changes: 3 additions & 12 deletions app/Helpers/Cache/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,7 @@ class Account extends BaseCache
const CACHE_KEY_FIND = 'Account_find_%s';
const CACHE_KEY_USER = 'Account_user_%s';

/**
* @param int $id
*
* @return \App\Models\InputSource|null
*/
public static function find($id)
public static function find(int $id): ?\App\Models\Account
{
return Cache::remember(
self::getCacheKey(static::CACHE_KEY_FIND, $id),
Expand All @@ -25,12 +20,8 @@ function () use ($id) {
);
}

public static function user($account)
public static function user(\App\Models\Account $account)
{
if (! $account instanceof \App\Models\Account) {
$account = self::find($account);
}

return Cache::remember(
self::getCooperationCacheKey(static::CACHE_KEY_USER, $account->id),
config('hoomdossier.cache.times.default'),
Expand All @@ -40,7 +31,7 @@ function () use ($account) {
);
}

public static function wipe($id)
public static function wipe(int $id): void
{
static::clear(self::getCacheKey(static::CACHE_KEY_FIND, $id));
static::clear(self::getCooperationCacheKey(static::CACHE_KEY_USER, $id));
Expand Down
19 changes: 19 additions & 0 deletions app/Http/Controllers/Cooperation/Admin/ActionController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?php

namespace App\Http\Controllers\Cooperation\Admin;

use App\Http\Controllers\Controller;
use App\Models\Account;
use App\Models\Cooperation;

class ActionController extends Controller
{
public function verifyEmail(Cooperation $cooperation, Account $account)
{
$this->authorize('verifyEmail', $account);
$account->update(['email_verified_at' => now()]);

return redirect()->back()
->with('success', __('cooperation/admin/cooperation/residents.index.table.columns.email-verified'));
}
}
14 changes: 5 additions & 9 deletions app/Models/Account.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use App\Notifications\ResetPasswordNotification;
use App\Notifications\VerifyEmailNotification;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Collection;
Expand Down Expand Up @@ -75,6 +76,7 @@ class Account extends Authenticatable implements MustVerifyEmail
*/
protected $casts = [
'is_admin' => 'boolean',
'email_verified_at' => 'datetime',
];

/**
Expand Down Expand Up @@ -117,30 +119,24 @@ public function cooperations(): Collection
* Will return the user from the account and cooperation that is being used.
*
* This will work because the global cooperation scope is applied.
*
* @return User|null
*/
public function user()
public function user(): ?User
{
return \App\Helpers\Cache\Account::user($this);
}

/**
* Will return all the users from the account.
*
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function users()
public function users(): HasMany
{
return $this->hasMany(User::class);
}

/**
* Returns whether or not a user is associated with a particular Cooperation.
*
* @return bool
*/
public function isAssociatedWith(Cooperation $cooperation)
public function isAssociatedWith(Cooperation $cooperation): bool
{
return $this->users()->withoutGlobalScopes()->where('cooperation_id', '=', $cooperation->id)->count() > 0;
}
Expand Down
22 changes: 22 additions & 0 deletions app/Policies/AccountPolicy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

namespace App\Policies;

use App\Helpers\RoleHelper;
use App\Models\Account;
use Illuminate\Auth\Access\HandlesAuthorization;

class AccountPolicy
{
use HandlesAuthorization;

public function verifyEmail(Account $account, Account $target): bool
{
// A user can verify their own email, and certain roles can verify a (resident's) account's email also.
return is_null($target->email_verified_at) && ($account->id === $target->id || ($account->user()->hasRole([
RoleHelper::ROLE_COACH,
RoleHelper::ROLE_COORDINATOR,
RoleHelper::ROLE_COOPERATION_ADMIN,
])) && $target->user()->hasRole(RoleHelper::ROLE_RESIDENT));
}
}
35 changes: 5 additions & 30 deletions app/Providers/AuthServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,8 @@

namespace App\Providers;

use App\Models\Building;
use App\Models\Cooperation;
use App\Models\FileStorage;
use App\Models\Media;
use App\Models\PrivateMessage;
use App\Models\Questionnaire;
use App\Models\Role;
use App\Models\SubStep;
use App\Models\ToolQuestion;
use App\Models\User;
use App\Models\UserActionPlanAdvice;
use App\Policies\BuildingPolicy;
use App\Policies\CooperationPolicy;
use App\Policies\FileStoragePolicy;
use App\Policies\MediaPolicy;
use App\Policies\PrivateMessagePolicy;
use App\Policies\QuestionnairePolicy;
use App\Policies\RolePolicy;
use App\Policies\SubStepPolicy;
use App\Policies\ToolQuestionPolicy;
use App\Policies\UserActionPlanAdvicePolicy;
use App\Policies\UserPolicy;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;
Expand All @@ -35,17 +16,7 @@ class AuthServiceProvider extends ServiceProvider
* @var array
*/
protected $policies = [
SubStep::class => SubStepPolicy::class,
ToolQuestion::class => ToolQuestionPolicy::class,
PrivateMessage::class => PrivateMessagePolicy::class,
Questionnaire::class => QuestionnairePolicy::class,
Cooperation::class => CooperationPolicy::class,
User::class => UserPolicy::class,
Building::class => BuildingPolicy::class,
FileStorage::class => FileStoragePolicy::class,
UserActionPlanAdvice::class => UserActionPlanAdvicePolicy::class,
Media::class => MediaPolicy::class,
Role::class => RolePolicy::class
// Use auto discovery
];

/**
Expand All @@ -57,6 +28,10 @@ public function boot()
{
$this->registerPolicies();

Gate::guessPolicyNamesUsing(function ($modelClass) {
return 'App\\Policies\\'.class_basename($modelClass).'Policy';
});

Gate::define('talk-to-resident', BuildingPolicy::class.'@talkToResident');
Gate::define('access-building', BuildingPolicy::class.'@accessBuilding');
Gate::define('set-appointment', BuildingPolicy::class.'@setAppointment');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'city' => 'Stad',
'status' => 'Status',
'no-known-created-at' => 'Niet bekend',
'email-verified' => 'E-mail geverifieerd',
],
],
],
Expand Down
1 change: 1 addition & 0 deletions resources/lang/nl/default.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
'from' => 'van',
'to' => 'tot',
'for' => 'voor',
'verify' => 'Verifiëren',

'progress' => [
'not-completed' => ':step - deze stap is nog niet afgerond',
Expand Down
16 changes: 13 additions & 3 deletions resources/views/cooperation/admin/coach/buildings/index.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@
<th>@lang('woningdossier.cooperation.admin.coach.buildings.index.table.columns.zip-code')</th>
<th>@lang('woningdossier.cooperation.admin.coach.buildings.index.table.columns.city')</th>
<th>@lang('woningdossier.cooperation.admin.coach.buildings.index.table.columns.date')</th>
<th>@lang('cooperation/admin/cooperation/residents.index.table.columns.email-verified')</th>
</tr>
</thead>
<tbody>


@foreach($buildings as $building)
<?php
@php
/** @var \App\Models\Building $building */
$user = $building->user;
Expand All @@ -37,8 +38,7 @@
}
$appointmentDateStrotime = strtotime($appointmentDateFormatted);
?>
@endphp
<tr>
<td data-sort="{{$appointmentDateStrotime ?? '-'}}">
{{$appointmentDateFormatted ?? '-'}}
Expand All @@ -59,6 +59,16 @@
<td data-sort="{{$userCreatedAtStrotime ?? '-'}}">
{{$userCreatedAtFormatted ?? '-'}}
</td>
<td>
@can('verifyEmail', $user->account)
<a class="btn btn-success"
href="{{ route('cooperation.admin.actions.verify-email', ['account' => $user->account]) }}">
@lang('default.verify')
</a>
@else
{{ is_null($user->account->email_verified_at) ? __('default.no') : $user->account->email_verified_at->format('d-m-Y') }}
@endcan
</td>
</tr>
@endforeach
</tbody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class="btn btn-md btn-primary pull-right"><span class="glyphicon glyphicon-plus"
<div class="panel-body">
<div class="row">
<div class="col-sm-12">
<table class="table table-responsive ">
<table class="table table-responsive">
<thead>
<tr>
<th>@lang('woningdossier.cooperation.admin.cooperation.questionnaires.index.table.columns.questionnaire-name')</th>
Expand All @@ -21,10 +21,10 @@ class="btn btn-md btn-primary pull-right"><span class="glyphicon glyphicon-plus"
</tr>
</thead>
<tbody>
@forelse($questionnaires as $questionnaire)
@foreach($questionnaires as $questionnaire)
<tr>
<td>{{$questionnaire->name}}</td>
<td>{{ $questionnaire->steps()->pluck('name')->implode(', ') }}</td> {{-- Step could be hidden, so we optional it --}}
<td>{{ $questionnaire->steps()->pluck('name')->implode(', ') }}</td>
<td>
<input data-active="{{$questionnaire->isActive() ? 'on' : 'off'}}" class="toggle-active" data-questionnaire-id="{{$questionnaire->id}}" type="checkbox" data-toggle="toggle" data-on="Actief" data-off="Niet actief">
</td>
Expand All @@ -38,9 +38,7 @@ class="btn btn-md btn-primary pull-right"><span class="glyphicon glyphicon-plus"
</button>
</td>
</tr>
@empty

@endforelse
@endforeach
</tbody>
</table>

Expand Down Expand Up @@ -84,7 +82,6 @@ class="btn btn-md btn-primary pull-right"><span class="glyphicon glyphicon-plus"
});
toggleActive.change(function () {
console.log($(this));
$.ajax({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<th>@lang('cooperation/admin/cooperation/residents.index.table.columns.zip-code')</th>
<th>@lang('cooperation/admin/cooperation/residents.index.table.columns.city')</th>
<th>@lang('cooperation/admin/cooperation/residents.index.table.columns.status')</th>
<th>@lang('cooperation/admin/cooperation/residents.index.table.columns.email-verified')</th>
</tr>
</thead>
<tbody>
Expand Down Expand Up @@ -55,6 +56,16 @@
{{$mostRecentBuildingStatus->status->name}}
@endif
</td>
<td>
@can('verifyEmail', $user->account)
<a class="btn btn-success"
href="{{ route('cooperation.admin.actions.verify-email', ['account' => $user->account]) }}">
@lang('default.verify')
</a>
@else
{{ is_null($user->account->email_verified_at) ? __('default.no') : $user->account->email_verified_at->format('d-m-Y') }}
@endcan
</td>
</tr>
@endforeach
</tbody>
Expand All @@ -68,8 +79,7 @@
@push('js')
<script>
$(document).ready(function () {
var table = $('table');
let table = $('table');
table.DataTable({
responsive: true,
stateSave: true,
Expand All @@ -80,7 +90,8 @@
{responsivePriority: 3},
{responsivePriority: 4},
{responsivePriority: 6},
{responsivePriority: 5}
{responsivePriority: 5},
{responsivePriority: 7}
]
});
})
Expand Down
4 changes: 4 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@

/* Section that a coach, coordinator and cooperation-admin can access */
Route::middleware('current-role:cooperation-admin|coach|coordinator')->group(function () {
Route::prefix('actions')->as('actions.')->group(function () {
Route::get('{account}/verify-email', [Cooperation\Admin\ActionController::class, 'verifyEmail'])->name('verify-email');
});

Route::resource('messages', Cooperation\Admin\MessagesController::class)->only('index');

Route::prefix('tool')->name('tool.')->group(function () {
Expand Down

0 comments on commit f7bcb90

Please sign in to comment.