Merge branch 'release/0.11'

This commit is contained in:
Jonny Barnes 2017-10-19 14:34:58 +01:00
commit d33cee84b9
36 changed files with 854 additions and 1770 deletions

View file

@ -54,7 +54,6 @@ before_script:
- php artisan key:generate
- php artisan migrate
- php artisan db:seed
- php artisan token:generate
- google-chrome-stable --headless --disable-gpu --remote-debugging-port=9515 http://localhost:8000 &
script:

View file

@ -1,61 +0,0 @@
<?php
namespace App\Console\Commands;
use App\IndieWebUser;
use App\Services\TokenService;
use Illuminate\Console\Command;
class GenerateToken extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'token:generate';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Generate a token that can be used for testing purposes';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}
/**
* The token service class.
*
* @var TokenService
*/
protected $tokenService;
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(TokenService $tokenService)
{
$data = [
'me' => config('app.url'),
'client_id' => route('micropub-client'),
'scope' => 'create update',
];
$token = $tokenService->getNewToken($data);
$user = IndieWebUser::where('me', config('app.url'))->first();
$user->token = $token;
$user->save();
$this->info('Set token');
}
}

View file

@ -16,7 +16,6 @@ class Kernel extends ConsoleKernel
Commands\SecurityCheck::class,
Commands\ParseCachedWebMentions::class,
Commands\ReDownloadWebMentions::class,
Commands\GenerateToken::class,
];
/**

View file

@ -1,94 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\IndieWebUser;
use IndieAuth\Client;
use Illuminate\Http\Request;
class IndieAuthController extends Controller
{
/**
* The IndieAuth Client.
*/
protected $client;
/**
* Inject the dependency.
*
* @param \IndieAuth\Client $client
* @return void
*/
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* Begin the indie auth process. This method ties in to the login page
* from our micropub client. Here we then query the users homepage
* for their authorisation endpoint, and redirect them there with a
* unique secure state value.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Routing\RedirectResponse redirect
*/
public function start(Request $request)
{
$url = normalize_url($request->input('me'));
$authorizationEndpoint = $this->client->discoverAuthorizationEndpoint($url);
if ($authorizationEndpoint != null) {
$state = bin2hex(openssl_random_pseudo_bytes(16));
session(['state' => $state]);
$authorizationURL = $this->client->buildAuthorizationURL(
$authorizationEndpoint,
$url,
route('indieauth-callback'), //redirect_uri
route('micropub-client'), //client_id
$state
);
if ($authorizationURL) {
return redirect($authorizationURL);
}
return redirect(route('micropub-client'))->with('error', 'Error building authorization URL');
}
return redirect(route('micropub-client'))->with('error', 'Unable to determine authorisation endpoint');
}
/**
* Once they have verified themselves through the authorisation endpoint
* the next step is register/login the user.
*
* @param \Illuminate\Http\Rrequest $request
* @return \Illuminate\Routing\RedirectResponse redirect
*/
public function callback(Request $request)
{
if ($request->session()->get('state') != $request->input('state')) {
return redirect(route('micropub-client'))->with(
'error',
'Invalid <code>state</code> value returned from indieauth server'
);
}
$url = normalize_url($request->input('me'));
$indiewebUser = IndieWebUser::firstOrCreate(['me' => $url]);
$request->session()->put(['me' => $url]);
return redirect(route('micropub-client'));
}
/**
* Log out the user, flush the session data.
*
* @return \Illuminate\Routing\RedirectResponse redirect
*/
public function logout(Request $request)
{
$request->session()->flush();
return redirect(route('micropub-client'));
}
}

View file

@ -1,548 +0,0 @@
<?php
namespace App\Http\Controllers;
use App\IndieWebUser;
use Illuminate\Http\{Request, Response};
use GuzzleHttp\Exception\{ClientException, ServerException};
class MicropubClientController extends Controller
{
/**
* Inject the dependencies.
*/
public function __construct(
\IndieAuth\Client $indieClient,
\GuzzleHttp\Client $guzzleClient
) {
$this->indieClient = $indieClient;
$this->guzzleClient = $guzzleClient;
}
/**
* Display the new notes form.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\View\Factory view
*/
public function create(Request $request)
{
//initiate varaibles
$indiewebUser = null;
$syndication = null;
$mediaEndpoint = null;
$mediaURLs = null;
$url = $request->session()->get('me');
if ($url) {
$indiewebUser = IndieWebUser::where('me', $url)->first();
}
if ($indiewebUser) {
$syndication = $this->parseSyndicationTargets($indiewebUser->syndication);
$mediaEndpoint = $indiewebUser->mediaEndpoint ?? null;
$mediaURLs = $request->session()->get('media-links');
}
return view('micropub.create', compact('url', 'syndication', 'mediaEndpoint', 'mediaURLs'));
}
/**
* Process an upload to the media endpoint.
*
* @param Illuminate\Http\Request $request
* @return Illuminate\Http\Response
*/
public function processMedia(Request $request)
{
if ($request->hasFile('files') == false) {
return back()->with('error', 'No files uploaded');
}
$user = IndieWebUser::where('me', $request->session()->get('me'))->firstOrFail();
if ($user->mediaEndpoint == null || $user->token == null) {
return back()->with('error', 'No user token or known endpoint');
}
$mediaURLs = [];
foreach ($request->file('files') as $file) {
try {
$response = $this->guzzleClient->request('POST', $user->mediaEndpoint, [
'headers' => [
'Authorization' => 'Bearer ' . $user->token,
],
'multipart' => [
[
'name' => 'file',
'contents' => fopen($file->path(), 'r'),
'filename' => $file->getClientOriginalName(),
],
],
]);
} catch (ClientException | ServerException $e) {
continue;
}
$mediaURLs[] = $response->getHeader('Location')[0];
}
$storedMediaURLs = $request->session()->get('media-links') ?? [];
$mediaURLsToSave = array_merge($storedMediaURLs, $mediaURLs);
$request->session()->put('media-links', $mediaURLsToSave);
return redirect()->route('micropub-client');
}
public function clearLinks(Request $request)
{
$request->session()->forget('media-links');
return redirect(route('micropub-client'));
}
/**
* Post the notes content to the relavent micropub API endpoint.
*
* @todo make sure this works with multiple syndication targets
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function store(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
if ($user->token == null) {
return redirect()->route('micropub-client')->with('error', 'You havent requested a token yet');
}
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if (! $micropubEndpoint) {
return redirect()->route('micropub-client')->with('error', 'Unable to determine micropub API endpoint');
}
$headers = [
'Authorization' => 'Bearer ' . $user->token,
];
if ($user->syntax == 'html') {
$multipart = [
[
'name' => 'h',
'contents' => 'entry',
],
[
'name' => 'content',
'contents' => $request->input('content'),
],
];
if ($request->hasFile('photo')) {
$photos = $request->file('photo');
foreach ($photos as $photo) {
$multipart[] = [
'name' => 'photo[]',
'contents' => fopen($photo->path(), 'r'),
'filename' => $photo->getClientOriginalName(),
];
}
}
if ($request->input('in-reply-to') != '') {
$multipart[] = [
'name' => 'in-reply-to',
'contents' => $request->input('in-reply-to'),
];
}
if ($request->input('mp-syndicate-to')) {
foreach ($request->input('mp-syndicate-to') as $syn) {
$multipart[] = [
'name' => 'mp-syndicate-to[]',
'contents' => $syn,
];
}
}
if ($request->input('location')) {
if ($request->input('location') !== 'no-location') {
$multipart[] = [
'name' => 'location',
'contents' => $request->input('location'),
];
}
}
if ($request->input('media')) {
foreach ($request->input('media') as $media) {
$multipart[] = [
'name' => 'photo[]',
'contents' => $media,
];
}
}
try {
$response = $this->guzzleClient->post($micropubEndpoint, [
'multipart' => $multipart,
'headers' => $headers,
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return redirect()->route('micropub-client')->with(
'error',
'There was a bad response from the micropub endpoint.'
);
}
if ($response->getStatusCode() == 201) {
$request->session()->forget('media-links');
$location = $response->getHeader('Location');
if (is_array($location)) {
return redirect($location[0]);
}
return redirect($location);
}
}
if ($user->syntax == 'json') {
$json = [];
$json['type'] = ['h-entry'];
$json['properties'] = ['content' => [$request->input('content')]];
if ($request->input('in-reply-to') != '') {
$json['properties']['in-reply-to'][] = $request->input('in-reply-to');
}
if ($request->input('mp-syndicate-to')) {
foreach ($request->input('mp-syndicate-to') as $syn) {
$json['properties']['mp-syndicate-to'][] = $syn;
}
}
if ($request->input('location')) {
if ($request->input('location') !== 'no-location') {
$json['properties']['location'][] = $request->input('location');
}
}
if ($request->input('media')) {
$json['properties']['photo'] = [];
foreach ($request->input('media') as $media) {
$json['properties']['photo'][] = $media;
}
}
try {
$response = $this->guzzleClient->post($micropubEndpoint, [
'json' => $json,
'headers' => $headers,
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return redirect()->route('micropub-client')->with(
'error',
'There was a bad response from the micropub endpoint.'
);
}
if ($response->getStatusCode() == 201) {
$request->session()->forget('media-links');
$location = $response->getHeader('Location');
if (is_array($location)) {
return redirect($location[0]);
}
return redirect($location);
}
}
return redirect()->route('micropub-client')->with('error', 'Endpoint didnt create the note.');
}
/**
* Show currently stored configuration values.
*
* @param Illuminate\Http\Request $request
* @return view
*/
public function config(Request $request)
{
//default values
$data = [
'me' => '',
'token' => 'none',
'syndication' => 'none defined',
'media-endpoint' => 'none defined',
'syntax' => 'html',
];
if ($request->session()->has('me')) {
$data['me'] = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $request->session()->get('me'))->firstOrFail();
$data['token'] = $user->token ?? 'none defined';
$data['syndication'] = $user->syndication ?? 'none defined';
$data['media-endpoint'] = $user->mediaEndpoint ?? 'none defined';
$data['syntax'] = $user->syntax;
}
return view('micropub.config', compact('data'));
}
/**
* Get a new token.
*
* @param Illuminate\Http\Request $request
* @return view
*/
public function getNewToken(Request $request)
{
if ($request->session()->has('me')) {
$url = normalize_url($request->session()->get('me'));
$authozationEndpoint = $this->indieClient->discoverAuthorizationEndpoint($url);
if ($authozationEndpoint) {
$state = bin2hex(random_bytes(16));
$request->session()->put('state', $state);
$authorizationURL = $this->indieClient->buildAuthorizationURL(
$authozationEndpoint,
$url,
route('micropub-client-get-new-token-callback'), // redirect_uri
route('micropub-client'), //client_id
$state,
'create update' // scope needs to be a setting
);
return redirect($authorizationURL);
}
return redirect()->route('micropub-config')->with('error', 'Unable to find authorisation endpoint');
}
return redirect()->route('micropub-config')->with('error', 'You arent logged in');
}
/**
* The callback for getting a token.
*/
public function getNewTokenCallback(Request $request)
{
if ($request->input('state') !== $request->session()->get('state')) {
return redirect()->route('micropub-config')->with('error', 'The <code>state</code> didnt match.');
}
$tokenEndpoint = $this->indieClient->discoverTokenEndpoint(normalize_url($request->input('me')));
if ($tokenEndpoint) {
$token = $this->indieClient->getAccessToken(
$tokenEndpoint,
$request->input('code'),
$request->input('me'),
route('micropub-client-get-new-token-callback'), // redirect_uri
route('micropub-client') // client_id
);
if (array_key_exists('access_token', $token)) {
$url = normalize_url($token['me']);
$user = IndieWebUser::where('me', $url)->firstOrFail();
$user->token = $token['access_token'];
$user->save();
return redirect()->route('micropub-config');
}
return redirect()->route('micropub-config')->with('error', 'Error getting token from the endpoint');
}
return redirect()->route('micropub-config')->with('error', 'Unable to find token endpoint');
}
/**
* Query the micropub endpoint and store response.
*
* @param Illuminate\Http\Request $request
* @return redirect
*/
public function queryEndpoint(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
$token = $user->token;
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if ($micropubEndpoint) {
try {
$response = $this->guzzleClient->get($micropubEndpoint, [
'headers' => ['Authorization' => 'Bearer ' . $token],
'query' => 'q=config',
]);
} catch (ClientException | ServerException $e) {
return back();
}
$body = (string) $response->getBody();
$data = json_decode($body, true);
if (array_key_exists('syndicate-to', $data)) {
$user->syndication = json_encode($data['syndicate-to']);
}
if (array_key_exists('media-endpoint', $data)) {
$user->mediaEndpoint = $data['media-endpoint'];
}
$user->save();
return back();
}
}
/**
* Update the syntax setting.
*
* @param Illuminate\Http\Request $request
* @return Illuminate\Http\RedirectResponse
* @todo validate input
*/
public function updateSyntax(Request $request)
{
$user = IndieWebUser::where('me', $request->session()->get('me'))->firstOrFail();
$user->syntax = $request->syntax;
$user->save();
return redirect()->route('micropub-config');
}
/**
* Create a new place.
*
* @param \Illuminate\Http\Request $request
* @return mixed
*/
public function newPlace(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
if ($user->token === null) {
return response()->json([
'error' => true,
'error_description' => 'No known token',
], 400);
}
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if (! $micropubEndpoint) {
return response()->json([
'error' => true,
'error_description' => 'Could not determine the micropub endpoint.',
], 400);
}
$formParams = [
'h' => 'card',
'name' => $request->input('place-name'),
'description' => $request->input('place-description'),
'geo' => 'geo:' . $request->input('place-latitude') . ',' . $request->input('place-longitude'),
];
$headers = [
'Authorization' => 'Bearer ' . $user->token,
];
try {
$response = $this->guzzleClient->request('POST', $micropubEndpoint, [
'form_params' => $formParams,
'headers' => $headers,
]);
} catch (ClientException $e) {
return response()->json([
'error' => true,
'error_description' => 'Unable to create the new place',
], 400);
}
$place = $response->getHeader('Location')[0];
return response()->json([
'uri' => $place,
'name' => $request->input('place-name'),
'latitude' => $request->input('place-latitude'),
'longitude' => $request->input('place-longitude'),
]);
}
/**
* Make a request to the micropub endpoint requesting any nearby places.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function nearbyPlaces(Request $request)
{
$url = normalize_url($request->session()->get('me'));
$user = IndieWebUser::where('me', $url)->firstOrFail();
if ($user->token === null) {
return response()->json([
'error' => true,
'error_description' => 'No known token',
], 400);
}
$micropubEndpoint = $this->indieClient->discoverMicropubEndpoint($url);
if (! $micropubEndpoint) {
return response()->json([
'error' => true,
'error_description' => 'No known endpoint',
], 400);
}
try {
$query = 'geo:' . $request->input('latitude') . ',' . $request->input('longitude');
if ($request->input('u') !== null) {
$query .= ';u=' . $request->input('u');
}
$response = $this->guzzleClient->get($micropubEndpoint, [
'headers' => ['Authorization' => 'Bearer ' . $user->token],
'query' => ['q' => $query],
]);
} catch (\GuzzleHttp\Exception\BadResponseException $e) {
return response()->json([
'error' => true,
'error_description' => 'The endpoint ' . $micropubEndpoint . ' returned a non-good response',
], 400);
}
return response($response->getBody(), 200)
->header('Content-Type', 'application/json');
}
/**
* Parse the syndication targets JSON into a an array.
*
* @param string|null
* @return array|null
*/
private function parseSyndicationTargets($syndicationTargets = null)
{
if ($syndicationTargets === null || $syndicationTargets === '') {
return;
}
$syndicateTo = [];
$data = json_decode($syndicationTargets, true);
if (array_key_exists('uid', $data)) {
$syndicateTo[] = [
'target' => $data['uid'],
'name' => $data['name'],
];
}
foreach ($data as $syn) {
if (array_key_exists('uid', $syn)) {
$syndicateTo[] = [
'target' => $syn['uid'],
'name' => $syn['name'],
];
}
}
return $syndicateTo;
}
/**
* Parse the media-endpoint retrieved from querying a micropub endpoint.
*
* @param string|null
* @return string
*/
private function parseMediaEndpoint($queryResponse = null)
{
if ($queryResponse === null) {
return;
}
$data = json_decode($queryResponse, true);
if (array_key_exists('media-endpoint', $data)) {
return $data['media-endpoint'];
}
}
}

View file

@ -1,15 +0,0 @@
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class IndieWebUser extends Model
{
/**
* Mass assignment protection.
*
* @var array
*/
protected $fillable = ['me'];
}

View file

@ -1,120 +0,0 @@
<?php
declare(strict_types=1);
namespace App\Services;
use IndieAuth\Client;
class IndieAuthService
{
protected $client;
public function __construct()
{
$this->client = new Client();
}
/**
* Given a domain, determing the assocaited authorization endpoint,
* if one exists.
*
* @param string The domain
* @return string|null
*/
public function getAuthorizationEndpoint(string $domain): ?string
{
$endpoint = $this->client->discoverAuthorizationEndpoint($this->client->normalizeMeURL($domain));
if ($endpoint === false) {
return null;
}
return $endpoint;
}
/**
* Given an authorization endpoint, build the appropriate authorization URL.
*
* @param string $authEndpoint
* @param string $domain
* @return string
*/
public function buildAuthorizationURL(string $authEndpoint, string $domain): string
{
$state = bin2hex(openssl_random_pseudo_bytes(16));
session(['state' => $state]);
$redirectURL = route('indieauth-callback');
$clientId = route('micropub-client');
$authorizationURL = $this->client->buildAuthorizationURL(
$authEndpoint,
$this->client->normalizeMeURL($domain),
$redirectURL,
$clientId,
$state
);
return $authorizationURL;
}
/**
* Discover the token endpoint for a given domain.
*
* @param string The domain
* @return string|null
*/
public function getTokenEndpoint(string $domain): ?string
{
return $this->client->discoverTokenEndpoint($this->client->normalizeMeURL($domain));
}
/**
* Retrieve a token from the token endpoint.
*
* @param array The relavent data
* @return array
*/
public function getAccessToken(array $data): array
{
return $this->client->getAccessToken(
$data['endpoint'],
$data['code'],
$data['me'],
$data['redirect_url'],
$data['client_id'],
$data['state']
);
}
/**
* Determine the Authorization endpoint, then verify the suplied code is
* valid.
*
* @param array The data.
* @return array|null
*/
public function verifyIndieAuthCode(array $data): ?array
{
$authEndpoint = $this->client->discoverAuthorizationEndpoint($data['me']);
if ($authEndpoint) {
return $this->client->verifyIndieAuthCode(
$authEndpoint,
$data['code'],
$data['me'],
$data['redirect_url'],
$data['client_id'],
$data['state']
);
}
}
/**
* Determine the micropub endpoint.
*
* @param string $domain
* @return string|null The endpoint
*/
public function discoverMicropubEndpoint(string $domain): ?string
{
return $this->client->discoverMicropubEndpoint($this->client->normalizeMeURL($domain));
}
}

View file

@ -1,5 +1,8 @@
# Changelog
## Version 0.11 (2017-10-19)
- No more built-in micropub client
## Version 0.10 (20017-10-13)
- Bookmarks!
- They can only be added via micropub

76
composer.lock generated
View file

@ -8,16 +8,16 @@
"packages": [
{
"name": "aws/aws-sdk-php",
"version": "3.36.23",
"version": "3.36.29",
"source": {
"type": "git",
"url": "https://github.com/aws/aws-sdk-php.git",
"reference": "755ff10ad2ea4ebb04b4e2b150a4a4fb13a22060"
"reference": "210958295921f0004d0fdc38bc9aefe33ef2a3de"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/755ff10ad2ea4ebb04b4e2b150a4a4fb13a22060",
"reference": "755ff10ad2ea4ebb04b4e2b150a4a4fb13a22060",
"url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/210958295921f0004d0fdc38bc9aefe33ef2a3de",
"reference": "210958295921f0004d0fdc38bc9aefe33ef2a3de",
"shasum": ""
},
"require": {
@ -84,7 +84,7 @@
"s3",
"sdk"
],
"time": "2017-10-06T22:26:58+00:00"
"time": "2017-10-18T18:46:11+00:00"
},
{
"name": "barnabywalters/mf-cleaner",
@ -1940,16 +1940,16 @@
},
{
"name": "laravel/framework",
"version": "v5.5.14",
"version": "v5.5.17",
"source": {
"type": "git",
"url": "https://github.com/laravel/framework.git",
"reference": "26c700eb79e5bb55b59df2c495c9c71f16f43302"
"reference": "3a16d196bd8d2b7761c9b0060a30a3687c3ea201"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/framework/zipball/26c700eb79e5bb55b59df2c495c9c71f16f43302",
"reference": "26c700eb79e5bb55b59df2c495c9c71f16f43302",
"url": "https://api.github.com/repos/laravel/framework/zipball/3a16d196bd8d2b7761c9b0060a30a3687c3ea201",
"reference": "3a16d196bd8d2b7761c9b0060a30a3687c3ea201",
"shasum": ""
},
"require": {
@ -2068,7 +2068,7 @@
"framework",
"laravel"
],
"time": "2017-10-03T17:41:03+00:00"
"time": "2017-10-17T12:19:22+00:00"
},
{
"name": "laravel/horizon",
@ -3630,16 +3630,16 @@
},
{
"name": "phpunit/phpunit",
"version": "6.4.0",
"version": "6.4.3",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/phpunit.git",
"reference": "a1bcaca096998de32c29535fdd2dea0c475e8f61"
"reference": "06b28548fd2b4a20c3cd6e247dc86331a7d4db13"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/a1bcaca096998de32c29535fdd2dea0c475e8f61",
"reference": "a1bcaca096998de32c29535fdd2dea0c475e8f61",
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/06b28548fd2b4a20c3cd6e247dc86331a7d4db13",
"reference": "06b28548fd2b4a20c3cd6e247dc86331a7d4db13",
"shasum": ""
},
"require": {
@ -3710,7 +3710,7 @@
"testing",
"xunit"
],
"time": "2017-10-06T03:14:57+00:00"
"time": "2017-10-16T13:18:59+00:00"
},
{
"name": "phpunit/phpunit-mock-objects",
@ -4079,16 +4079,16 @@
},
{
"name": "psy/psysh",
"version": "v0.8.11",
"version": "v0.8.13",
"source": {
"type": "git",
"url": "https://github.com/bobthecow/psysh.git",
"reference": "b193cd020e8c6b66cea6457826ae005e94e6d2c0"
"reference": "cdb5593c3684bab74e10fcfffe4a0c8d1c39695d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/b193cd020e8c6b66cea6457826ae005e94e6d2c0",
"reference": "b193cd020e8c6b66cea6457826ae005e94e6d2c0",
"url": "https://api.github.com/repos/bobthecow/psysh/zipball/cdb5593c3684bab74e10fcfffe4a0c8d1c39695d",
"reference": "cdb5593c3684bab74e10fcfffe4a0c8d1c39695d",
"shasum": ""
},
"require": {
@ -4148,7 +4148,7 @@
"interactive",
"shell"
],
"time": "2017-07-29T19:30:02+00:00"
"time": "2017-10-19T06:13:20+00:00"
},
{
"name": "ramsey/uuid",
@ -5571,16 +5571,16 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.5.0",
"version": "v1.6.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803"
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
"reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
"reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296",
"shasum": ""
},
"require": {
@ -5592,7 +5592,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.5-dev"
"dev-master": "1.6-dev"
}
},
"autoload": {
@ -5626,7 +5626,7 @@
"portable",
"shim"
],
"time": "2017-06-14T15:44:48+00:00"
"time": "2017-10-11T12:05:26+00:00"
},
{
"name": "symfony/process",
@ -6346,16 +6346,16 @@
},
{
"name": "filp/whoops",
"version": "2.1.10",
"version": "2.1.12",
"source": {
"type": "git",
"url": "https://github.com/filp/whoops.git",
"reference": "ffbbd2c06c64b08fb47974eed5dbce4ca2bb0eec"
"reference": "a99f0b151846021ba7a73b4e3cba3ebc9f14f03e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/filp/whoops/zipball/ffbbd2c06c64b08fb47974eed5dbce4ca2bb0eec",
"reference": "ffbbd2c06c64b08fb47974eed5dbce4ca2bb0eec",
"url": "https://api.github.com/repos/filp/whoops/zipball/a99f0b151846021ba7a73b4e3cba3ebc9f14f03e",
"reference": "a99f0b151846021ba7a73b4e3cba3ebc9f14f03e",
"shasum": ""
},
"require": {
@ -6400,10 +6400,10 @@
"exception",
"handling",
"library",
"whoops",
"zf2"
"throwable",
"whoops"
],
"time": "2017-08-03T18:23:40+00:00"
"time": "2017-10-15T13:05:10+00:00"
},
{
"name": "fzaninotto/faker",
@ -6549,16 +6549,16 @@
},
{
"name": "laravel/dusk",
"version": "v2.0.6",
"version": "v2.0.7",
"source": {
"type": "git",
"url": "https://github.com/laravel/dusk.git",
"reference": "322de1489d7b5a09b7a04758e2dd71586a07b046"
"reference": "29127c21d72ab5bff53ed3b8eb914c0059c77763"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/dusk/zipball/322de1489d7b5a09b7a04758e2dd71586a07b046",
"reference": "322de1489d7b5a09b7a04758e2dd71586a07b046",
"url": "https://api.github.com/repos/laravel/dusk/zipball/29127c21d72ab5bff53ed3b8eb914c0059c77763",
"reference": "29127c21d72ab5bff53ed3b8eb914c0059c77763",
"shasum": ""
},
"require": {
@ -6606,7 +6606,7 @@
"testing",
"webdriver"
],
"time": "2017-10-02T14:18:54+00:00"
"time": "2017-10-09T16:10:59+00:00"
},
{
"name": "maximebf/debugbar",

View file

@ -17,7 +17,6 @@ class DatabaseSeeder extends Seeder
$this->call(PlacesTableSeeder::class);
$this->call(NotesTableSeeder::class);
$this->call(WebMentionsTableSeeder::class);
$this->call(IndieWebUserTableSeeder::class);
$this->call(LikesTableSeeder::class);
$this->call(BookmarksTableSeeder::class);
}

View file

@ -1,17 +0,0 @@
<?php
use App\IndieWebUser;
use Illuminate\Database\Seeder;
class IndieWebUserTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
IndieWebUser::create(['me' => config('app.url')]);
}
}

1401
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,36 +6,36 @@
"license": "CC0-1.0",
"dependencies": {
"alertify.js": "^1.0.12",
"mapbox-gl": "^0.39.1",
"mapbox-gl": "^0.41.0",
"marked": "^0.3.6",
"normalize.css": "^7.0.0",
"webStorage": "^1.2.4"
},
"devDependencies": {
"ajv": "^5.2.2",
"autoprefixer": "^7.1.4",
"ajv": "^5.2.3",
"autoprefixer": "^7.1.5",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0",
"babel-preset-env": "^1.6.1",
"babel-preset-es2015": "^6.18.0",
"babel-preset-latest": "^6.16.0",
"babel-runtime": "^6.26.0",
"dotenv-webpack": "^1.5.4",
"eslint": "^4.6.1",
"eslint": "^4.9.0",
"eslint-config-standard": "^10.2.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.1.1",
"eslint-plugin-promise": "^3.5.0",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-node": "^5.2.0",
"eslint-plugin-promise": "^3.6.0",
"eslint-plugin-standard": "^3.0.1",
"lint-staged": "^4.1.3",
"postcss-cli": "^4.1.0",
"lint-staged": "^4.3.0",
"postcss-cli": "^4.1.1",
"pre-commit": "^1.1.3",
"source-list-map": "^2.0.0",
"stylelint": "^8.1.1",
"stylelint": "^8.2.0",
"stylelint-config-standard": "^17.0.0",
"uglify-js": "^3.0.28",
"webpack": "^3.5.6",
"uglify-js": "^3.1.4",
"webpack": "^3.8.1",
"webpack-sources": "^1.0.1"
},
"scripts": {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -1,2 +1,2 @@
!function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}var installedModules={};__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{configurable:!1,enumerable:!0,get:getter})},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function(){return module.default}:function(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=16)}({16:function(module,exports,__webpack_require__){"use strict";(function(process){var idSite=process.env.PIWIK_ID,piwikTrackingApiUrl=process.env.PIWIK_URL,_paq=_paq||[];_paq.push(["setTrackerUrl",piwikTrackingApiUrl]),_paq.push(["setSiteId",idSite]),_paq.push(["trackPageView"]),_paq.push(["enableLinkTracking"])}).call(exports,__webpack_require__(17))},17:function(module,exports){function defaultSetTimout(){throw new Error("setTimeout has not been defined")}function defaultClearTimeout(){throw new Error("clearTimeout has not been defined")}function runTimeout(fun){if(cachedSetTimeout===setTimeout)return setTimeout(fun,0);if((cachedSetTimeout===defaultSetTimout||!cachedSetTimeout)&&setTimeout)return cachedSetTimeout=setTimeout,setTimeout(fun,0);try{return cachedSetTimeout(fun,0)}catch(e){try{return cachedSetTimeout.call(null,fun,0)}catch(e){return cachedSetTimeout.call(this,fun,0)}}}function runClearTimeout(marker){if(cachedClearTimeout===clearTimeout)return clearTimeout(marker);if((cachedClearTimeout===defaultClearTimeout||!cachedClearTimeout)&&clearTimeout)return cachedClearTimeout=clearTimeout,clearTimeout(marker);try{return cachedClearTimeout(marker)}catch(e){try{return cachedClearTimeout.call(null,marker)}catch(e){return cachedClearTimeout.call(this,marker)}}}function cleanUpNextTick(){draining&&currentQueue&&(draining=!1,currentQueue.length?queue=currentQueue.concat(queue):queueIndex=-1,queue.length&&drainQueue())}function drainQueue(){if(!draining){var timeout=runTimeout(cleanUpNextTick);draining=!0;for(var len=queue.length;len;){for(currentQueue=queue,queue=[];++queueIndex<len;)currentQueue&&currentQueue[queueIndex].run();queueIndex=-1,len=queue.length}currentQueue=null,draining=!1,runClearTimeout(timeout)}}function Item(fun,array){this.fun=fun,this.array=array}function noop(){}var cachedSetTimeout,cachedClearTimeout,process=module.exports={};!function(){try{cachedSetTimeout="function"==typeof setTimeout?setTimeout:defaultSetTimout}catch(e){cachedSetTimeout=defaultSetTimout}try{cachedClearTimeout="function"==typeof clearTimeout?clearTimeout:defaultClearTimeout}catch(e){cachedClearTimeout=defaultClearTimeout}}();var currentQueue,queue=[],draining=!1,queueIndex=-1;process.nextTick=function(fun){var args=new Array(arguments.length-1);if(arguments.length>1)for(var i=1;i<arguments.length;i++)args[i-1]=arguments[i];queue.push(new Item(fun,args)),1!==queue.length||draining||runTimeout(drainQueue)},Item.prototype.run=function(){this.fun.apply(null,this.array)},process.title="browser",process.browser=!0,process.env={},process.argv=[],process.version="",process.versions={},process.on=noop,process.addListener=noop,process.once=noop,process.off=noop,process.removeListener=noop,process.removeAllListeners=noop,process.emit=noop,process.prependListener=noop,process.prependOnceListener=noop,process.listeners=function(name){return[]},process.binding=function(name){throw new Error("process.binding is not supported")},process.cwd=function(){return"/"},process.chdir=function(dir){throw new Error("process.chdir is not supported")},process.umask=function(){return 0}}});
!function(modules){function __webpack_require__(moduleId){if(installedModules[moduleId])return installedModules[moduleId].exports;var module=installedModules[moduleId]={i:moduleId,l:!1,exports:{}};return modules[moduleId].call(module.exports,module,module.exports,__webpack_require__),module.l=!0,module.exports}var installedModules={};__webpack_require__.m=modules,__webpack_require__.c=installedModules,__webpack_require__.d=function(exports,name,getter){__webpack_require__.o(exports,name)||Object.defineProperty(exports,name,{configurable:!1,enumerable:!0,get:getter})},__webpack_require__.n=function(module){var getter=module&&module.__esModule?function(){return module.default}:function(){return module};return __webpack_require__.d(getter,"a",getter),getter},__webpack_require__.o=function(object,property){return Object.prototype.hasOwnProperty.call(object,property)},__webpack_require__.p="",__webpack_require__(__webpack_require__.s=16)}({16:function(module,exports,__webpack_require__){"use strict";var _paq=_paq||[];_paq.push(["setTrackerUrl","https://analytics.jmb.lv/piwik.php"]),_paq.push(["setSiteId","1"]),_paq.push(["trackPageView"]),_paq.push(["enableLinkTracking"])}});
//# sourceMappingURL=piwik.js.map

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -120,26 +120,6 @@ Route::group(['domain' => config('url.longurl')], function () {
Route::get('/{bookmark}', 'BookmarksController@show');
});
// Micropub Client
Route::group(['prefix' => 'micropub'], function () {
Route::get('/create', 'MicropubClientController@create')->name('micropub-client');
Route::post('/', 'MicropubClientController@store')->name('micropub-client-post');
Route::get('/config', 'MicropubClientController@config')->name('micropub-config');
Route::get('/get-new-token', 'MicropubClientController@getNewToken')->name('micropub-client-get-new-token');
Route::get('/get-new-token/callback', 'MicropubClientController@getNewTokenCallback')->name('micropub-client-get-new-token-callback');
Route::get('/query-endpoint', 'MicropubClientController@queryEndpoint')->name('micropub-query-action');
Route::post('/update-syntax', 'MicropubClientController@updateSyntax')->name('micropub-update-syntax');
Route::get('/places', 'MicropubClientController@nearbyPlaces');
Route::post('/places', 'MicropubClientController@newPlace');
Route::post('/media', 'MicropubClientController@processMedia')->name('process-media');
Route::get('/media/clearlinks', 'MicropubClientController@clearLinks');
});
// IndieAuth
Route::post('indieauth/start', 'IndieAuthController@start')->name('indieauth-start');
Route::get('indieauth/callback', 'IndieAuthController@callback')->name('indieauth-callback');
Route::get('logout', 'IndieAuthController@logout')->name('indieauth-logout');
// Token Endpoint
Route::post('api/token', 'TokenEndpointController@create');

View file

@ -1,39 +0,0 @@
<?php
namespace Tests\Browser;
use Tests\DuskTestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
class MicropubClientTest extends DuskTestCase
{
/**
* Test the client is shown for an unauthorised request.
*
* @return void
*/
public function test_client_page_see_authenticated()
{
$this->browse(function ($browser) {
$browser->visit(route('micropub-client'))
->assertSee('You are authenticated as');
});
}
public function test_client_page_creates_new_note()
{
\Artisan::call('token:generate');
$faker = \Faker\Factory::create();
$note = 'Fake note from #LaravelDusk: ' . $faker->text;
$this->browse(function ($browser) use ($note) {
$response = $browser->visit(route('micropub-client'))
->assertSeeLink('log out')
->type('content', $note);
$response->element('form')->submit();
});
sleep(2);
$this->assertDatabaseHas('notes', ['note' => $note]);
$newNote = \App\Note::where('note', $note)->first();
$newNote->forceDelete();
}
}

View file

@ -1,49 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
class IndieAuthControllerTest extends TestCase
{
/**
* Test the `start` method redirects to the client on error.
*
* @return void
*/
public function test_indieauthcontroller_begin_auth_flow_redirects_back_to_client_on_error()
{
$response = $this->call('POST', '/indieauth/start', ['me' => 'http://example.org']);
$this->assertSame(route('micropub-client'), $response->headers->get('Location'));
}
/**
* Now we test the `start` method as a whole.
*
* @return void
*/
public function test_indieauthcontroller_begin_auth_redirects_to_endpoint()
{
$response = $this->call('POST', '/indieauth/start', ['me' => config('app.url')]);
$this->assertSame(
'https://indieauth.com/auth?me=',
substr($response->headers->get('Location'), 0, 30)
);
}
/**
* Test the `callback` method.
*
* @return void
*/
public function test_indieauthcontroller_callback_method_gives_error_with_mismatched_state()
{
$response = $this->withSession(['state' => 'state-session'])
->call(
'GET',
'indieauth/callback',
['me', config('app.url'), 'state' => 'request-session']
);
$response->assertSessionHas(['error' => 'Invalid <code>state</code> value returned from indieauth server']);
}
}

View file

@ -1,55 +0,0 @@
<?php
namespace Tests\Feature;
use Tests\TestCase;
use GuzzleHttp\Client;
use GuzzleHttp\Middleware;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Handler\MockHandler;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class MicropubClientControllerTest extends TestCase
{
use DatabaseTransactions;
public function test_json_syntax_is_created_correctly()
{
/*
$container = [];
$history = Middleware::history($container);
$mock = new MockHandler([
new Response(201, ['Location' => 'http://example.org/a'], 'Created'),
]);
$stack = HandlerStack::create($mock);
// add the history middleware to the stack
$stack->push($history);
$client = new Client(['handler' => $stack]);
$this->app->instance(Client::class, $client);
$response = $this->post(
'/micropub',
[
'content' => 'Hello Fred',
'in-reply-to' => 'https://fredbloggs.com/note/abc',
'mp-syndicate-to' => ['https://twitter.com/jonnybarnes', 'https://facebook.com/jonnybarnes'],
]
);
$expected = '{"type":["h-entry"],"properties":{"content":["Hello Fred"],"in-reply-to":["https:\/\/fredbloggs.com\/note\/abc"],"mp-syndicate-to":["https:\/\/twitter.com\/jonnybarnes","https:\/\/facebook.com\/jonnybarnes"]}}';
if (count($container) === 0) {
$this->fail();
}
foreach ($container as $transaction) {
$this->assertEquals($expected, $transaction['request']->getBody()->getContents());
}
*/
$this->assertTrue(true);
}
}

View file

@ -24,8 +24,8 @@ class TokenEndpointTest extends TestCase
$response = $this->post('/api/token', [
'me' => config('app.url'),
'code' => 'abc123',
'redirect_uri' => route('indieauth-callback'),
'client_id' => route('micropub-client'),
'redirect_uri' => config('app.url') . '/indieauth-callback',
'client_id' => config('app.url') . '/micropub-client',
'state' => mt_rand(1000, 10000),
]);
parse_str($response->content(), $output);

View file

@ -1,75 +0,0 @@
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Services\IndieAuthService;
class IndieAuthServiceTest extends TestCase
{
/**
* Test the getAuthorizationEndpoint method.
*
* @return void
*/
public function test_indieauthservice_getauthorizationendpoint_method()
{
$service = new IndieAuthService();
$result = $service->getAuthorizationEndpoint(config('app.url'));
$this->assertEquals('https://indieauth.com/auth', $result);
}
/**
* Test the getAuthorizationEndpoint method returns null on failure.
*
* @return void
*/
public function test_indieauthservice_getauthorizationendpoint_method_returns_null_on_failure()
{
$service = new IndieAuthService();
$result = $service->getAuthorizationEndpoint('http://example.org');
$this->assertEquals(null, $result);
}
/**
* Test that the Service build the correct redirect URL.
*
* @return void
*/
public function test_indieauthservice_builds_correct_redirect_url()
{
$service = new IndieAuthService();
$result = $service->buildAuthorizationURL(
'https://indieauth.com/auth',
config('app.url')
);
$this->assertEquals(
'https://indieauth.com/auth?me=',
substr($result, 0, 30)
);
}
/**
* Test the getTokenEndpoint method.
*
* @return void
*/
public function test_indieauthservice_gettokenendpoint_method()
{
$service = new IndieAuthService();
$result = $service->getTokenEndpoint(config('app.url'));
$this->assertEquals(config('app.url') . '/api/token', $result);
}
/**
* Test the discoverMicropubEndpoint method.
*
* @return void
*/
public function test_indieauthservice_discovermicropubendpoint_method()
{
$service = new IndieAuthService();
$result = $service->discoverMicropubEndpoint(config('app.url'));
$this->assertEquals(config('app.url') . '/api/post', $result);
}
}