Allowed port number on domain field when creating shotr URLs

This commit is contained in:
Alejandro Celaya 2019-10-20 10:30:11 +02:00
parent 232bf5a68b
commit b5e4da847a
3 changed files with 44 additions and 13 deletions

View file

@ -50,9 +50,7 @@ class ShortUrlMetaInputFilter extends InputFilter
$this->add($this->createBooleanInput(self::FIND_IF_EXISTS, false)); $this->add($this->createBooleanInput(self::FIND_IF_EXISTS, false));
$domain = $this->createInput(self::DOMAIN, false); $domain = $this->createInput(self::DOMAIN, false);
$domain->getValidatorChain()->attach(new Validator\Hostname([ $domain->getValidatorChain()->attach(new Validation\HostAndPortValidator());
'allow' => Validator\Hostname::ALLOW_DNS | Validator\Hostname::ALLOW_LOCAL,
]));
$this->add($domain); $this->add($domain);
} }
} }

View file

@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl;
use Cake\Chronos\Chronos; use Cake\Chronos\Chronos;
use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Message\ServerRequestInterface as Request;
use Shlinkio\Shlink\Core\Exception\InvalidArgumentException; use Shlinkio\Shlink\Core\Exception\InvalidArgumentException;
use Shlinkio\Shlink\Core\Exception\ValidationException;
use Shlinkio\Shlink\Core\Model\CreateShortUrlData; use Shlinkio\Shlink\Core\Model\CreateShortUrlData;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Zend\Diactoros\Uri; use Zend\Diactoros\Uri;
@ -29,18 +30,20 @@ class CreateShortUrlAction extends AbstractCreateShortUrlAction
throw new InvalidArgumentException('A URL was not provided'); throw new InvalidArgumentException('A URL was not provided');
} }
return new CreateShortUrlData( try {
new Uri($postData['longUrl']), $meta = ShortUrlMeta::createFromParams(
(array) ($postData['tags'] ?? []),
ShortUrlMeta::createFromParams(
$this->getOptionalDate($postData, 'validSince'), $this->getOptionalDate($postData, 'validSince'),
$this->getOptionalDate($postData, 'validUntil'), $this->getOptionalDate($postData, 'validUntil'),
$postData['customSlug'] ?? null, $postData['customSlug'] ?? null,
$postData['maxVisits'] ?? null, $postData['maxVisits'] ?? null,
$postData['findIfExists'] ?? null, $postData['findIfExists'] ?? null,
$postData['domain'] ?? null $postData['domain'] ?? null
) );
);
return new CreateShortUrlData(new Uri($postData['longUrl']), (array) ($postData['tags'] ?? []), $meta);
} catch (ValidationException $e) {
throw new InvalidArgumentException('Provided meta data is not valid', -1, $e);
}
} }
private function getOptionalDate(array $postData, string $fieldName): ?Chronos private function getOptionalDate(array $postData, string $fieldName): ?Chronos

View file

@ -15,6 +15,7 @@ use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
use Shlinkio\Shlink\Core\Service\UrlShortener; use Shlinkio\Shlink\Core\Service\UrlShortener;
use Shlinkio\Shlink\Rest\Action\ShortUrl\CreateShortUrlAction; use Shlinkio\Shlink\Rest\Action\ShortUrl\CreateShortUrlAction;
use Shlinkio\Shlink\Rest\Util\RestUtils; use Shlinkio\Shlink\Rest\Util\RestUtils;
use Zend\Diactoros\Response\JsonResponse;
use Zend\Diactoros\ServerRequest; use Zend\Diactoros\ServerRequest;
use Zend\Diactoros\Uri; use Zend\Diactoros\Uri;
@ -50,8 +51,8 @@ class CreateShortUrlActionTest extends TestCase
{ {
$shortUrl = new ShortUrl(''); $shortUrl = new ShortUrl('');
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera()) $this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())
->willReturn($shortUrl) ->willReturn($shortUrl)
->shouldBeCalledOnce(); ->shouldBeCalledOnce();
$request = (new ServerRequest())->withParsedBody([ $request = (new ServerRequest())->withParsedBody([
'longUrl' => 'http://www.domain.com/foo/bar', 'longUrl' => 'http://www.domain.com/foo/bar',
@ -65,8 +66,8 @@ class CreateShortUrlActionTest extends TestCase
public function anInvalidUrlReturnsError(): void public function anInvalidUrlReturnsError(): void
{ {
$this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera()) $this->urlShortener->urlToShortCode(Argument::type(Uri::class), Argument::type('array'), Argument::cetera())
->willThrow(InvalidUrlException::class) ->willThrow(InvalidUrlException::class)
->shouldBeCalledOnce(); ->shouldBeCalledOnce();
$request = (new ServerRequest())->withParsedBody([ $request = (new ServerRequest())->withParsedBody([
'longUrl' => 'http://www.domain.com/foo/bar', 'longUrl' => 'http://www.domain.com/foo/bar',
@ -76,6 +77,35 @@ class CreateShortUrlActionTest extends TestCase
$this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_URL_ERROR) > 0); $this->assertTrue(strpos($response->getBody()->getContents(), RestUtils::INVALID_URL_ERROR) > 0);
} }
/**
* @test
* @dataProvider provideInvalidDomains
*/
public function anInvalidDomainReturnsError(string $domain): void
{
$shortUrl = new ShortUrl('');
$urlToShortCode = $this->urlShortener->urlToShortCode(Argument::cetera())->willReturn($shortUrl);
$request = (new ServerRequest())->withParsedBody([
'longUrl' => 'http://www.domain.com/foo/bar',
'domain' => $domain,
]);
/** @var JsonResponse $response */
$response = $this->action->handle($request);
$payload = $response->getPayload();
$this->assertEquals(400, $response->getStatusCode());
$this->assertEquals(RestUtils::INVALID_ARGUMENT_ERROR, $payload['error']);
$urlToShortCode->shouldNotHaveBeenCalled();
}
public function provideInvalidDomains(): iterable
{
yield ['localhost:80000'];
yield ['127.0.0.1'];
yield ['???/&%$&'];
}
/** @test */ /** @test */
public function nonUniqueSlugReturnsError(): void public function nonUniqueSlugReturnsError(): void
{ {