jonnybarnes.uk/app/Http/Controllers/MicropubController.php
Jonny Barnes f9a133e727 Squashed commit of the following:
commit 1eecb177b6d5aed8fd7ced14ba5a3fb8d528521b
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 16:19:29 2017 +0100

    Fix some Style issues

commit 490ca7b8e90b08ad1e59714e09ba49a853cb753e
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 16:06:13 2017 +0100

    Run check on POST as well GET methods

commit fb588798ce4e6548a0e0be8ec00673b24a41fe40
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 16:04:47 2017 +0100

    Check for exceptions when validating token

commit da2ce1a2893c03ae058c07361d860f4e509ac0ee
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:57:27 2017 +0100

    Go back to doing all dusk tests

commit c34fbf519a0cc528c49e8d5d9628a8891540c294
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:53:17 2017 +0100

    Don’t check for error message now

commit bd48859f7faf279296c0a15f2f6b18f83a951ebd
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:49:23 2017 +0100

    Escape the facade call with backslash

commit fcb405d8caf0d23cb70abb67da35cc68e2e86e4f
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:43:19 2017 +0100

    Regenerate the token within dusk

commit 8732f100a075742802447e65a2ba56b7cc69149a
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:36:39 2017 +0100

    Slight refactor of token verification

commit cb0ec39bba5765d55ad024fe3d73ad81877034da
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:29:46 2017 +0100

    Stupid forgetting to root the namespace

commit 1f2b309d7e77ff195ac8a4aa2f755044d15b4b92
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:22:54 2017 +0100

    Try throwing my own exception

commit 41dab30389cd04746051b8552e605eb3693ad0f1
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:12:18 2017 +0100

    Checked headers

commit 5eb12543adf999c4887a1e63b8d174fb7321202b
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:08:37 2017 +0100

    Don’t catch exceptions

commit 71dbf8d08a7eed9f3795f06811431b7a3c2ddd6c
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 15:00:22 2017 +0100

    Log headers for request

commit 1109a394456dd07375ccfab5a8436a902f3b27ce
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:54:19 2017 +0100

    Add laravels own logs to the artifacts

commit f6cd9ee81385bf4366e0e59fc6c34c9b56b6e52c
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:38:18 2017 +0100

    Log error

commit dadf3b3150c74d2199a87a529151d55fc1e67394
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:33:14 2017 +0100

    Look for different error message

commit 9df2d1b831e6c65283fb5438967f4517752c31ac
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:24:15 2017 +0100

    Look for different error message

commit 090736735fe3cc5fb4076b77730f7e39bc92d80c
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:14:14 2017 +0100

    Just checking error message

commit 6a766b1bc4af3dd0e335b3f57287086a6e528340
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:08:16 2017 +0100

    Look for error message

commit a399af496a440e30a0c0a1d4050121b3e491e968
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 14:00:29 2017 +0100

    Simpler setup of php error log

commit 3472c48ed50466c2d150e727cb6c1e43b1298d4c
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 13:45:11 2017 +0100

    Set the php error log

commit 978c17f33e2d9c3dd5166744aef924bf21483737
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 13:37:04 2017 +0100

    Set the php-fpm error log

commit 0e3407a4781f83bf3c516578a4594eff92b9d4d8
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 13:18:45 2017 +0100

    Save a token for the user

commit 951fe7966e18ac9e38526b6dfdc4a75fd4d68a0c
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 12:56:49 2017 +0100

    Use the right phrase

commit faedc802a46c35b3b9264348ebbd895f4d2b3895
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 12:41:03 2017 +0100

    Easier phrase to find in log

commit 71b3c905bb4be0943254e51604af1293cc63cd0f
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 12:35:07 2017 +0100

    Check we are logged in

commit fd6e3c79f13b9aada395a53b367e316936787298
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 12:20:14 2017 +0100

    Cleanup output

commit 231ac5868070f232f5bd455f0d6070621da9ae91
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 21 12:00:36 2017 +0100

    Try pausing test before running second check

commit 754cbafacdb8dd46db2eee699b8e23009c7d0bf5
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Thu Apr 20 13:15:10 2017 +0100

    Temporary fix for TLS issues on travis-ci and sensiolab’s security checker

commit 8e03bba917ca9ba951bac5197e0008ae7188e19a
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 19 21:24:51 2017 +0100

    Use less strict check on no auth endpoint being found

commit 91ee495ae1302f269febdd0e14ecbe81222b1014
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 19 21:23:44 2017 +0100

    Use newer scope values in token

commit 989a242b4ff271f4ac6ee0873dc2924506c0efcf
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Tue Apr 18 22:30:53 2017 +0100

    Missed some array syntax

commit 92d11c9a62c2a122b4fef62da8af6bf4be23288d
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Tue Apr 18 22:21:43 2017 +0100

    Fix some styleci issues

commit 3a3aa8daca981561594cf3fb542c3e240b174013
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Tue Apr 18 21:50:31 2017 +0100

    Allow client to send request with JSON syntax, cleanup a but of other code

commit dcc3baf2dc554175fdd1de408cb4f7170a6540d4
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Tue Apr 18 21:47:51 2017 +0100

    composer update

commit b89dbb6b7a4940b5eed30727f0bd67bc382d98bc
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Apr 14 09:50:39 2017 +0100

    wip

commit 87eb63dc9570f6822324d665bc1658cfcb446554
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Thu Apr 13 18:44:15 2017 +0100

    Allow the syntax for the endpoint to be set in the config page

commit 9595a0b68e4b51d77c18e63f9940026aa1325475
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Thu Apr 13 17:17:34 2017 +0100

    Use better scope values in command

commit 08e2223a8d56984df826c1cd876f58b00cce8faf
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 12 21:53:44 2017 +0100

    Get media uploads working again

commit 4c0d6861324c23afe4145c4ff9a46d8a1ecc10e9
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 12 21:09:16 2017 +0100

    Show syndication targets properlu in client form and config page

commit 63939316a0818c8dfa8cba9919b231c2fdad5821
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 12 21:08:40 2017 +0100

    Update prism, recompress assets

commit 74902237cb227e05386135fb0e2d5330b65e1c4e
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 12 17:58:41 2017 +0100

    Better presentation of syndication targets

commit 345cda342051af406d6616a9162a75af625a64e0
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 12 17:47:12 2017 +0100

    yarn upgrade

commit 17de68cc8f26cc472b009bf42942778fac75c890
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Wed Apr 12 17:42:49 2017 +0100

    composer update

commit 7da78294dfdda8c68fc411ab03db65f101c7fc4c
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 10 18:34:56 2017 +0100

    Move to using indieauth-client-php client directly, add code to get new tokens

commit 0020596b52c1590936d12ec9c458b7e70a06bc8e
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 10 18:31:40 2017 +0100

    Add routes for retreiving a new token

commit ee0a6763f037629e2a97fa8536c84cbffbbbd7e2
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 10 18:30:33 2017 +0100

    Show syndication targets as an unformated json string

commit 765d032fa883db834a005f61dc553b3b0ef9ee8e
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 10 18:29:21 2017 +0100

    Add a migratin for the indieweb users

commit 3a5c458f132cf6c308c2e83eb57c61ae64cc2b48
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 10 18:28:51 2017 +0100

    Add a normlize_url helper function

commit fb71bd6418e7903b7d50a90dd600a82a8af03d38
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 10 18:27:41 2017 +0100

    Add a token to the users table

commit 56df9e8aa453394f8b83e8f6a28cc41c66d172f1
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 3 09:18:06 2017 +0100

    Set default values for config screen

commit 0df8217a82cac91c3129acc492b73d6e8cb69b44
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 3 09:17:14 2017 +0100

    Use the helper function to normalize URLs

commit d5f882972ec43e766ae7b9288b1ab3645dd471cf
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 3 09:15:41 2017 +0100

    In a dev environment automatically log in as the pre-created user

commit 2c3379d0e560226a4db181e7d45ce02b575990ae
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 3 09:14:13 2017 +0100

    Add a default user of me

commit 5c955803a8218c477e2f2b126811e6bed5f20379
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Mon Apr 3 09:13:08 2017 +0100

    Add a helpers file, currently only has a function to normalize URLs

commit ae052d305c835952c83602d305cfdb08d5be975e
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Fri Mar 31 16:08:30 2017 +0100

    Allow a user to register/login with his domain

commit 638ab8085f18c1bdf9c036c0272a8e88079013f5
Author: Jonny Barnes <jonny@jonnybarnes.uk>
Date:   Tue Mar 28 16:51:42 2017 +0100

    Work on allowing people to “register” for the client
2017-04-21 16:38:39 +01:00

339 lines
13 KiB
PHP

<?php
namespace App\Http\Controllers;
use Ramsey\Uuid\Uuid;
use App\{Media, Place};
use Illuminate\Http\{Request, Response};
use Ramsey\Uuid\Exception\UnsatisfiedDependencyException;
use App\Services\{NoteService, PlaceService, TokenService};
class MicropubController extends Controller
{
/**
* The Token service container.
*/
protected $tokenService;
/**
* The Note service container.
*/
protected $noteService;
/**
* The Place service container.
*/
protected $placeService;
/**
* Inject the dependencies.
*/
public function __construct(
TokenService $tokenService = null,
NoteService $noteService = null,
PlaceService $placeService = null
) {
$this->tokenService = $tokenService ?? new TokenService();
$this->noteService = $noteService ?? new NoteService();
$this->placeService = $placeService ?? new PlaceService();
}
/**
* This function receives an API request, verifies the authenticity
* then passes over the info to the relavent Service class.
*
* @param \Illuminate\Http\Request request
* @return \Illuminate\Http\Response
*/
public function post(Request $request)
{
try {
$tokenData = $this->tokenService->validateToken($request->bearerToken());
} catch (\Exception $e) {
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The provided token did not pass validation',
], 400);
}
if ($tokenData->hasClaim('scope')) {
$scopes = explode(' ', $tokenData->getClaim('scope'));
if (array_search('create', $scopes) !== false) {
$clientId = $tokenData->getClaim('client_id');
if (($request->input('h') == 'entry') || ($request->input('type')[0] == 'h-entry')) {
$data = [];
$data['client-id'] = $clientId;
if ($request->header('Content-Type') == 'application/json') {
$data['content'] = $request->input('properties.content')[0];
$data['in-reply-to'] = $request->input('properties.in-reply-to')[0];
$data['location'] = $request->input('properties.location');
//flatten location if array
if (is_array($data['location'])) {
$data['location'] = $data['location'][0];
}
} else {
$data['content'] = $request->input('content');
$data['in-reply-to'] = $request->input('in-reply-to');
$data['location'] = $request->input('location');
}
$data['syndicate'] = [];
$targets = array_pluck(config('syndication.targets'), 'uid', 'service.name');
if (is_string($request->input('mp-syndicate-to'))) {
$service = array_search($request->input('mp-syndicate-to'));
if ($service == 'Twitter') {
$data['syndicate'][] = 'twitter';
}
if ($service == 'Facebook') {
$data['syndicate'][] = 'facebook';
}
}
if (is_array($request->input('mp-syndicate-to'))) {
foreach ($targets as $service => $target) {
if (in_array($target, $request->input('mp-syndicate-to'))) {
if ($service == 'Twitter') {
$data['syndicate'][] = 'twitter';
}
if ($service == 'Facebook') {
$data['syndicate'][] = 'facebook';
}
}
}
}
$data['photo'] = [];
if (is_array($request->input('photo'))) {
foreach ($request->input('photo') as $photo) {
if (is_string($photo)) {
//only supporting media URLs for now
$data['photo'][] = $photo;
}
}
}
try {
$note = $this->noteService->createNote($data);
} catch (Exception $exception) {
return response()->json(['error' => true], 400);
}
return response()->json([
'response' => 'created',
'location' => $note->longurl,
], 201)->header('Location', $note->longurl);
}
if ($request->input('h') == 'card' || $request->input('type')[0] == 'h-card') {
$data = [];
if ($request->header('Content-Type') == 'application/json') {
$data['name'] = $request->input('properties.name');
$data['description'] = $request->input('properties.description') ?? null;
if ($request->has('properties.geo')) {
$data['geo'] = $request->input('properties.geo');
}
} else {
$data['name'] = $request->input('name');
$data['description'] = $request->input('description');
if ($request->has('geo')) {
$data['geo'] = $request->input('geo');
}
if ($request->has('latitude')) {
$data['latitude'] = $request->input('latitude');
$data['longitude'] = $request->input('longitude');
}
}
try {
$place = $this->placeService->createPlace($data);
} catch (Exception $exception) {
return response()->json(['error' => true], 400);
}
return response()->json([
'response' => 'created',
'location' => $place->longurl,
], 201)->header('Location', $place->longurl);
}
}
}
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The token provided is not valid or does not have the necessary scope',
], 400);
}
/**
* A GET request has been made to `api/post` with an accompanying
* token, here we check wether the token is valid and respond
* appropriately. Further if the request has the query parameter
* synidicate-to we respond with the known syndication endpoints.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function get(Request $request)
{
try {
$tokenData = $this->tokenService->validateToken($request->bearerToken());
} catch (\Exception $e) {
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The provided token did not pass validation',
], 400);
}
//we have a valid token, is `syndicate-to` set?
if ($request->input('q') === 'syndicate-to') {
return response()->json([
'syndicate-to' => config('syndication.targets'),
]);
}
//nope, how about a config query?
if ($request->input('q') == 'config') {
return response()->json([
'syndicate-to' => config('syndication.targets'),
'media-endpoint' => route('media-endpoint'),
]);
}
//nope, how about a geo URL?
if (substr($request->input('q'), 0, 4) === 'geo:') {
preg_match_all(
'/([0-9\.\-]+)/',
$request->input('q'),
$matches
);
$distance = (count($matches[0]) == 3) ? 100 * $matches[0][2] : 1000;
$places = Place::near($matches[0][0], $matches[0][1], $distance);
foreach ($places as $place) {
$place->uri = config('app.url') . '/places/' . $place->slug;
}
return response()->json([
'response' => 'places',
'places' => $places,
]);
}
//nope, just return the token
return response()->json([
'response' => 'token',
'token' => [
'me' => $tokenData->getClaim('me'),
'scope' => $tokenData->getClaim('scope'),
'client_id' => $tokenData->getClaim('client_id'),
],
]);
}
/**
* Process a media item posted to the media endpoint.
*
* @param Illuminate\Http\Request $request
* @return Illuminate\Http\Response
*/
public function media(Request $request)
{
$tokenData = $this->tokenService->validateToken($request->bearerToken());
if ($tokenData === null) {
return response()->json([
'response' => 'error',
'error' => 'invalid_token',
'error_description' => 'The provided token did not pass validation',
], 400);
}
//check post scope
if ($tokenData->hasClaim('scope')) {
$scopes = explode(' ', $tokenData->getClaim('scope'));
if (array_search('post', $scopes) !== false) {
//check media valid
if ($request->hasFile('file') && $request->file('file')->isValid()) {
$type = $this->getFileTypeFromMimeType($request->file('file')->getMimeType());
try {
$filename = Uuid::uuid4() . '.' . $request->file('file')->extension();
} catch (UnsatisfiedDependencyException $e) {
return response()->json([
'response' => 'error',
'error' => 'internal_server_error',
'error_description' => 'A problem occured handling your request',
], 500);
}
try {
$path = $request->file('file')->storeAs('media', $filename, 's3');
} catch (Exception $e) { // which exception?
return response()->json([
'response' => 'error',
'error' => 'service_unavailable',
'error_description' => 'Unable to save media to S3',
], 503);
}
$media = new Media();
$media->token = $request->bearerToken();
$media->path = $path;
$media->type = $type;
$media->save();
return response()->json([
'response' => 'created',
'location' => $media->url,
], 201)->header('Location', $media->url);
}
return response()->json([
'response' => 'error',
'error' => 'invalid_request',
'error_description' => 'The uploaded file failed validation',
], 400);
}
}
return response()->json([
'response' => 'error',
'error' => 'insufficient_scope',
'error_description' => 'The provided token has insufficient scopes',
], 401);
}
/**
* Get the file type from the mimetype of the uploaded file.
*
* @param string The mimetype
* @return string The type
*/
private function getFileTypeFromMimeType($mimetype)
{
//try known images
$imageMimeTypes = [
'image/gif',
'image/jpeg',
'image/png',
'image/svg+xml',
'image/tiff',
'image/webp',
];
if (in_array($mimetype, $imageMimeTypes)) {
return 'image';
}
//try known video
$videoMimeTypes = [
'video/mp4',
'video/mpeg',
'video/quicktime',
'video/webm',
];
if (in_array($mimetype, $videoMimeTypes)) {
return 'video';
}
//try known audio types
$audioMimeTypes = [
'audio/midi',
'audio/mpeg',
'audio/ogg',
'audio/x-m4a',
];
if (in_array($mimetype, $audioMimeTypes)) {
return 'audio';
}
return 'download';
}
}