mirror of
https://gitnet.fr/deblan/gist.git
synced 2021-08-14 08:30:49 +02:00
Merge branch 'feature/api' into dev-master
This commit is contained in:
commit
9fe6510fc9
@ -3,5 +3,11 @@
|
||||
use Gist\Api\Client;
|
||||
|
||||
$app['api_client'] = $app->share(function ($app) {
|
||||
return new Client(['base_uri' => rtrim($app['settings']['api']['base_url'], '/')]);
|
||||
$client = new Client(['base_uri' => rtrim($app['settings']['api']['base_url'], '/')]);
|
||||
|
||||
if (!empty($app['settings']['api']['client']['api_key'])) {
|
||||
$client->setApiKey($app['settings']['api']['client']['api_key']);
|
||||
}
|
||||
|
||||
return $client;
|
||||
});
|
||||
|
@ -6,7 +6,11 @@ security:
|
||||
login_required_to_view_gist: false
|
||||
login_required_to_view_embeded_gist: false
|
||||
api:
|
||||
enabled: true
|
||||
api_key_required: false
|
||||
base_url: 'https://gist.deblan.org/'
|
||||
client:
|
||||
api_key:
|
||||
data:
|
||||
path: data/git
|
||||
git:
|
||||
|
@ -57,10 +57,18 @@ revisions:
|
||||
path: /revs/{gist}
|
||||
defaults: {_controller: Gist\Controller\ViewController::revisionsAction, _locale: en}
|
||||
|
||||
api_list:
|
||||
path: /api/list/{apiKey}
|
||||
defaults: {_controller: Gist\Controller\ApiController::listAction, _locale: en, apiKey: null}
|
||||
|
||||
api_create:
|
||||
path: /api/create
|
||||
defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en}
|
||||
path: /api/create/{apiKey}
|
||||
defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en, apiKey: null}
|
||||
|
||||
api_update:
|
||||
path: /api/update/{gist}
|
||||
defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en}
|
||||
path: /api/update/{gist}/{apiKey}
|
||||
defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en, apiKey: null}
|
||||
|
||||
api_delete:
|
||||
path: /api/delete/{gist}/{apiKey}
|
||||
defaults: {_controller: Gist\Controller\ApiController::deleteAction, _locale: en, apiKey: null}
|
||||
|
@ -2,15 +2,19 @@
|
||||
<?php
|
||||
|
||||
use Gist\Command\CreateCommand;
|
||||
use Gist\Command\ListCommand;
|
||||
use Gist\Command\UpdateCommand;
|
||||
use Gist\Command\StatsCommand;
|
||||
use Gist\Command\DeleteCommand;
|
||||
use Gist\Command\UserCreateCommand;
|
||||
use Gist\Command\Migration\UpgradeTo1p4p1Command;
|
||||
|
||||
$app = require __DIR__.'/bootstrap.php';
|
||||
|
||||
$app['console']->add(new CreateCommand());
|
||||
$app['console']->add(new ListCommand());
|
||||
$app['console']->add(new UpdateCommand());
|
||||
$app['console']->add(new DeleteCommand());
|
||||
$app['console']->add(new StatsCommand());
|
||||
$app['console']->add(new UserCreateCommand());
|
||||
$app['console']->add(new UpgradeTo1p4p1Command());
|
||||
|
@ -20,6 +20,11 @@ app:
|
||||
my:
|
||||
title: '我的 Gist'
|
||||
nothing: '这家伙很懒,暂时没有内容'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
gist:
|
||||
untitled: '未命名'
|
||||
|
@ -20,6 +20,11 @@ app:
|
||||
my:
|
||||
title: 'Meine Gists'
|
||||
nothing: 'Nichts zu finden (momentan)!'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
gist:
|
||||
untitled: 'Ohne Titel'
|
||||
|
@ -20,6 +20,12 @@ app:
|
||||
my:
|
||||
title: 'My gists'
|
||||
nothing: 'Nothing yet!'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
|
||||
gist:
|
||||
untitled: 'Untitled'
|
||||
|
@ -20,6 +20,11 @@ app:
|
||||
my:
|
||||
title: 'Mis Gists'
|
||||
nothing: 'Nada por ahora.'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Keep it <strong>secret!</strong>'
|
||||
form:
|
||||
generate: 'Regenerate'
|
||||
|
||||
gist:
|
||||
untitled: 'Sin título'
|
||||
|
@ -20,6 +20,11 @@ app:
|
||||
my:
|
||||
title: 'Mes Gists'
|
||||
nothing: 'Rien pour le moment !'
|
||||
api:
|
||||
title: 'API'
|
||||
warning: 'Gardez-la <strong>secrète !</strong>'
|
||||
form:
|
||||
generate: 'Regénérer'
|
||||
|
||||
gist:
|
||||
untitled: 'Sans titre'
|
||||
|
@ -12,31 +12,53 @@ use GuzzleHttp\Client as BaseClient;
|
||||
class Client extends BaseClient
|
||||
{
|
||||
/**
|
||||
* URI of creation
|
||||
*
|
||||
* URI of creation.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const CREATE = '/en/api/create';
|
||||
|
||||
/**
|
||||
* URI of updating
|
||||
* URI of update.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const UPDATE = '/en/api/update/{gist}';
|
||||
|
||||
/**
|
||||
* Creates a gist
|
||||
* URI of delete.
|
||||
*
|
||||
* @param string $title The title
|
||||
* @param string $type The type
|
||||
* @const string
|
||||
*/
|
||||
const DELETE = '/en/api/delete/{gist}';
|
||||
|
||||
/**
|
||||
* URI of list.
|
||||
*
|
||||
* @const string
|
||||
*/
|
||||
const LIST = '/en/api/list';
|
||||
|
||||
/**
|
||||
* The API key.
|
||||
*
|
||||
* @var string|null
|
||||
*/
|
||||
protected $apiKey;
|
||||
|
||||
/**
|
||||
* Creates a gist.
|
||||
*
|
||||
* @param string $title The title
|
||||
* @param string $type The type
|
||||
* @param string $content The content
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function create($title, $type, $content)
|
||||
{
|
||||
$response = $this->post(
|
||||
self::CREATE,
|
||||
$this->mergeApiKey(self::CREATE),
|
||||
array(
|
||||
'form_params' => array(
|
||||
'form' => array(
|
||||
@ -56,9 +78,9 @@ class Client extends BaseClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Clones and update a gist
|
||||
* Clones and update a gist.
|
||||
*
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $content The content
|
||||
*
|
||||
* @return array
|
||||
@ -66,7 +88,7 @@ class Client extends BaseClient
|
||||
public function update($gist, $content)
|
||||
{
|
||||
$response = $this->post(
|
||||
str_replace('{gist}', $gist, self::UPDATE),
|
||||
str_replace('{gist}', $gist, $this->mergeApiKey(self::LIST)),
|
||||
array(
|
||||
'form_params' => array(
|
||||
'form' => array(
|
||||
@ -82,4 +104,81 @@ class Client extends BaseClient
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a gist.
|
||||
*
|
||||
* @param string $gist Gist's ID
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function delete($gist)
|
||||
{
|
||||
$response = $this->post(str_replace('{gist}', $gist, $this->mergeApiKey(self::DELETE)));
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
return json_decode($response->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Lists the user's gists.
|
||||
*
|
||||
* @param string $gist Gist's ID
|
||||
* @param string $content The content
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function list()
|
||||
{
|
||||
$response = $this->get($this->mergeApiKey(self::LIST));
|
||||
|
||||
if ($response->getStatusCode() === 200) {
|
||||
return json_decode($response->getBody()->getContents(), true);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/*
|
||||
* Merges the API key with the given url.
|
||||
*
|
||||
* @param string $url
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function mergeApiKey($url)
|
||||
{
|
||||
if (empty($this->apiKey)) {
|
||||
return $url;
|
||||
}
|
||||
|
||||
return rtrim($url, '/').'/'.$this->apiKey;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the value of "apiKey".
|
||||
*
|
||||
* @param string|null $apiKey
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function setApiKey($apiKey)
|
||||
{
|
||||
$this->apiKey = $apiKey;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the value of "apiKey".
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApiKey()
|
||||
{
|
||||
return $this->apiKey;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Propel\Runtime\Parser\YamlParser;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* class CreateCommand.
|
||||
@ -27,8 +29,9 @@ class CreateCommand extends Command
|
||||
->addArgument('input', InputArgument::REQUIRED, 'Input')
|
||||
->addArgument('type', InputArgument::OPTIONAL, 'Type', 'text')
|
||||
->addOption('title', 't', InputOption::VALUE_REQUIRED, 'Title of the gist')
|
||||
->addOption('show-url', 'u', InputOption::VALUE_NONE, 'Display only the gist url')
|
||||
->addOption('show-id', 'i', InputOption::VALUE_NONE, 'Display only the gist Id')
|
||||
->addOption('all', 'a', InputOption::VALUE_NONE, 'Display all the response')
|
||||
->addOption('id', 'i', InputOption::VALUE_NONE, 'Display only the id of the gist')
|
||||
->addOption('json', 'j', InputOption::VALUE_NONE, 'Format the response to json')
|
||||
->setHelp(<<<EOF
|
||||
Provides a client to create a gist using the API.
|
||||
|
||||
@ -43,12 +46,15 @@ Arguments:
|
||||
Options:
|
||||
<info>--title</info>, <info>-t</info>
|
||||
Defines a title
|
||||
|
||||
<info>--show-id</info>, <info>-i</info>
|
||||
Display only the Id of the gist
|
||||
|
||||
<info>--show-url</info>, <info>-u</info>
|
||||
Display only the url of the gist
|
||||
<info>--id</info>, <info>-i</info>
|
||||
Display only the id of the gist
|
||||
|
||||
<info>--all</info>, <info>-a</info>
|
||||
Display all the response
|
||||
|
||||
<info>--json</info>, <info>-j</info>
|
||||
Format the response to json
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@ -58,11 +64,10 @@ EOF
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
//$output->writeln(sprintf('<comment>%s</comment> bar.', 'test'));
|
||||
|
||||
$file = $input->getArgument('input');
|
||||
$type = $input->getArgument('type');
|
||||
$title = $input->getOption('title');
|
||||
$json = $input->getOption('json');
|
||||
|
||||
if ($file === '-') {
|
||||
$content = file_get_contents('php://stdin');
|
||||
@ -94,19 +99,17 @@ EOF
|
||||
|
||||
$gist = $this->getSilexApplication()['api_client']->create($title, $type, $content);
|
||||
|
||||
if ($input->getOption('show-url')) {
|
||||
$output->writeln($gist['url']);
|
||||
|
||||
return true;
|
||||
if ($input->getOption('id')) {
|
||||
$id = isset($gist['gist']['id']) ? $gist['gist']['id'] : $gist['gist']['Id'];
|
||||
$result = $json ? json_encode(array('id' => $id)) : $id;
|
||||
} elseif ($input->getOption('all')) {
|
||||
$result = $json ? json_encode($gist) : Yaml::dump($gist);
|
||||
} else {
|
||||
$url = $gist['url'];
|
||||
$result = $json ? json_encode(array('url' => $url)) : $url;
|
||||
}
|
||||
|
||||
if ($input->getOption('show-id')) {
|
||||
$output->writeln($gist['gist']['Id']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$output->writeln(json_encode($gist));
|
||||
$output->writeln($result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
48
src/Gist/Command/DeleteCommand.php
Normal file
48
src/Gist/Command/DeleteCommand.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
namespace Gist\Command;
|
||||
|
||||
use Knp\Command\Command;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
/**
|
||||
* class DeleteCommand.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class DeleteCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('delete')
|
||||
->setDescription('Delete a gist using the API')
|
||||
->addOption('gist', null, InputOption::VALUE_REQUIRED, 'Id or File of the gist')
|
||||
->setHelp(<<<'EOF'
|
||||
Provides a client to delete a gist using the API.
|
||||
|
||||
Arguments:
|
||||
none.
|
||||
|
||||
Options:
|
||||
<info>--gist</info>
|
||||
Defines the Gist to delete by using its Id or its File
|
||||
EOF
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$result = $this->getSilexApplication()['api_client']->delete($input->getOption('gist'));
|
||||
|
||||
$output->writeln(empty($result['error']) ? 'OK' : '<error>An error occured.</error>');
|
||||
}
|
||||
}
|
59
src/Gist/Command/ListCommand.php
Normal file
59
src/Gist/Command/ListCommand.php
Normal file
@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
namespace Gist\Command;
|
||||
|
||||
use Knp\Command\Command;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Propel\Runtime\Parser\YamlParser;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* class ListCommand.
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class ListCommand extends Command
|
||||
{
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('gists')
|
||||
->setDescription('List gists using the API');
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$gists = $this->getSilexApplication()['api_client']->list();
|
||||
$rows = [];
|
||||
|
||||
foreach ($gists as $gist) {
|
||||
$rows[] = array(
|
||||
$gist['id'],
|
||||
$gist['title'],
|
||||
$gist['cipher'] ? 'y' : 'n',
|
||||
$gist['type'],
|
||||
(new DateTime($gist['createdAt']))->format('Y-m-d H:i:s'),
|
||||
(new DateTime($gist['updatedAt']))->format('Y-m-d H:i:s'),
|
||||
$gist['url'],
|
||||
);
|
||||
}
|
||||
|
||||
$table = new Table($output);
|
||||
$table
|
||||
->setHeaders(array('ID', 'Title', 'Cipher', 'Type', 'Created At', 'Updated At', 'url'))
|
||||
->setRows($rows);
|
||||
|
||||
$table->render();
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* class UpdateCommand.
|
||||
@ -26,8 +27,9 @@ class UpdateCommand extends Command
|
||||
->setDescription('Update a gist using the API')
|
||||
->addArgument('input', InputArgument::REQUIRED, 'Input')
|
||||
->addOption('gist', null, InputOption::VALUE_REQUIRED, 'Id or File of the gist')
|
||||
->addOption('show-url', 'u', InputOption::VALUE_NONE, 'Display only the gist url')
|
||||
->addOption('show-id', 'i', InputOption::VALUE_NONE, 'Display only the gist Id')
|
||||
->addOption('all', 'a', InputOption::VALUE_NONE, 'Display all the response')
|
||||
->addOption('id', 'i', InputOption::VALUE_NONE, 'Display only the id of the gist')
|
||||
->addOption('json', 'j', InputOption::VALUE_NONE, 'Format the response to json')
|
||||
->setHelp(<<<EOF
|
||||
Provides a client to create a gist using the API.
|
||||
|
||||
@ -43,11 +45,14 @@ Options:
|
||||
<info>--gist</info>
|
||||
Defines the Gist to update by using its Id or its File
|
||||
|
||||
<info>--show-id</info>, <info>-i</info>
|
||||
Display only the Id of the gist
|
||||
<info>--id</info>, <info>-i</info>
|
||||
Display only the id of the gist
|
||||
|
||||
<info>--show-url</info>, <info>-u</info>
|
||||
Display only the url of the gist
|
||||
<info>--all</info>, <info>-a</info>
|
||||
Display all the response
|
||||
|
||||
<info>--json</info>, <info>-j</info>
|
||||
Format the response to json
|
||||
EOF
|
||||
);
|
||||
}
|
||||
@ -57,10 +62,9 @@ EOF
|
||||
*/
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
//$output->writeln(sprintf('<comment>%s</comment> bar.', 'test'));
|
||||
|
||||
$file = $input->getArgument('input');
|
||||
$gist = $input->getOption('gist');
|
||||
$json = $input->getOption('json');
|
||||
|
||||
if ($file === '-') {
|
||||
$content = file_get_contents('php://stdin');
|
||||
@ -86,19 +90,17 @@ EOF
|
||||
|
||||
$gist = $this->getSilexApplication()['api_client']->update($gist, $content);
|
||||
|
||||
if ($input->getOption('show-url')) {
|
||||
$output->writeln($gist['url']);
|
||||
|
||||
return true;
|
||||
if ($input->getOption('id')) {
|
||||
$id = isset($gist['gist']['id']) ? $gist['gist']['id'] : $gist['gist']['Id'];
|
||||
$result = $json ? json_encode(array('id' => $id)) : $id;
|
||||
} elseif ($input->getOption('all')) {
|
||||
$result = $json ? json_encode($gist) : Yaml::dump($gist);
|
||||
} else {
|
||||
$url = $gist['url'];
|
||||
$result = $json ? json_encode(array('url' => $url)) : $url;
|
||||
}
|
||||
|
||||
if ($input->getOption('show-id')) {
|
||||
$output->writeln($gist['gist']['Id']);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
$output->writeln(json_encode($gist));
|
||||
$output->writeln($result);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -8,6 +8,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Gist\Form\ApiCreateGistForm;
|
||||
use Gist\Model\GistQuery;
|
||||
use Gist\Form\ApiUpdateGistForm;
|
||||
use GitWrapper\GitException;
|
||||
use Gist\Model\UserQuery;
|
||||
|
||||
/**
|
||||
* Class ApiController.
|
||||
@ -16,10 +18,75 @@ use Gist\Form\ApiUpdateGistForm;
|
||||
*/
|
||||
class ApiController extends Controller
|
||||
{
|
||||
public function createAction(Request $request)
|
||||
/**
|
||||
* Lists gists.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $apiKey
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function listAction(Request $request, $apiKey)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (false === $app['settings']['api']['enabled']) {
|
||||
return new Response('', 403);
|
||||
}
|
||||
|
||||
if (false === $this->isValidApiKey($apiKey, true)) {
|
||||
return $this->invalidApiKeyResponse();
|
||||
}
|
||||
|
||||
if (false === $request->isMethod('get')) {
|
||||
return $this->invalidMethodResponse('GET method is required.');
|
||||
}
|
||||
|
||||
$user = $app['user.provider']->loadUserByApiKey($apiKey);
|
||||
$gists = $user->getGists();
|
||||
$data = array();
|
||||
|
||||
foreach ($gists as $gist) {
|
||||
try {
|
||||
$history = $app['gist']->getHistory($gist);
|
||||
|
||||
$value = $gist->toArray();
|
||||
$value['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate(
|
||||
'view',
|
||||
array(
|
||||
'gist' => $gist->getFile(),
|
||||
'commit' => array_pop($history)['commit'],
|
||||
)
|
||||
);
|
||||
|
||||
$data[] = $value;
|
||||
} catch (GitException $e) {
|
||||
}
|
||||
}
|
||||
|
||||
return new JsonResponse($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a gist.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $apiKey
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function createAction(Request $request, $apiKey)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (false === $app['settings']['api']['enabled']) {
|
||||
return new Response('', 403);
|
||||
}
|
||||
|
||||
if (false === $this->isValidApiKey($apiKey, (bool) $app['settings']['api']['api_key_required'])) {
|
||||
return $this->invalidApiKeyResponse();
|
||||
}
|
||||
|
||||
if (false === $request->isMethod('post')) {
|
||||
return $this->invalidMethodResponse('POST method is required.');
|
||||
}
|
||||
@ -36,30 +103,51 @@ class ApiController extends Controller
|
||||
$form->submit($request);
|
||||
|
||||
if ($form->isValid()) {
|
||||
$user = !empty($apiKey) ? $app['user.provider']->loadUserByApiKey($apiKey) : null;
|
||||
$gist = $app['gist']->create(new Gist(), $form->getData());
|
||||
$gist->setCipher(false)->save();
|
||||
$gist
|
||||
->setCipher(false)
|
||||
->setUser($user)
|
||||
->save();
|
||||
|
||||
$history = $app['gist']->getHistory($gist);
|
||||
|
||||
return new JsonResponse(array(
|
||||
'url' => $request->getSchemeAndHttpHost().$app['url_generator']->generate(
|
||||
'view',
|
||||
array(
|
||||
'gist' => $gist->getFile(),
|
||||
'commit' => array_pop($history)['commit'],
|
||||
)
|
||||
),
|
||||
'gist' => $gist->toArray(),
|
||||
));
|
||||
$data = $gist->toArray();
|
||||
$data['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate(
|
||||
'view',
|
||||
array(
|
||||
'gist' => $gist->getFile(),
|
||||
'commit' => array_pop($history)['commit'],
|
||||
)
|
||||
);
|
||||
|
||||
return new JsonResponse($data);
|
||||
}
|
||||
|
||||
return $this->invalidRequestResponse('Invalid field(s)');
|
||||
}
|
||||
|
||||
public function updateAction(Request $request, $gist)
|
||||
/**
|
||||
* Updates a gist.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist
|
||||
* @param string $apiKey
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function updateAction(Request $request, $gist, $apiKey)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (false === $app['settings']['api']['enabled']) {
|
||||
return new Response('', 403);
|
||||
}
|
||||
|
||||
if (false === $this->isValidApiKey($apiKey, (bool) $app['settings']['api']['api_key_required'])) {
|
||||
return $this->invalidApiKeyResponse();
|
||||
}
|
||||
|
||||
if (false === $request->isMethod('post')) {
|
||||
return $this->invalidMethodResponse('POST method is required.');
|
||||
}
|
||||
@ -91,21 +179,88 @@ class ApiController extends Controller
|
||||
|
||||
$history = $app['gist']->getHistory($gist);
|
||||
|
||||
return new JsonResponse(array(
|
||||
'url' => $request->getSchemeAndHttpHost().$app['url_generator']->generate(
|
||||
'view',
|
||||
array(
|
||||
'gist' => $gist->getFile(),
|
||||
'commit' => array_pop($history)['commit'],
|
||||
)
|
||||
),
|
||||
'gist' => $gist->toArray(),
|
||||
));
|
||||
$data = $gist->toArray();
|
||||
$data['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate(
|
||||
'view',
|
||||
array(
|
||||
'gist' => $gist->getFile(),
|
||||
'commit' => array_pop($history)['commit'],
|
||||
)
|
||||
);
|
||||
|
||||
return new JsonResponse($data);
|
||||
}
|
||||
|
||||
return $this->invalidRequestResponse('Invalid field(s)');
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a gist.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $gist
|
||||
* @param string $apiKey
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function deleteAction(Request $request, $gist, $apiKey)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (false === $app['settings']['api']['enabled']) {
|
||||
return new Response('', 403);
|
||||
}
|
||||
|
||||
if (false === $this->isValidApiKey($apiKey, true)) {
|
||||
return $this->invalidApiKeyResponse();
|
||||
}
|
||||
|
||||
if (false === $request->isMethod('post')) {
|
||||
return $this->invalidMethodResponse('POST method is required.');
|
||||
}
|
||||
|
||||
$user = $app['user.provider']->loadUserByApiKey($apiKey);
|
||||
|
||||
$gist = GistQuery::create()
|
||||
->filterById((int) $gist)
|
||||
->_or()
|
||||
->filterByFile($gist)
|
||||
->filterByUser($user)
|
||||
->findOne();
|
||||
|
||||
if (!$gist) {
|
||||
return $this->invalidRequestResponse('Invalid Gist');
|
||||
}
|
||||
|
||||
$gist->delete();
|
||||
|
||||
return new JsonResponse(['error' => false]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an invalid api key response.
|
||||
*
|
||||
* @param mixed $message
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
protected function invalidApiKeyResponse()
|
||||
{
|
||||
$data = [
|
||||
'error' => ' Unauthorized',
|
||||
'message' => 'Invalid API KEY',
|
||||
];
|
||||
|
||||
return new JsonResponse($data, 401);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an invalid method response.
|
||||
*
|
||||
* @param mixed $message
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
protected function invalidMethodResponse($message = null)
|
||||
{
|
||||
$data = [
|
||||
@ -116,6 +271,13 @@ class ApiController extends Controller
|
||||
return new JsonResponse($data, 405);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds an invalid request response.
|
||||
*
|
||||
* @param mixed $message
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
protected function invalidRequestResponse($message = null)
|
||||
{
|
||||
$data = [
|
||||
@ -125,4 +287,24 @@ class ApiController extends Controller
|
||||
|
||||
return new JsonResponse($data, 400);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given api key is valid
|
||||
* depending of the requirement.
|
||||
*
|
||||
* @param mixed $apiKey
|
||||
* @param mixed $required
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function isValidApiKey($apiKey, $required = false)
|
||||
{
|
||||
if (empty($apiKey)) {
|
||||
return !$required;
|
||||
}
|
||||
|
||||
return UserQuery::create()
|
||||
->filterByApiKey($apiKey)
|
||||
->count() === 1;
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ use Symfony\Component\HttpFoundation\Response;
|
||||
*
|
||||
* @author Simon Vieille <simon@deblan.fr>
|
||||
*/
|
||||
class Controller
|
||||
abstract class Controller
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
@ -128,12 +128,18 @@ class Controller
|
||||
/**
|
||||
* Returns the connected user.
|
||||
*
|
||||
* @param Request $request An API request
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getUser()
|
||||
public function getUser(Request $request = null)
|
||||
{
|
||||
$app = $this->getApp();
|
||||
|
||||
if (!empty($request)) {
|
||||
|
||||
}
|
||||
|
||||
$securityContext = $app['security.token_storage'];
|
||||
$securityToken = $securityContext->getToken();
|
||||
|
||||
|
@ -58,6 +58,26 @@ class MyController extends Controller
|
||||
|
||||
$gists = $this->getUser()->getGistsPager($page, $options);
|
||||
|
||||
$apiKey = $this->getUser()->getApiKey();
|
||||
|
||||
if (empty($apiKey)) {
|
||||
$regenerateApiKey = true;
|
||||
}
|
||||
// FIXME: CSRF issue!
|
||||
elseif ($request->request->get('apiKey') === $apiKey && $request->request->has('generateApiKey')) {
|
||||
$regenerateApiKey = true;
|
||||
} else {
|
||||
$regenerateApiKey = false;
|
||||
}
|
||||
|
||||
if ($regenerateApiKey) {
|
||||
$apiKey = $app['salt_generator']->generate(32, true);
|
||||
|
||||
$this->getUser()
|
||||
->setApiKey($apiKey)
|
||||
->save();
|
||||
}
|
||||
|
||||
if ($request->isMethod('post')) {
|
||||
$deleteForm->handleRequest($request);
|
||||
$passwordForm->handleRequest($request);
|
||||
@ -104,6 +124,7 @@ class MyController extends Controller
|
||||
array(
|
||||
'gists' => $gists,
|
||||
'page' => $page,
|
||||
'apiKey' => $apiKey,
|
||||
'deleteForm' => $deleteForm->createView(),
|
||||
'filterForm' => $filterForm->createView(),
|
||||
'passwordForm' => $passwordForm->createView(),
|
||||
|
@ -16,7 +16,9 @@ class ApiCreateGistForm extends CreateGistForm
|
||||
{
|
||||
parent::build($options);
|
||||
|
||||
$this->builder->remove('cipher');
|
||||
$this->builder
|
||||
->remove('cipher')
|
||||
->remove('file');
|
||||
|
||||
return $this->builder;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class ApiUpdateGistForm extends ApiCreateGistForm
|
||||
|
||||
$this->builder
|
||||
->remove('title')
|
||||
->remove('file')
|
||||
->remove('type');
|
||||
|
||||
return $this->builder;
|
||||
|
@ -3,6 +3,7 @@
|
||||
namespace Gist\Model;
|
||||
|
||||
use Gist\Model\Base\Gist as BaseGist;
|
||||
use Propel\Runtime\Map\TableMap;
|
||||
|
||||
/**
|
||||
* Class Gist.
|
||||
@ -86,4 +87,25 @@ class Gist extends BaseGist
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function toArray($keyType = TableMap::TYPE_PHPNAME, $includeLazyLoadColumns = true, $alreadyDumpedObjects = array(), $includeForeignObjects = false)
|
||||
{
|
||||
$data = parent::toArray(
|
||||
$keyType,
|
||||
$includeLazyLoadColumns,
|
||||
$alreadyDumpedObjects,
|
||||
$includeForeignObjects
|
||||
);
|
||||
|
||||
foreach ($data as $key => $value) {
|
||||
$newKey = lcfirst($key);
|
||||
unset($data[$key]);
|
||||
$data[$newKey] = $value;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ class User extends BaseUser implements UserInterface
|
||||
*
|
||||
* @return Propel\Runtime\Util\PropelModelPager
|
||||
*/
|
||||
public function getGistsPager($page, $options = array(), $maxPerPage = 10)
|
||||
public function getGistsPager($page, $options = array(), $maxPerPage = 10)
|
||||
{
|
||||
$query = GistQuery::create()
|
||||
->filterByUser($this)
|
||||
@ -63,11 +63,11 @@ class User extends BaseUser implements UserInterface
|
||||
if (!empty($options['type']) && $options['type'] !== 'all') {
|
||||
$query->filterByType($options['type']);
|
||||
}
|
||||
|
||||
|
||||
if (!empty($options['title'])) {
|
||||
$query->filterByTitle('%'.$options['title'].'%', Criteria::LIKE);
|
||||
}
|
||||
|
||||
|
||||
if (!empty($options['cipher']) && $options['cipher'] !== 'anyway') {
|
||||
$bools = array(
|
||||
'yes' => true,
|
||||
|
@ -22,6 +22,7 @@
|
||||
<column name="password" type="VARCHAR" size="255" required="true" />
|
||||
<column name="roles" type="VARCHAR" size="255" required="true" />
|
||||
<column name="salt" type="VARCHAR" size="64" required="true" />
|
||||
<column name="api_key" type="VARCHAR" size="32" required="true" />
|
||||
|
||||
<behavior name="timestampable"/>
|
||||
</table>
|
||||
|
@ -205,30 +205,61 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'login.login.form.password.placeholder'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<form action="{{ path('my', params) }}" method="post">
|
||||
<p>
|
||||
{{ form_errors(passwordForm.currentPassword) }}
|
||||
{{ form_widget(passwordForm.currentPassword) }}
|
||||
</p>
|
||||
{% set apiEnabled = app.settings.api.enabled %}
|
||||
|
||||
<p>
|
||||
{{ form_errors(passwordForm.newPassword) }}
|
||||
{{ form_widget(passwordForm.newPassword) }}
|
||||
</p>
|
||||
<div class="row">
|
||||
<div class="col-md-{{ apiEnabled ? 6 : 12 }}">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'login.login.form.password.placeholder'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<form action="{{ path('my', params) }}" method="post">
|
||||
<p>
|
||||
{{ form_errors(passwordForm.currentPassword) }}
|
||||
{{ form_widget(passwordForm.currentPassword) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_rest(passwordForm) }}
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
</form>
|
||||
<p>
|
||||
{{ form_errors(passwordForm.newPassword) }}
|
||||
{{ form_widget(passwordForm.newPassword) }}
|
||||
</p>
|
||||
|
||||
<p>
|
||||
{{ form_rest(passwordForm) }}
|
||||
<input type="submit" class="btn btn-primary" value="{{ 'form.submit'|trans }}">
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if apiEnabled %}
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
{{ 'my.api.title'|trans }}
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="tab-content">
|
||||
<p>{{ 'my.api.warning'|trans|raw }}</p>
|
||||
|
||||
<form action="{{ path('my', params) }}" method="post">
|
||||
<div class="row">
|
||||
<p class="col-md-12">
|
||||
<input type="text" name="apiKey" id="form-api-key" class="form-control" value="{{ apiKey }}" data-key="{{ apiKey }}">
|
||||
</p>
|
||||
<p class="col-md-12">
|
||||
<input type="submit" name="generateApiKey" value="{{ 'my.api.form.generate'|trans }}" class="btn btn-primary">
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,18 +18,30 @@ class SaltGenerator
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generate($length = 32)
|
||||
public function generate($length = 32, $isApiKey = false)
|
||||
{
|
||||
if (!is_numeric($length)) {
|
||||
throw new InvalidArgumentException('Paramter length must be a valid integer.');
|
||||
}
|
||||
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
return substr(base64_encode(openssl_random_pseudo_bytes($length)), 0, $length);
|
||||
$string = base64_encode(openssl_random_pseudo_bytes(256));
|
||||
}
|
||||
|
||||
if (function_exists('mcrypt_create_iv')) {
|
||||
return substr(base64_encode(mcrypt_create_iv($length, MCRYPT_DEV_URANDOM)), 0, $length);
|
||||
$string = base64_encode(mcrypt_create_iv(256, MCRYPT_DEV_URANDOM));
|
||||
}
|
||||
|
||||
if (!empty($string)) {
|
||||
if (true === $isApiKey) {
|
||||
$string = str_replace(
|
||||
array('+', '%', '/', '#', '&'),
|
||||
'',
|
||||
$string
|
||||
);
|
||||
}
|
||||
|
||||
return substr($string, 0, $length);
|
||||
}
|
||||
|
||||
throw new RuntimeException('You must enable openssl or mcrypt modules.');
|
||||
|
@ -126,6 +126,7 @@ class UserProvider implements UserProviderInterface
|
||||
$user
|
||||
->setRoles('ROLE_USER')
|
||||
->setPassword($this->encoder->encodePassword($password, $user->getSalt()))
|
||||
->setApiKey($this->saltGenerator->generate(32, true))
|
||||
->save();
|
||||
|
||||
return $user;
|
||||
@ -166,6 +167,20 @@ class UserProvider implements UserProviderInterface
|
||||
return $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a user by his api key.
|
||||
*
|
||||
* @param string $apiKey
|
||||
*
|
||||
* @return User
|
||||
*/
|
||||
public function loadUserByApiKey($apiKey)
|
||||
{
|
||||
$user = UserQuery::create()->findOneByApiKey($apiKey);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if the given password is the current user password.
|
||||
*
|
||||
|
@ -98,6 +98,10 @@ var myEvents = function() {
|
||||
$('#form-deletion form').submit();
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('change keyup keydown', '#form-api-key', function() {
|
||||
$(this).val($(this).data('key'));
|
||||
});
|
||||
}
|
||||
|
||||
var mainEditorEvents = function() {
|
||||
|
Loading…
Reference in New Issue
Block a user