1
0
Fork 0
forked from deblan/gist

Merge stash

This commit is contained in:
Simon Vieille 2017-06-25 19:13:27 +02:00
parent a9435906fe
commit 80b3c0bbdb
14 changed files with 216 additions and 41 deletions

View file

@ -6,6 +6,7 @@ security:
login_required_to_view_gist: false login_required_to_view_gist: false
login_required_to_view_embeded_gist: false login_required_to_view_embeded_gist: false
api: api:
enabled: true
base_url: 'https://gist.deblan.org/' base_url: 'https://gist.deblan.org/'
data: data:
path: data/git path: data/git

View file

@ -57,10 +57,14 @@ revisions:
path: /revs/{gist} path: /revs/{gist}
defaults: {_controller: Gist\Controller\ViewController::revisionsAction, _locale: en} defaults: {_controller: Gist\Controller\ViewController::revisionsAction, _locale: en}
api_list:
path: /api/list/{apiKey}
defaults: {_controller: Gist\Controller\ApiController::listAction, _locale: en}
api_create: api_create:
path: /api/create path: /api/create/{apiKey}
defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en} defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en}
api_update: api_update:
path: /api/update/{gist} path: /api/update/{gist}/{apiKey}
defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en} defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en}

View file

@ -12,25 +12,26 @@ use GuzzleHttp\Client as BaseClient;
class Client extends BaseClient class Client extends BaseClient
{ {
/** /**
* URI of creation * URI of creation.
* *
* @const string * @const string
*/ */
const CREATE = '/en/api/create'; const CREATE = '/en/api/create';
/** /**
* URI of update * URI of update.
* *
* @const string * @const string
*/ */
const UPDATE = '/en/api/update/{gist}'; const UPDATE = '/en/api/update/{gist}';
/** /**
* Creates a gist * Creates a gist.
* *
* @param string $title The title * @param string $title The title
* @param string $type The type * @param string $type The type
* @param string $content The content * @param string $content The content
*
* @return array * @return array
*/ */
public function create($title, $type, $content) public function create($title, $type, $content)

View file

@ -8,6 +8,8 @@ use Symfony\Component\HttpFoundation\JsonResponse;
use Gist\Form\ApiCreateGistForm; use Gist\Form\ApiCreateGistForm;
use Gist\Model\GistQuery; use Gist\Model\GistQuery;
use Gist\Form\ApiUpdateGistForm; use Gist\Form\ApiUpdateGistForm;
use GitWrapper\GitException;
use Gist\Model\UserQuery;
/** /**
* Class ApiController. * Class ApiController.
@ -17,16 +19,73 @@ use Gist\Form\ApiUpdateGistForm;
class ApiController extends Controller class ApiController extends Controller
{ {
/** /**
* Creates a gist. * Lists gists.
* *
* @param Request $request * @param Request $request
* @param string $apiKey
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function createAction(Request $request) public function listAction(Request $request, $apiKey)
{ {
$app = $this->getApp(); $app = $this->getApp();
if (false === $app['settings']['api']['enabled']) {
return new Response('', 403);
}
if (false === $this->isValidApiKey($apiKey)) {
return $this->invalidApiKeyResponse();
}
if (false === $request->isMethod('get')) {
return $this->invalidMethodResponse('GET method is required.');
}
$gists = GistQuery::create()->find();
$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)) {
return $this->invalidApiKeyResponse();
}
if (false === $request->isMethod('post')) { if (false === $request->isMethod('post')) {
return $this->invalidMethodResponse('POST method is required.'); return $this->invalidMethodResponse('POST method is required.');
} }
@ -48,16 +107,16 @@ class ApiController extends Controller
$history = $app['gist']->getHistory($gist); $history = $app['gist']->getHistory($gist);
return new JsonResponse(array( $data = $gist->toArray();
'url' => $request->getSchemeAndHttpHost().$app['url_generator']->generate( $data['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate(
'view', 'view',
array( array(
'gist' => $gist->getFile(), 'gist' => $gist->getFile(),
'commit' => array_pop($history)['commit'], 'commit' => array_pop($history)['commit'],
) )
), );
'gist' => $gist->toArray(),
)); return new JsonResponse($data);
} }
return $this->invalidRequestResponse('Invalid field(s)'); return $this->invalidRequestResponse('Invalid field(s)');
@ -67,14 +126,23 @@ class ApiController extends Controller
* Updates a gist. * Updates a gist.
* *
* @param Request $request * @param Request $request
* @param string $gist * @param string $gist
* @param string $apiKey
* *
* @return JsonResponse * @return JsonResponse
*/ */
public function updateAction(Request $request, $gist) public function updateAction(Request $request, $gist, $apiKey)
{ {
$app = $this->getApp(); $app = $this->getApp();
if (false === $app['settings']['api']['enabled']) {
return new Response('', 403);
}
if (false === $this->isValidApiKey($apiKey)) {
return $this->invalidApiKeyResponse();
}
if (false === $request->isMethod('post')) { if (false === $request->isMethod('post')) {
return $this->invalidMethodResponse('POST method is required.'); return $this->invalidMethodResponse('POST method is required.');
} }
@ -106,21 +174,38 @@ class ApiController extends Controller
$history = $app['gist']->getHistory($gist); $history = $app['gist']->getHistory($gist);
return new JsonResponse(array( $data = $gist->toArray();
'url' => $request->getSchemeAndHttpHost().$app['url_generator']->generate( $data['url'] = $request->getSchemeAndHttpHost().$app['url_generator']->generate(
'view', 'view',
array( array(
'gist' => $gist->getFile(), 'gist' => $gist->getFile(),
'commit' => array_pop($history)['commit'], 'commit' => array_pop($history)['commit'],
) )
), );
'gist' => $gist->toArray(),
)); return new JsonResponse($data);
} }
return $this->invalidRequestResponse('Invalid field(s)'); return $this->invalidRequestResponse('Invalid field(s)');
} }
/**
* 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. * Builds an invalid method response.
* *
@ -154,4 +239,11 @@ class ApiController extends Controller
return new JsonResponse($data, 400); return new JsonResponse($data, 400);
} }
protected function isValidApiKey($apiKey)
{
return !empty($apiKey) && UserQuery::create()
->filterByApiKey($apiKey)
->count() === 1;
}
} }

View file

@ -26,7 +26,7 @@ class LoginController extends Controller
{ {
$app = $this->getApp(); $app = $this->getApp();
if (false === $app['settings']['enable_registration']) { if (false === $app['settings']['security']['enable_registration']) {
return new Response('', 403); return new Response('', 403);
} }
@ -78,7 +78,7 @@ class LoginController extends Controller
{ {
$app = $this->getApp(); $app = $this->getApp();
if (false === $app['settings']['enable_login']) { if (false === $app['settings']['security']['enable_login']) {
return new Response('', 403); return new Response('', 403);
} }

View file

@ -58,6 +58,24 @@ class MyController extends Controller
$gists = $this->getUser()->getGistsPager($page, $options); $gists = $this->getUser()->getGistsPager($page, $options);
$apiKey = $this->getUser()->getApiKey();
if (empty($apiKey)) {
$regenerateApiKey = true;
} 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')) { if ($request->isMethod('post')) {
$deleteForm->handleRequest($request); $deleteForm->handleRequest($request);
$passwordForm->handleRequest($request); $passwordForm->handleRequest($request);
@ -104,6 +122,7 @@ class MyController extends Controller
array( array(
'gists' => $gists, 'gists' => $gists,
'page' => $page, 'page' => $page,
'apiKey' => $apiKey,
'deleteForm' => $deleteForm->createView(), 'deleteForm' => $deleteForm->createView(),
'filterForm' => $filterForm->createView(), 'filterForm' => $filterForm->createView(),
'passwordForm' => $passwordForm->createView(), 'passwordForm' => $passwordForm->createView(),

View file

@ -3,6 +3,7 @@
namespace Gist\Model; namespace Gist\Model;
use Gist\Model\Base\Gist as BaseGist; use Gist\Model\Base\Gist as BaseGist;
use Propel\Runtime\Map\TableMap;
/** /**
* Class Gist. * Class Gist.
@ -86,4 +87,25 @@ class Gist extends BaseGist
return $this; 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;
}
} }

View file

@ -54,7 +54,7 @@ class User extends BaseUser implements UserInterface
* *
* @return Propel\Runtime\Util\PropelModelPager * @return Propel\Runtime\Util\PropelModelPager
*/ */
public function getGistsPager($page, $options = array(), $maxPerPage = 10) public function getGistsPager($page, $options = array(), $maxPerPage = 10)
{ {
$query = GistQuery::create() $query = GistQuery::create()
->filterByUser($this) ->filterByUser($this)
@ -63,11 +63,11 @@ class User extends BaseUser implements UserInterface
if (!empty($options['type']) && $options['type'] !== 'all') { if (!empty($options['type']) && $options['type'] !== 'all') {
$query->filterByType($options['type']); $query->filterByType($options['type']);
} }
if (!empty($options['title'])) { if (!empty($options['title'])) {
$query->filterByTitle('%'.$options['title'].'%', Criteria::LIKE); $query->filterByTitle('%'.$options['title'].'%', Criteria::LIKE);
} }
if (!empty($options['cipher']) && $options['cipher'] !== 'anyway') { if (!empty($options['cipher']) && $options['cipher'] !== 'anyway') {
$bools = array( $bools = array(
'yes' => true, 'yes' => true,

View file

@ -22,6 +22,7 @@
<column name="password" type="VARCHAR" size="255" required="true" /> <column name="password" type="VARCHAR" size="255" required="true" />
<column name="roles" 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="salt" type="VARCHAR" size="64" required="true" />
<column name="api_key" type="VARCHAR" size="32" required="true" />
<behavior name="timestampable"/> <behavior name="timestampable"/>
</table> </table>

View file

@ -230,6 +230,24 @@
</div> </div>
</div> </div>
</div> </div>
{% if app.settings.api.enabled %}
<div class="panel panel-default">
<div class="panel-heading">
{{ 'api.title'|trans }}
</div>
<div class="panel-body">
<div class="tab-content">
<form action="{{ path('my', params) }}" method="post">
<p>
<input type="text" name="apiKey" id="form-api-key" size="32" value="{{ apiKey }}" data-key="{{ apiKey }}">
<input type="submit" name="generateApiKey" value="{{ 'api.form.generate' }}">
</p>
</form>
</div>
</div>
</div>
{% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View file

@ -1,6 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
{% set theme_settings = app.settings.theme %} {% set theme_settings = app.settings.theme %}
{% set security_dettings = app.settings.security %} {% set security_settings = app.settings.security %}
<html xmlns="http://www.w3.org/1999/xhtml"> <html xmlns="http://www.w3.org/1999/xhtml">
<head> <head>
{% block css %} {% block css %}
@ -56,14 +56,14 @@
{{ 'app.menu.my.logout.title'|trans }} {{ 'app.menu.my.logout.title'|trans }}
</a> </a>
</li> </li>
{% elseif security_dettings.enable_login %} {% elseif security_settings.enable_login %}
<li> <li>
<a href="{{ path('login') }}"> <a href="{{ path('login') }}">
{{ 'app.menu.my.login.title'|trans }} {{ 'app.menu.my.login.title'|trans }}
</a> </a>
</li> </li>
{% if security_dettings.enable_registration %} {% if security_settings.enable_registration %}
<li> <li>
<a href="{{ path('register') }}"> <a href="{{ path('register') }}">
{{ 'app.menu.my.register.title'|trans }} {{ 'app.menu.my.register.title'|trans }}

View file

@ -18,18 +18,30 @@ class SaltGenerator
* *
* @return string * @return string
*/ */
public function generate($length = 32) public function generate($length = 32, $isApiKey = false)
{ {
if (!is_numeric($length)) { if (!is_numeric($length)) {
throw new InvalidArgumentException('Paramter length must be a valid integer.'); throw new InvalidArgumentException('Paramter length must be a valid integer.');
} }
if (function_exists('openssl_random_pseudo_bytes')) { 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')) { 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.'); throw new RuntimeException('You must enable openssl or mcrypt modules.');

View file

@ -126,6 +126,7 @@ class UserProvider implements UserProviderInterface
$user $user
->setRoles('ROLE_USER') ->setRoles('ROLE_USER')
->setPassword($this->encoder->encodePassword($password, $user->getSalt())) ->setPassword($this->encoder->encodePassword($password, $user->getSalt()))
->setApiKey($this->saltGenerator->generate(32, true))
->save(); ->save();
return $user; return $user;

View file

@ -98,6 +98,10 @@ var myEvents = function() {
$('#form-deletion form').submit(); $('#form-deletion form').submit();
} }
}); });
$(document).on('change keyup keydown', '#form-api-key', function() {
$(this).val($(this).data('key'));
});
} }
var mainEditorEvents = function() { var mainEditorEvents = function() {