chore: use Authenticator instead of inheritance for Last.fm auth

This commit is contained in:
Phan An 2024-03-22 15:34:13 +01:00
parent 8b5fd336df
commit f405924bee
11 changed files with 92 additions and 65 deletions

View file

@ -0,0 +1,64 @@
<?php
namespace App\Http\Integrations\Lastfm\Auth;
use App\Http\Integrations\Lastfm\Contracts\RequiresSignature;
use Saloon\Contracts\Authenticator;
use Saloon\Http\PendingRequest;
use Saloon\Repositories\Body\FormBodyRepository;
final class LastfmAuthenticator implements Authenticator
{
public function __construct(private string $key, private string $secret)
{
}
public function set(PendingRequest $pendingRequest): void
{
$this->addApiKey($pendingRequest);
if ($pendingRequest->getRequest() instanceof RequiresSignature) {
$this->sign($pendingRequest);
}
}
private function addApiKey(PendingRequest $request): void
{
if ($request->body() instanceof FormBodyRepository) {
$request->body()->add('api_key', $this->key);
} else {
$request->query()->add('api_key', $this->key);
}
}
protected function sign(PendingRequest $request): void
{
if ($request->body() instanceof FormBodyRepository) {
$request->body()->add('api_sig', $this->createSignature($request->body()->all()));
} else {
$request->query()->add('api_sig', $this->createSignature($request->query()->all()));
}
}
private function createSignature(array $parameters): string
{
ksort($parameters);
// Generate the API signature.
// @link http://www.last.fm/api/webauth#6
$str = '';
foreach ($parameters as $name => $value) {
if ($name === 'format') {
// The format parameter is not part of the signature.
continue;
}
$str .= $name . $value;
}
$str .= $this->secret;
return md5($str);
}
}

View file

@ -1,6 +1,6 @@
<?php
namespace App\Http\Integrations\Concerns;
namespace App\Http\Integrations\Lastfm\Concerns;
trait FormatsLastFmText
{

View file

@ -0,0 +1,7 @@
<?php
namespace App\Http\Integrations\Lastfm\Contracts;
interface RequiresSignature
{
}

View file

@ -2,6 +2,7 @@
namespace App\Http\Integrations\Lastfm;
use App\Http\Integrations\Lastfm\Auth\LastfmAuthenticator;
use Saloon\Http\Connector;
use Saloon\Traits\Plugins\AcceptsJson;
@ -13,4 +14,9 @@ class LastfmConnector extends Connector
{
return config('koel.lastfm.endpoint');
}
protected function defaultAuth(): LastfmAuthenticator
{
return new LastfmAuthenticator(config('koel.lastfm.key'), config('koel.lastfm.secret'));
}
}

View file

@ -2,7 +2,7 @@
namespace App\Http\Integrations\Lastfm\Requests;
use App\Http\Integrations\Concerns\FormatsLastFmText;
use App\Http\Integrations\Lastfm\Concerns\FormatsLastFmText;
use App\Models\Album;
use App\Values\AlbumInformation;
use Illuminate\Support\Arr;
@ -29,7 +29,6 @@ final class GetAlbumInfoRequest extends Request
protected function defaultQuery(): array
{
return [
'api_key' => config('koel.lastfm.key'),
'method' => 'album.getInfo',
'artist' => $this->album->artist->name,
'album' => $this->album->name,

View file

@ -2,7 +2,7 @@
namespace App\Http\Integrations\Lastfm\Requests;
use App\Http\Integrations\Concerns\FormatsLastFmText;
use App\Http\Integrations\Lastfm\Concerns\FormatsLastFmText;
use App\Models\Artist;
use App\Values\ArtistInformation;
use Saloon\Enums\Method;
@ -28,7 +28,6 @@ final class GetArtistInfoRequest extends Request
protected function defaultQuery(): array
{
return [
'api_key' => config('koel.lastfm.key'),
'method' => 'artist.getInfo',
'artist' => $this->artist->name,
'autocorrect' => 1,

View file

@ -2,15 +2,16 @@
namespace App\Http\Integrations\Lastfm\Requests;
use App\Http\Integrations\Lastfm\Contracts\RequiresSignature;
use Saloon\Enums\Method;
use Saloon\Http\Request;
final class GetSessionKeyRequest extends SignedRequest
final class GetSessionKeyRequest extends Request implements RequiresSignature
{
protected Method $method = Method::GET;
public function __construct(private string $token)
{
parent::__construct();
}
public function resolveEndpoint(): string
@ -22,7 +23,6 @@ final class GetSessionKeyRequest extends SignedRequest
protected function defaultQuery(): array
{
return [
'api_key' => config('koel.lastfm.key'),
'method' => 'auth.getSession',
'token' => $this->token,
'format' => 'json',

View file

@ -2,14 +2,16 @@
namespace App\Http\Integrations\Lastfm\Requests;
use App\Http\Integrations\Lastfm\Contracts\RequiresSignature;
use App\Models\Album;
use App\Models\Song;
use App\Models\User;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasFormBody;
final class ScrobbleRequest extends SignedRequest implements HasBody
final class ScrobbleRequest extends Request implements HasBody, RequiresSignature
{
use HasFormBody;
@ -17,7 +19,6 @@ final class ScrobbleRequest extends SignedRequest implements HasBody
public function __construct(private Song $song, private User $user, private int $timestamp)
{
parent::__construct();
}
public function resolveEndpoint(): string
@ -29,7 +30,6 @@ final class ScrobbleRequest extends SignedRequest implements HasBody
protected function defaultBody(): array
{
$body = [
'api_key' => config('koel.lastfm.key'),
'method' => 'track.scrobble',
'artist' => $this->song->artist->name,
'track' => $this->song->title,

View file

@ -1,48 +0,0 @@
<?php
namespace App\Http\Integrations\Lastfm\Requests;
use Saloon\Http\PendingRequest;
use Saloon\Http\Request;
use Saloon\Repositories\Body\FormBodyRepository;
abstract class SignedRequest extends Request
{
public function __construct()
{
$this->middleware()->onRequest(fn (PendingRequest $request) => $this->sign($request));
}
protected function sign(PendingRequest $request): void
{
if ($request->body() instanceof FormBodyRepository) {
$request->body()->add('api_sig', self::createSignature($request->body()->all()));
return;
}
$request->query()->add('api_sig', self::createSignature($request->query()->all()));
}
private static function createSignature(array $parameters): string
{
ksort($parameters);
// Generate the API signature.
// @link http://www.last.fm/api/webauth#6
$str = '';
foreach ($parameters as $name => $value) {
if ($name === 'format') {
// The format parameter is not part of the signature.
continue;
}
$str .= $name . $value;
}
$str .= config('koel.lastfm.secret');
return md5($str);
}
}

View file

@ -2,13 +2,15 @@
namespace App\Http\Integrations\Lastfm\Requests;
use App\Http\Integrations\Lastfm\Contracts\RequiresSignature;
use App\Models\Song;
use App\Models\User;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasFormBody;
final class ToggleLoveTrackRequest extends SignedRequest implements HasBody
final class ToggleLoveTrackRequest extends Request implements HasBody, RequiresSignature
{
use HasFormBody;
@ -16,7 +18,6 @@ final class ToggleLoveTrackRequest extends SignedRequest implements HasBody
public function __construct(private Song $song, private User $user, private bool $love)
{
parent::__construct();
}
public function resolveEndpoint(): string
@ -28,7 +29,6 @@ final class ToggleLoveTrackRequest extends SignedRequest implements HasBody
protected function defaultBody(): array
{
return [
'api_key' => config('koel.lastfm.key'),
'method' => $this->love ? 'track.love' : 'track.unlove',
'sk' => $this->user->preferences->lastFmSessionKey,
'artist' => $this->song->artist->name,

View file

@ -2,14 +2,16 @@
namespace App\Http\Integrations\Lastfm\Requests;
use App\Http\Integrations\Lastfm\Contracts\RequiresSignature;
use App\Models\Album;
use App\Models\Song;
use App\Models\User;
use Saloon\Contracts\Body\HasBody;
use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Traits\Body\HasFormBody;
final class UpdateNowPlayingRequest extends SignedRequest implements HasBody
final class UpdateNowPlayingRequest extends Request implements HasBody, RequiresSignature
{
use HasFormBody;
@ -17,7 +19,6 @@ final class UpdateNowPlayingRequest extends SignedRequest implements HasBody
public function __construct(private Song $song, private User $user)
{
parent::__construct();
}
public function resolveEndpoint(): string
@ -29,7 +30,6 @@ final class UpdateNowPlayingRequest extends SignedRequest implements HasBody
protected function defaultBody(): array
{
$parameters = [
'api_key' => config('koel.lastfm.key'),
'method' => 'track.updateNowPlaying',
'artist' => $this->song->artist->name,
'track' => $this->song->title,