Skip to content

Commit 19fa820

Browse files
authored
Remove legacy action format support and enforce migration (#148)
1 parent 0c3b486 commit 19fa820

File tree

7 files changed

+49
-90
lines changed

7 files changed

+49
-90
lines changed

src/Actions/ResponseAction.php

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
use Illuminate\Http\RedirectResponse;
88
use Illuminate\View\View;
99
use Laravel\SerializableClosure\SerializableClosure;
10-
use Laravel\SerializableClosure\Serializers\Signed;
10+
use MagicLink\Exceptions\LegacyActionFormatException;
1111
use MagicLink\MagicLink;
1212
use MagicLink\Security\Serializable\Serializable;
1313

@@ -79,11 +79,7 @@ public function run()
7979
try {
8080
return $this->callResponse(Serializable::unserialize($this->httpResponse));
8181
} catch (Exception $e) {
82-
return $this->callResponse(unserialize($this->httpResponse, ['allowed_classes' => [
83-
RedirectResponse::class,
84-
SerializableClosure::class,
85-
Signed::class,
86-
]]));
82+
throw LegacyActionFormatException::detected($e);
8783
}
8884
}
8985

src/Controllers/MagicLinkController.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,20 @@
33
namespace MagicLink\Controllers;
44

55
use Illuminate\Routing\Controller;
6+
use Illuminate\Support\Facades\Log;
7+
use MagicLink\Exceptions\LegacyActionFormatException;
68
use MagicLink\MagicLink;
79

810
class MagicLinkController extends Controller
911
{
1012
public function access($token)
1113
{
12-
return MagicLink::getMagicLinkByToken($token)->run();
14+
try {
15+
return MagicLink::getMagicLinkByToken($token)->run();
16+
} catch (LegacyActionFormatException $e) {
17+
Log::error('Legacy action format detected for token: '.$token.'. Error: '.$e->getMessage());
18+
19+
return response()->json(['message' => 'This magic link is no longer valid. Please request a new one.'], 419);
20+
}
1321
}
1422
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace MagicLink\Exceptions;
4+
5+
use RuntimeException;
6+
7+
class LegacyActionFormatException extends RuntimeException
8+
{
9+
/**
10+
* Create a new LegacyActionFormatException instance.
11+
*
12+
* @param \Exception|null $previous
13+
*/
14+
public static function detected($previous = null): LegacyActionFormatException
15+
{
16+
return new self(
17+
'Legacy action format detected. Please run the migration command to update your magic links: php artisan magiclink:migrate',
18+
0,
19+
$previous
20+
);
21+
}
22+
}

src/MagicLink.php

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
use MagicLink\Events\MagicLinkWasCreated;
1313
use MagicLink\Events\MagicLinkWasDeleted;
1414
use MagicLink\Events\MagicLinkWasVisited;
15+
use MagicLink\Exceptions\LegacyActionFormatException;
1516
use MagicLink\Security\Serializable\ActionSerializable;
16-
use MagicLink\Security\Serializable\LegacyAllowClasses;
1717

1818
/**
1919
* @property string $token
@@ -61,16 +61,10 @@ protected static function getTokenLength()
6161
public function getActionAttribute($value)
6262
{
6363
try {
64-
$action = ActionSerializable::unserialize($value);
64+
return ActionSerializable::unserialize($value);
6565
} catch (\Exception $e) {
66-
$action = $this->legacyGetAction($value);
66+
throw LegacyActionFormatException::detected($e);
6767
}
68-
69-
if (! $action instanceof ActionAbstract) {
70-
throw new \RuntimeException('Invalid action type. Only ActionAbstract instances are allowed.');
71-
}
72-
73-
return $action;
7468
}
7569

7670
public function setActionAttribute($value)
@@ -82,25 +76,6 @@ public function setActionAttribute($value)
8276
$this->attributes['action'] = ActionSerializable::serialize($value);
8377
}
8478

85-
#[\Deprecated('Please run php artisan magiclink:migrate to migrate legacy actions.', 'v2.24.3')]
86-
private function legacyGetAction($value)
87-
{
88-
$data = $this->getConnection()->getDriverName() === 'pgsql'
89-
? base64_decode($value)
90-
: $value;
91-
92-
if (preg_match('/^O:\d+:"([^"]+)"/', $data, $matches)) {
93-
$className = $matches[1];
94-
if (is_subclass_of($className, ActionAbstract::class)) {
95-
return unserialize($data, [
96-
'allowed_classes' => LegacyAllowClasses::get($className),
97-
]);
98-
}
99-
}
100-
101-
throw new \RuntimeException('Invalid legacy action class');
102-
}
103-
10479
public function baseUrl(?string $baseUrl): self
10580
{
10681
$this->attributes['base_url'] = rtrim($baseUrl, '/').'/';

src/Security/Serializable/LegacyAllowClasses.php

Lines changed: 0 additions & 25 deletions
This file was deleted.

tests/Actions/LegacyTest.php

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,31 +22,23 @@ public function test_controller()
2222
$magiclinkUrl = $this->generateMagicLink($action);
2323

2424
$this->get($magiclinkUrl)
25-
->assertStatus(200)
26-
->assertSeeText('im a controller invoke');
25+
->assertStatus(419);
2726
}
2827

2928
public function test_download_file()
3029
{
3130
$magiclinkUrl = $this->generateMagicLink(new DownloadFileAction('text.txt'));
3231

3332
$this->get($magiclinkUrl)
34-
->assertStatus(200)
35-
->assertHeader(
36-
'content-disposition',
37-
'attachment; filename=text.txt'
38-
);
33+
->assertStatus(419);
3934
}
4035

4136
public function test_auth()
4237
{
4338
$magiclinkUrl = $this->generateMagicLink(new LoginAction(User::first()));
4439

4540
$this->get($magiclinkUrl)
46-
->assertStatus(302)
47-
->assertRedirect('/');
48-
49-
$this->assertAuthenticatedAs(User::first());
41+
->assertStatus(419);
5042
}
5143

5244
public function test_response_callable()
@@ -58,17 +50,17 @@ function () {
5850
));
5951

6052
$this->get($magiclinkUrl)
61-
->assertStatus(200)
62-
->assertSeeText('callback called');
53+
->assertStatus(419)
54+
->assertDontSee('callback called');
6355
}
6456

6557
public function test_view()
6658
{
6759
$magiclinkUrl = $this->generateMagicLink(new ViewAction('view'));
6860

6961
$this->get($magiclinkUrl)
70-
->assertStatus(200)
71-
->assertSeeText('This is a tests view');
62+
->assertStatus(419)
63+
->assertDontSee('This is a tests view');
7264
}
7365

7466
private function generateMagicLink($action): string

tests/Commands/MigrateLegacyActionsCommandTest.php

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,7 @@ public function test_controller()
2222
[$id, $magiclinkUrl] = $this->generateLegacyMagicLink($action);
2323

2424
$this->get($magiclinkUrl)
25-
->assertStatus(200)
26-
->assertSeeText('im a controller invoke');
25+
->assertStatus(419);
2726

2827
$this->artisan('magiclink:migrate', ['--force' => true])
2928
->assertSuccessful();
@@ -42,11 +41,7 @@ public function test_download_file()
4241
[$id, $magiclinkUrl] = $this->generateLegacyMagicLink(new DownloadFileAction('text.txt'));
4342

4443
$this->get($magiclinkUrl)
45-
->assertStatus(200)
46-
->assertHeader(
47-
'content-disposition',
48-
'attachment; filename=text.txt'
49-
);
44+
->assertStatus(419);
5045

5146
$this->artisan('magiclink:migrate', ['--force' => true])
5247
->assertSuccessful();
@@ -90,8 +85,7 @@ function () {
9085
));
9186

9287
$this->get($magiclinkUrl)
93-
->assertStatus(200)
94-
->assertSeeText('callback called');
88+
->assertStatus(419);
9589

9690
$this->artisan('magiclink:migrate', ['--force' => true])
9791
->assertSuccessful();
@@ -110,8 +104,7 @@ public function test_view()
110104
[$id, $magiclinkUrl] = $this->generateLegacyMagicLink(new ViewAction('view'));
111105

112106
$this->get($magiclinkUrl)
113-
->assertStatus(200)
114-
->assertSeeText('This is a tests view');
107+
->assertStatus(419);
115108

116109
$this->artisan('magiclink:migrate', ['--force' => true])
117110
->assertSuccessful();
@@ -132,8 +125,7 @@ public function test_dry_run()
132125
[$id, $magiclinkUrl] = $this->generateLegacyMagicLink($action);
133126

134127
$this->get($magiclinkUrl)
135-
->assertStatus(200)
136-
->assertSeeText('im a controller invoke');
128+
->assertStatus(419);
137129

138130
$actionLegacy = DB::table('magic_links')->where('id', $id)->first()->action;
139131

@@ -143,8 +135,7 @@ public function test_dry_run()
143135
$this->assertEquals($actionLegacy, DB::table('magic_links')->where('id', $id)->first()->action);
144136

145137
$this->get($magiclinkUrl)
146-
->assertStatus(200)
147-
->assertSeeText('im a controller invoke');
138+
->assertStatus(419);
148139
}
149140

150141
private function generateLegacyMagicLink($action): array

0 commit comments

Comments
 (0)