Skip to content

Commit b7b50b3

Browse files
authored
Feature/add security header (#319)
1 parent 1f209f7 commit b7b50b3

37 files changed

+333
-178
lines changed

app/Http/Controllers/User/DashboardController.php

-33
This file was deleted.

app/Http/Middleware/CacheHeaders.php

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Illuminate\Support\Facades\Auth;
10+
use Symfony\Component\HttpFoundation\Response;
11+
12+
final class CacheHeaders
13+
{
14+
public function handle(Request $request, Closure $next): Response
15+
{
16+
/**
17+
* @var \Illuminate\Http\Response $response
18+
*/
19+
$response = $next($request);
20+
21+
if (Auth::check()) {
22+
$response->setCache(
23+
options: [
24+
'private' => true,
25+
'max_age' => 0,
26+
's_maxage' => 0,
27+
'no_store' => true,
28+
],
29+
);
30+
} else {
31+
$response->setCache(
32+
options: [
33+
'public' => true,
34+
'max_age' => 60,
35+
's_maxage' => 60,
36+
],
37+
);
38+
39+
foreach ($response->headers->getCookies() as $cookie) {
40+
$response->headers->removeCookie(
41+
name: $cookie->getName(),
42+
);
43+
}
44+
}
45+
46+
return $response;
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware\Security;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
11+
final class ContentSecurityPolicy
12+
{
13+
public function handle(Request $request, Closure $next): Response
14+
{
15+
/**
16+
* @var Response $response
17+
*/
18+
$response = $next($request);
19+
20+
$response->headers->add([
21+
'Content-Security-Policy' => "default-src 'self'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src *;",
22+
]);
23+
24+
return $response;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware\Security;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
11+
final class PermissionsPolicy
12+
{
13+
public function handle(Request $request, Closure $next): Response
14+
{
15+
/**
16+
* @var Response $response
17+
*/
18+
$response = $next($request);
19+
20+
$response->headers->add([
21+
'Permissions-Policy' => 'geolocation=(self), microphone=()',
22+
]);
23+
24+
return $response;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware\Security;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
11+
final class ReferrerPolicy
12+
{
13+
public function handle(Request $request, Closure $next): Response
14+
{
15+
/**
16+
* @var Response $response
17+
*/
18+
$response = $next($request);
19+
20+
$response->headers->add([
21+
'Referrer-Policy' => 'no-referrer',
22+
]);
23+
24+
return $response;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware\Security;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
11+
final class StrictTransportSecurity
12+
{
13+
public function handle(Request $request, Closure $next): Response
14+
{
15+
/**
16+
* @var Response $response
17+
*/
18+
$response = $next($request);
19+
20+
$response->headers->add([
21+
'Strict-Transport-Security' => 'max-age=31536000; includeSubDomains; preload',
22+
]);
23+
24+
return $response;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace App\Http\Middleware\Security;
6+
7+
use Closure;
8+
use Illuminate\Http\Request;
9+
use Symfony\Component\HttpFoundation\Response;
10+
11+
final class XFrameOption
12+
{
13+
public function handle(Request $request, Closure $next): Response
14+
{
15+
/**
16+
* @var Response $response
17+
*/
18+
$response = $next($request);
19+
20+
$response->headers->add([
21+
'X-Frame-Options' => 'deny',
22+
]);
23+
24+
return $response;
25+
}
26+
}

app/Http/Middleware/TrackLastActivity.php

-28
This file was deleted.

app/Livewire/Components/Discussion/Comments.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public function comments(): Collection
7575
$replies = collect();
7676

7777
// @phpstan-ignore-next-line
78-
foreach ($this->discussion->replies->load(['allChildReplies', 'user']) as $reply) {
78+
foreach ($this->discussion->replies->load(['allChildReplies', 'user', 'user.media']) as $reply) {
7979
/** @var Reply $reply */
8080
if ($reply->allChildReplies->isNotEmpty()) {
8181
foreach ($reply->allChildReplies as $childReply) {

app/Livewire/Components/User/Articles.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ final class Articles extends Component implements HasActions, HasForms
2929
#[Computed]
3030
public function articles(): LengthAwarePaginator
3131
{
32-
return Article::with(['user', 'tags', 'reactions'])
32+
return Article::with('tags', 'reactions')
3333
->where('user_id', Auth::id())
3434
->latest()
3535
->paginate(10);

app/Livewire/Components/User/Discussions.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class Discussions extends Component implements HasActions, HasForms
3232
#[Computed]
3333
public function discussions(): LengthAwarePaginator
3434
{
35-
return Discussion::with('user')
35+
return Discussion::with('tags', 'replies')
3636
->where('user_id', Auth::id())
3737
->latest()
3838
->paginate(10);

app/Livewire/Components/User/Threads.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ final class Threads extends Component implements HasActions, HasForms
3232
#[Computed]
3333
public function threads(): LengthAwarePaginator
3434
{
35-
return Thread::with('user')
35+
return Thread::with('channels', 'solutionReply', 'replies')
3636
->scopes('withViewsCount')
3737
->where('user_id', Auth::id())
3838
->latest()

app/Livewire/Pages/Account/Dashboard.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ final class Dashboard extends Component
1818
#[Computed]
1919
public function user(): User
2020
{
21-
return User::query()
21+
return User::with('providers')
2222
->scopes('withCounts')
2323
->find(Auth::id());
2424
}

app/Livewire/Pages/Articles/Index.php

+9-4
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use App\Models\Tag;
99
use App\Traits\WithLocale;
1010
use Illuminate\Contracts\View\View;
11+
use Illuminate\Support\Facades\Cache;
1112
use Livewire\Component;
1213
use Livewire\WithoutUrlPagination;
1314
use Livewire\WithPagination;
@@ -26,7 +27,7 @@ public function mount(): void
2627
public function render(): View
2728
{
2829
return view('livewire.pages.articles.index', [
29-
'articles' => Article::with(['tags', 'user', 'user.transactions']) // @phpstan-ignore-line
30+
'articles' => Article::with(['tags', 'user', 'user.transactions', 'user.media', 'media']) // @phpstan-ignore-line
3031
->withCount(['views', 'reactions'])
3132
->orderByDesc('sponsored_at')
3233
->orderByDesc('published_at')
@@ -35,9 +36,13 @@ public function render(): View
3536
->forLocale($this->locale)
3637
->simplePaginate(21),
3738

38-
'tags' => Tag::query()->whereHas('articles', function ($query): void {
39-
$query->published();
40-
})->orderBy('name')->get(),
39+
'tags' => Cache::remember(
40+
key: 'articles.tags',
41+
ttl: now()->addWeek(),
42+
callback: fn () => Tag::query()->whereHas('articles', function ($query): void {
43+
$query->published();
44+
})->orderBy('name')->get()
45+
),
4146
])
4247
->title(__('pages/article.title'));
4348
}

app/Livewire/Pages/Discussions/Index.php

+10-5
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use App\Models\Tag;
1010
use App\Traits\WithLocale;
1111
use Illuminate\Contracts\View\View;
12+
use Illuminate\Support\Facades\Cache;
1213
use Livewire\Attributes\Url;
1314
use Livewire\Component;
1415
use Livewire\WithoutUrlPagination;
@@ -57,15 +58,19 @@ public function tagExists(string $tag): bool
5758
public function render(): View
5859
{
5960
/** @var DiscussionQueryBuilder $query */
60-
$query = Discussion::with(['tags', 'replies', 'user']) // @phpstan-ignore-line
61+
$query = Discussion::with(['tags', 'replies', 'user', 'user.media', 'reactions']) // @phpstan-ignore-line
6162
->withCount('replies')
6263
->forLocale($this->locale)
6364
->notPinned();
6465

65-
$tags = Tag::query()
66-
->whereJsonContains('concerns', ['discussion'])
67-
->orderBy('name')
68-
->get();
66+
$tags = Cache::remember(
67+
key: 'discussions.tags',
68+
ttl: now()->addWeek(),
69+
callback: fn () => Tag::query()
70+
->whereJsonContains('concerns', ['discussion'])
71+
->orderBy('name')
72+
->get()
73+
);
6974

7075
if ($this->currentTag) {
7176
$query->forTag($this->currentTag); // @phpstan-ignore-line

app/Livewire/Pages/Discussions/SingleDiscussion.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public function mount(): void
3636
->twitterDescription($this->discussion->excerpt(100))
3737
->withUrl();
3838

39-
$this->discussion->load('tags', 'replies', 'reactions', 'replies.user', 'user');
39+
$this->discussion->load('tags', 'replies', 'reactions', 'user', 'user.media');
4040
}
4141

4242
public function editAction(): Action

app/Livewire/Pages/Forum/DetailThread.php

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,12 @@ final class DetailThread extends Component implements HasActions, HasForms
2424

2525
public Thread $thread;
2626

27-
public function mount(Thread $thread): void
27+
public function mount(): void
2828
{
29-
views($thread)->cooldown(now()->addHour())->record();
29+
views($this->thread)->cooldown(now()->addHour())->record();
3030

31-
$this->thread = $thread->loadCount('views');
31+
$this->thread->load('channels', 'channels.parent', 'user', 'user.media', 'replies', 'solutionReply')
32+
->loadCount('views');
3233
}
3334

3435
public function editAction(): Action

app/Livewire/Pages/Forum/Index.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ protected function applySorting(Builder $query): Builder
171171

172172
public function render(): View
173173
{
174-
$query = Thread::with(['channels', 'user']);
174+
$query = Thread::with(['channels', 'user', 'user.media'])
175+
->withCount('replies');
175176

176177
$query = $this->applyChannel($query);
177178
$query = $this->applySearch($query);

0 commit comments

Comments
 (0)