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_embeded_gist: false
api:
enabled: true
base_url: 'https://gist.deblan.org/'
data:
path: data/git

View file

@ -57,10 +57,14 @@ 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}
api_create:
path: /api/create
path: /api/create/{apiKey}
defaults: {_controller: Gist\Controller\ApiController::createAction, _locale: en}
api_update:
path: /api/update/{gist}
path: /api/update/{gist}/{apiKey}
defaults: {_controller: Gist\Controller\ApiController::updateAction, _locale: en}

View file

@ -12,25 +12,26 @@ use GuzzleHttp\Client as BaseClient;
class Client extends BaseClient
{
/**
* URI of creation
*
* URI of creation.
*
* @const string
*/
const CREATE = '/en/api/create';
/**
* URI of update
* URI of update.
*
* @const string
*/
const UPDATE = '/en/api/update/{gist}';
/**
* Creates a gist
* 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)

View file

@ -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.
@ -17,16 +19,73 @@ use Gist\Form\ApiUpdateGistForm;
class ApiController extends Controller
{
/**
* Creates a gist.
* Lists gists.
*
* @param Request $request
* @param string $apiKey
*
* @return JsonResponse
*/
public function createAction(Request $request)
public function listAction(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('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')) {
return $this->invalidMethodResponse('POST method is required.');
}
@ -48,16 +107,16 @@ 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)');
@ -67,14 +126,23 @@ class ApiController extends Controller
* Updates a gist.
*
* @param Request $request
* @param string $gist
* @param string $gist
* @param string $apiKey
*
* @return JsonResponse
*/
public function updateAction(Request $request, $gist)
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)) {
return $this->invalidApiKeyResponse();
}
if (false === $request->isMethod('post')) {
return $this->invalidMethodResponse('POST method is required.');
}
@ -106,21 +174,38 @@ 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)');
}
/**
* 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.
*
@ -154,4 +239,11 @@ class ApiController extends Controller
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();
if (false === $app['settings']['enable_registration']) {
if (false === $app['settings']['security']['enable_registration']) {
return new Response('', 403);
}
@ -78,7 +78,7 @@ class LoginController extends Controller
{
$app = $this->getApp();
if (false === $app['settings']['enable_login']) {
if (false === $app['settings']['security']['enable_login']) {
return new Response('', 403);
}

View file

@ -58,6 +58,24 @@ class MyController extends Controller
$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')) {
$deleteForm->handleRequest($request);
$passwordForm->handleRequest($request);
@ -104,6 +122,7 @@ class MyController extends Controller
array(
'gists' => $gists,
'page' => $page,
'apiKey' => $apiKey,
'deleteForm' => $deleteForm->createView(),
'filterForm' => $filterForm->createView(),
'passwordForm' => $passwordForm->createView(),

View file

@ -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;
}
}

View file

@ -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,

View file

@ -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>

View file

@ -230,6 +230,24 @@
</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>
{% endblock %}

View file

@ -1,6 +1,6 @@
<!DOCTYPE html>
{% 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">
<head>
{% block css %}
@ -56,14 +56,14 @@
{{ 'app.menu.my.logout.title'|trans }}
</a>
</li>
{% elseif security_dettings.enable_login %}
{% elseif security_settings.enable_login %}
<li>
<a href="{{ path('login') }}">
{{ 'app.menu.my.login.title'|trans }}
</a>
</li>
{% if security_dettings.enable_registration %}
{% if security_settings.enable_registration %}
<li>
<a href="{{ path('register') }}">
{{ 'app.menu.my.register.title'|trans }}

View file

@ -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.');

View file

@ -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;

View file

@ -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() {