Compare commits

..

14 Commits

Author SHA1 Message Date
Alejandro Celaya
611a314cdf Merge branch 'develop' 2016-08-29 13:07:25 +02:00
Alejandro Celaya
e7b4d24e5d Merge pull request #68 from acelaya/develop
Develop
2016-08-29 13:07:01 +02:00
Alejandro Celaya
cf60440288 Fixed possible PHP errors being missed while checking REST auth 2016-08-29 12:43:02 +02:00
Alejandro Celaya
15896045f3 Removed logic making visits to be returned for 2 days only if no start or end date were provided 2016-08-28 19:32:07 +02:00
Alejandro Celaya
a9f480ca99 Fixed error while checking an API key that doesn't exist 2016-08-28 09:46:11 +02:00
Alejandro Celaya
4bd67d5f98 Fixed cross domain middleware not exposing the Authorization header 2016-08-27 13:00:41 +02:00
Alejandro Celaya
924ba58f73 Added swagger documentation file 2016-08-26 11:51:51 +02:00
Alejandro Celaya
6eef694315 Merge branch 'develop' 2016-08-21 21:24:31 +02:00
Alejandro Celaya
fe4a4aef34 Updated changelog 2016-08-21 21:24:00 +02:00
Alejandro Celaya
b13c95cf1a Placed cross domain middleware as the first one for rest requests 2016-08-21 21:21:31 +02:00
Alejandro Celaya
c1c325588e Merge branch 'develop' 2016-08-21 19:16:36 +02:00
Alejandro Celaya
faad60f79e Merge branch 'develop' of https://github.com/shlinkio/shlink into develop 2016-08-21 19:16:21 +02:00
Alejandro Celaya
cbb5a02b95 Kept symlincs while generating the dist file 2016-08-21 19:16:06 +02:00
Alejandro Celaya
7aa42ada54 Merge pull request #61 from acelaya/develop
Develop
2016-08-21 18:31:32 +02:00
9 changed files with 314 additions and 10 deletions

View File

@@ -1,5 +1,11 @@
## CHANGELOG
### 1.2.1
**Bugs**
* [62: Fix cross-domain requests in REST API](https://github.com/acelaya/url-shortener/issues/62)
### 1.2.0
**Features**

View File

@@ -40,5 +40,5 @@ rm -rf config/autoload/{{,*.}local.php{,.dist},.gitignore}
# Compressing file
rm -f "${projectdir}"/build/shlink_${version}_dist.zip
zip -r "${projectdir}"/build/shlink_${version}_dist.zip .
zip -ry "${projectdir}"/build/shlink_${version}_dist.zip .
rm -rf "${builtcontent}"

View File

@@ -73,5 +73,8 @@
"serve": "php -S 0.0.0.0:8000 -t public/",
"test": "phpunit --coverage-clover build/clover.xml",
"pretty-test": "phpunit --coverage-html build/coverage"
},
"config": {
"process-timeout": 0
}
}

289
docs/swagger.yaml Normal file
View File

@@ -0,0 +1,289 @@
swagger: '2.0'
info:
title: Shlink
description: Shlink, the self-hosted URL shortener
version: "1.2.0"
schemes:
- https
basePath: /rest
produces:
- application/json
paths:
/authenticate:
post:
description: Performs an authentication
parameters:
- name: apiKey
in: formData
description: The API key to authenticate with
required: true
type: string
responses:
200:
description: The authentication worked.
schema:
type: object
properties:
token:
type: string
description: The authentication token that needs to be sent in the Authorization header
400:
description: An API key was not provided.
schema:
$ref: '#/definitions/Error'
401:
description: The API key is incorrect, is disabled or has expired.
schema:
$ref: '#/definitions/Error'
500:
description: Unexpected error.
schema:
$ref: '#/definitions/Error'
/short-codes:
get:
description: Returns the list of short codes
parameters:
- name: page
in: query
description: The page to be displayed. Defaults to 1
required: false
type: integer
- name: Authorization
in: header
description: The authorization token with Bearer type
required: true
type: string
responses:
200:
description: The list of short URLs
schema:
type: object
properties:
shortUrls:
type: object
properties:
data:
type: array
items:
$ref: '#/definitions/ShortUrl'
pagination:
$ref: '#/definitions/Pagination'
500:
description: Unexpected error.
schema:
$ref: '#/definitions/Error'
post:
description: Creates a new short code
parameters:
- name: longUrl
in: formData
description: The URL to parse
required: true
type: string
- name: tags
in: formData
description: The URL to parse
required: false
type: array
items:
type: string
- name: Authorization
in: header
description: The authorization token with Bearer type
required: true
type: string
responses:
200:
description: The result of parsing the long URL
schema:
type: object
properties:
longUrl:
type: string
description: The original long URL that has been parsed
shortUrl:
type: string
description: The generated short URL
shortCode:
type: string
description: the short code that is being used in the short URL
400:
description: The long URL was not provided or is invalid.
schema:
$ref: '#/definitions/Error'
500:
description: Unexpected error.
schema:
$ref: '#/definitions/Error'
/short-codes/{shortCode}:
get:
description: Get the long URL behind a short code.
parameters:
- name: shortCode
in: path
type: string
description: The short code to resolve.
required: true
- name: Authorization
in: header
description: The authorization token with Bearer type
required: true
type: string
responses:
200:
description: The long URL behind a short code.
schema:
type: object
properties:
longUrl:
type: string
description: The original long URL behind the short code.
404:
description: No URL was found for provided short code.
schema:
$ref: '#/definitions/Error'
400:
description: Provided shortCode does not match the character set currently used by the app to generate short codes.
schema:
$ref: '#/definitions/Error'
500:
description: Unexpected error.
schema:
$ref: '#/definitions/Error'
/short-codes/{shortCode}/visits:
get:
description: Get the list of visits on provided short code.
parameters:
- name: shortCode
in: path
type: string
description: The shortCode from which we want to get the visits.
required: true
- name: Authorization
in: header
description: The authorization token with Bearer type
required: true
type: string
responses:
200:
description: List of visits.
schema:
type: object
properties:
visits:
type: object
properties:
data:
type: array
items:
$ref: '#/definitions/Visit'
404:
description: The short code does not belong to any short URL.
schema:
$ref: '#/definitions/Error'
500:
description: Unexpected error.
schema:
$ref: '#/definitions/Error'
/short-codes/{shortCode}/tags:
put:
description: Edit the tags on provided short code.
parameters:
- name: shortCode
in: path
type: string
description: The shortCode in which we want to edit tags.
required: true
- name: tags
in: formData
type: array
items:
type: string
description: The list of tags to set to the short URL.
required: true
- name: Authorization
in: header
description: The authorization token with Bearer type
required: true
type: string
responses:
200:
description: List of tags.
schema:
type: object
properties:
tags:
type: array
items:
type: string
400:
description: The request body does not contain a "tags" param with array type.
schema:
$ref: '#/definitions/Error'
404:
description: No short URL was found for provided short code.
schema:
$ref: '#/definitions/Error'
500:
description: Unexpected error.
schema:
$ref: '#/definitions/Error'
definitions:
ShortUrl:
type: object
properties:
shortCode:
type: string
description: The short code for this short URL.
originalUrl:
type: string
description: The original long URL.
dateCreated:
type: string
format: date-time
description: The date in which the short URL was created in ISO format.
visitsCount:
type: integer
description: The number of visits that this short URL has recieved.
tags:
type: array
items:
type: string
description: A list of tags applied to this short URL
Visit:
type: object
properties:
referer:
type: string
date:
type: string
format: date-time
remoteAddr:
type: string
userAgent:
type: string
Error:
type: object
properties:
code:
type: string
description: A machine unique code
message:
type: string
description: A human-friendly error message
Pagination:
type: object
properties:
currentPage:
type: integer
description: The number of current page being displayed.
pagesCount:
type: integer
description: The total number of pages that can be displayed.

View File

@@ -29,12 +29,6 @@ class VisitRepository extends EntityRepository implements VisitRepositoryInterfa
$shortUrl = $shortUrl instanceof ShortUrl
? $shortUrl
: $this->getEntityManager()->find(ShortUrl::class, $shortUrl);
if (! isset($dateRange) || $dateRange->isEmpty()) {
$startDate = $shortUrl->getDateCreated();
$endDate = clone $startDate;
$endDate->add(new \DateInterval('P2D'));
$dateRange = new DateRange($startDate, $endDate);
}
$qb = $this->createQueryBuilder('v');
$qb->where($qb->expr()->eq('v.shortUrl', ':shortUrl'))

View File

@@ -7,9 +7,9 @@ return [
'rest' => [
'path' => '/rest',
'middleware' => [
Middleware\CrossDomainMiddleware::class,
Middleware\BodyParserMiddleware::class,
Middleware\CheckAuthenticationMiddleware::class,
Middleware\CrossDomainMiddleware::class,
],
'priority' => 5,
],

View File

@@ -66,7 +66,7 @@ class AuthenticateAction extends AbstractRestAction
// Authenticate using provided API key
$apiKey = $this->apiKeyService->getByKey($authData['apiKey']);
if (! $apiKey->isValid()) {
if (! isset($apiKey) || ! $apiKey->isValid()) {
return new JsonResponse([
'error' => RestUtils::INVALID_API_KEY_ERROR,
'message' => $this->translator->translate('Provided API key does not exist or is invalid.'),

View File

@@ -13,6 +13,7 @@ use Shlinkio\Shlink\Rest\Util\RestUtils;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Expressive\Router\RouteResult;
use Zend\I18n\Translator\TranslatorInterface;
use Zend\Stdlib\ErrorHandler;
use Zend\Stratigility\MiddlewareInterface;
class CheckAuthenticationMiddleware implements MiddlewareInterface
@@ -117,9 +118,11 @@ class CheckAuthenticationMiddleware implements MiddlewareInterface
}
try {
ErrorHandler::start();
if (! $this->jwtService->verify($jwt)) {
return $this->createTokenErrorResponse();
}
ErrorHandler::stop(true);
// Update the token expiration and continue to next middleware
$jwt = $this->jwtService->refresh($jwt);
@@ -131,6 +134,14 @@ class CheckAuthenticationMiddleware implements MiddlewareInterface
} catch (AuthenticationException $e) {
$this->logger->warning('Tried to access API with an invalid JWT.' . PHP_EOL . $e);
return $this->createTokenErrorResponse();
} catch (\Exception $e) {
$this->logger->warning('Unexpected error occurred.' . PHP_EOL . $e);
return $this->createTokenErrorResponse();
} catch (\Throwable $e) {
$this->logger->warning('Unexpected error occurred.' . PHP_EOL . $e);
return $this->createTokenErrorResponse();
} finally {
ErrorHandler::clean();
}
}

View File

@@ -41,7 +41,8 @@ class CrossDomainMiddleware implements MiddlewareInterface
}
// Add Allow-Origin header
$response = $response->withHeader('Access-Control-Allow-Origin', $request->getHeader('Origin'));
$response = $response->withHeader('Access-Control-Allow-Origin', $request->getHeader('Origin'))
->withHeader('Access-Control-Expose-Headers', 'Authorization');
if ($request->getMethod() !== 'OPTIONS') {
return $response;
}