/** * Handle an incoming request. * * @param Request $request * @param Closure $next * * @return mixed */ public function handle($request, Closure $next) { $validationResult = Spec::define(['content-hash' => PrimitiveTypeConstraint::forType(ScalarTypes::SCALAR_STRING), 'authorization' => PrimitiveTypeConstraint::forType(ScalarTypes::SCALAR_STRING)], [], ['content-hash', 'authorization'])->check(array_map(function ($entry) { return $entry[0]; }, $request->headers->all())); if ($validationResult->failed()) { return ApiResponse::makeFromSpec($validationResult)->toResponse(); } $authorization = str_replace('Hash ', '', $request->headers->get('Authorization')); $content = $request->getContent(); try { $pair = $this->finder->byPublicId($authorization, KeyPairTypes::TYPE_HMAC); $hasher = new HmacHasher(); $verificationResult = $hasher->verify($request->headers->get('Content-Hash'), $content . Carbon::now()->format($this->format), $pair->getSecretKey()); if ($verificationResult) { $request->attributes->set(static::ATTRIBUTE_KEYPAIR, $pair); return $next($request); } return ApiResponse::create([], ApiResponse::STATUS_INVALID, ['HMAC content hash does not match the expected hash.'])->toResponse(); } catch (ModelNotFoundException $ex) { if ($ex->getModel() === KeyPair::class) { return ApiResponse::create([], ApiResponse::STATUS_INVALID, ['Unable to locate public ID. Check your credentials'])->toResponse(); } throw $ex; } }
public function testCheckNested() { $instance = Spec::define(['name' => Boa::string(), 'count' => Boa::integer(), 'address' => Spec::define(['street' => Spec::define(['first_line' => Boa::string(), 'second_line' => Boa::either(Boa::string(), Boa::integer())], [], ['first_line']), 'state' => [Boa::string(), new StringLengthConstraint(2, 2)], 'zip' => Boa::integer()], [], ['street', 'zip'])]); $resultOne = $instance->check(['name' => 'Doge', 'count' => 7, 'address' => []]); $this->assertTrue($resultOne->failed()); $resultTwo = $instance->check(['name' => 'Doge', 'count' => 7, 'address' => ['street' => [], 'state' => 90]]); $failed = $resultTwo->getFailed(); $missing = $resultTwo->getMissing(); $this->assertTrue($resultTwo->failed()); $this->assertArrayHasKey('address.state', $failed); $this->assertTrue(in_array('address.street.first_line', $missing)); $resultThree = $instance->check(['name' => 'Doge', 'count' => 7, 'address' => ['street' => ['first_line' => '1337 Hacker Way'], 'state' => 'GA', 'zip' => 13370]]); $this->assertTrue($resultThree->passed()); }
public function testComplexSpec() { $graph = new SpecGraph(); $graph->add('input', [], Spec::define(['sleepy' => Boa::boolean(), 'tennis_balls' => Boa::integer(), 'message' => Boa::either(Boa::string(), Boa::integer())], [], ['message'])); $graph->add('allowedMessage', ['input'], Spec::define(['message' => [Boa::in(['hi', 'how are you?', 'you dumb']), Boa::in(['hi', 'how are you?', 'you are smart'])]], [], ['message'])); $graph->add('validBallCount', ['input'], Spec::define(['tennis_balls' => Boa::between(1, 10)])); $graph->add('additionalBallProps', ['validBallCount'], Spec::define(['ball_color' => [Boa::string(), Boa::in(['blue', 'red', 'yellow'])]], [], ['ball_color'])); $result = $graph->check(['sleepy' => true, 'tennis_balls' => 3, 'message' => 'hi', 'ball_color' => 'blue']); $this->assertTrue($result->passed()); $result2 = $graph->check(['sleepy' => 1, 'tennis_balls' => 3]); $this->assertEqualsMatrix([[true, $result2->failed()], [1, count($result2->getFailed())], [['message'], $result2->getMissing()]]); $result3 = $graph->check(['sleepy' => true, 'tennis_balls' => -30, 'message' => 'hello']); $this->assertEqualsMatrix([[true, $result3->failed()], [2, count($result3->getFailed())], [[], $result3->getMissing()]]); $result4 = $graph->check(['sleepy' => true, 'tennis_balls' => 3, 'message' => 'how are you?']); $this->assertEqualsMatrix([[true, $result4->failed()], [0, count($result4->getFailed())], [['ball_color'], $result4->getMissing()]]); $result5 = $graph->check(['sleepy' => true, 'tennis_balls' => 3, 'message' => 'how are you?', 'ball_color' => 'liquid_gold']); $this->assertEqualsMatrix([[true, $result5->failed()], [1, count($result5->getFailed())], [[], $result5->getMissing()]]); }
/** * The inverse operation of calling toResponse. * * This function will attempt to parse a Response object into an * ApiResponse object, which can be useful for introspection and testing. * * @param Response $response * * @throws CoreException * @return static */ public static function fromResponse(Response $response) { $body = Json::decode($response->getContent()); $result = Spec::define(['messages' => Boa::arrOf(Boa::string()), 'status' => Boa::in(static::getValidStatuses()), 'code' => Boa::integer()])->check($body); if ($result->failed()) { throw new CoreException('Unable to parse an ApiResponse out of the content of a' . ' Response object. Make sure that the Response object was' . ' actually generated by an ApiResponse or a compatible' . ' implementation.'); } return new static(Arr::except($body, static::getReservedKeys()), $body['status'], $body['messages']); }
public function testFireWithValidSpec() { $laravelJob = m::mock(LaravelJob::class); $laravelJob->shouldReceive('delete')->atLeast()->once(); $job = new Job(); $job->state = JobState::QUEUED; $job->data = json_encode(['omg' => false, 'why_not' => 'because']); $handler = m::mock(BaseTask::class); $handler->shouldReceive('fire')->with($job, m::type(JobSchedulerInterface::class))->atLeast()->once(); $handler->shouldReceive('getSpec')->andReturn(Spec::define(['omg' => Boa::boolean()], ['yes' => 'please'], ['why_not'])); $impersonator = new Impersonator(); $impersonator->mock(JobRepositoryInterface::class, function (MockInterface $mock) use($job) { $mock->shouldReceive('find')->with(1337)->atLeast()->once()->andReturn($job); $mock->shouldReceive('started')->with($job, m::type('string'))->atLeast()->once(); $mock->shouldReceive('complete')->with($job)->atLeast()->once(); }); $impersonator->mock(HandlerResolverInterface::class, function (MockInterface $mock) use($job, $handler) { $mock->shouldReceive('resolve')->with($job)->atLeast()->once()->andReturn($handler); }); /** @var RunTaskCommand $command */ $command = $impersonator->make(RunTaskCommand::class); $command->fire($laravelJob, ['job_id' => 1337]); }
/** * @inheritDoc */ public function getSpec() { return Spec::define(['days' => Boa::integer(), 'repeat_in' => Boa::integer(), 'expire_after' => Boa::integer()], ['days' => 30, 'repeat_in' => -1, 'expire_after' => 1440]); }
/** * Shortcut for defining a validator using a Spec. * * @param array $constraints * @param array $defaults * @param array $required * @param array $messages * * @return static */ public static function spec($constraints, $defaults = [], $required = [], $messages = []) { return new static(Spec::define($constraints, $defaults, $required), $messages); }