I'm trying to add an Account to the Silex route. My goal is to have routes like:
/{_account}/{_locale}/
/{_account}/{_locale}/products
/{_account}/{_locale}/block
You can find my Code here on github. It's a small sample. I can read the account token from the request and save the Account in the AccountListener.
I try to handle the _account like _locale. Once set or updated the application don't have to bother about it. That means that the _account param will be set automatically if I call $app['url_generator']->generate('blog').
This is my current problem. I don't know how to inform the UrlGenerator to set these param.
Maybe my approach is completely wrong.
I Hope you can send me some examples or Cookbooks or somethink. Or a merge request.
The UrlGenerator uses parameters from the request_context (as you can see in the code) so you could set those in your listener.
src/app.php
$dispatcher = $app['dispatcher']->addSubscriber(
new AccountListener(
new AccountRepository(),
$app['request_context'],
$app['monolog']
)
);
SilexLab\Listener\AccountListener
public function __construct(
AccountProvider $accountProvider,
RequestContext $requestContext,
Logger $logger
) {
//...
$this->requestContext = $requestContext;
}
public function onKernelRequest(GetResponseEvent $event)
{
//...
$request->attributes->set('_account', $account);
$this->requestContext->setParameter('_account', $account);
}
Related
I have created a URL that take a request in method POST. He take a JSON and he return me a XML converted file.
I would like, before the request, a basic authorization ( just a password , i need not registered user )on this URL to allow the user make the request. But i don't understand how the authorization working on symfony.
I think that all i need is in this page of documentation
but i get some errors and i not understand how it work (what is realme? What does it mean with Secured Area?)
This is my controller, how can i implement a basic authentication on this REST call ?
class IndexController extends AbstractController
{
/**
* #Route("/converter-hl7", name="converter", methods={"POST"})
*/
public function index(Request $request) {
$json = $request->getContent();
$converter = new HL7ConverterService($json, new ErrorService());
$xml = $converter->outputXML();
$response = new Response($xml);
$response->headers->set('Content-Type', 'xml');
return $response;
}
}
i use JWT for my API's to Control and Secure Api access
you should check this Bundle
But to use this you need to create user .
if you don't want to create a user you could demand a password from the sender and integrate it in the request then control access based on that.
I'm working with Api-Platform 2.5 and Symfony 4.4 (lts).
I have discovered the API Platform and I understand that there are many "interceptors" that intercept the HTTP requests:
for GET requests: The Data Provider, The Controller, The Serialization Process and finally an "anonymous process" that's setting the response code.
for POST, PUT, PATCH, and DELETE: The serialization process, The Data Provider, The Controller, The Data Persister and finally the "anonymous process" that's setting the response code.
Here I have my Data Persister:
ProductDataPersister:
namespace App\DataPersister;
use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface;
use App\Entity\Product;
final class ProductDataPersister implements ContextAwareDataPersisterInterface
{
public function supports($data, array $context = []): bool
{
return $data instanceof Product;
}
public function persist($data, array $context = [])
{
//dd('persist');
return $data;
}
public function remove($data, array $context = [])
{
//dd('remove');
}
}
In this case neither persist nor remove are making the database stuff because it has to be done manually by injecting entity manager etc.
My question is, as we say the remove action with this code never deletes a Product and that's what I want But the response I get to the DELETE request still not overridden and still returns a 204 status code (no content).
I want to override the response type. What other listener gets executed after the Data Persister?
Is it overridable?
If you take a look at Api-Platform event system, it is clear that the event you are looking for, the one you refer as "anonymous process", is the appropriately named RespondListener.
You can see here the exact point where it is setting the response code.
The simplest way to override this would be to register your own event listener/subscriber to be executed after this listener:
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['fooMethod', EventPriorities::POST_RESPOND],
];
}
On fooMethod(ViewEvent $event) you can access the access response object ($event->getResponse() and modify it, or even create your own response object (although that's likely to be even less optimal).
Alternatively, you could decorate the #api_platform.listener.view.respond service, and implement your overriding logic in the decorator.
I'm writing unit tests for an API using PHPUnit and Laravel. Most functions I'm testing require that the user is authenticated before the function can be ran. The user data is stored in one table, and their permissions are stored inside of another table. I can fake the user object inside of Laravel, but I need to be able to also pull the corresponding permissions from the other table without having to hit the database like the dingo router currently is doing.
Currently running Laravel 5.8 and PHPUnit 8.1.5. I currently have the users object that I generated from a Laravel factory saved to a text file. I am able to pass that to a function called "actingAsApi" (found on Github, code below) and that allows me to authenticate as that user. However, the function is still going out and getting all permissions for that user from the database. I'm trying to mock or fake the permissions object it is pulling somewhere so that it doesn't need to hit the database at all. I also tried using the built in Passport functions for Passport::actingAs, and those did not work either as they were still hitting the DB (and not really working anyways).
actingAsApi (inside of TestCase.php)
protected function actingAsApi($user)
{
// mock service middleware
$auth = Mockery::mock('Dingo\Api\Http\Middleware\Auth[handle]',
[
Mockery::mock('Dingo\Api\Routing\Router'),
Mockery::mock('Dingo\Api\Auth\Auth'),
]);
$auth->shouldReceive('handle')
->andReturnUsing(function ($request, \Closure $next) {
return $next($request);
});
$this->app->instance('Dingo\Api\Http\Middleware\Auth', $auth);
$auth = Mockery::mock('Dingo\Api\Auth\Auth[user]',
[
app('Dingo\Api\Routing\Router'),
app('Illuminate\Container\Container'),
[],
]);
$auth->shouldReceive('user')
->andReturnUsing(function () use ($user) {
return $user;
});
$this->app->instance('Dingo\Api\Auth\Auth', $auth);
return $this;
}
Test inside of my Test file
public function testActAs() {
$user = 'tests/users/user1.txt';
$this->actingAsApi($user);
$request = new Request;
$t = new TestController($request);
$test = $t->index($request);
}
I expect the actingAsApi function to allow me to also pass in the mock permissions data that corresponds to my mock user object data from the file, but instead it is hitting the database to pull from the permissions table.
EDIT:
So i've been playing around with doing mock objects, and i figured out how to mock the original controller here:
$controlMock = Mockery::mock('App\Http\Controllers\Controller', [$request])->makePartial();
$controlMock->shouldReceive('userHasPermission')
->with('API_ACCESS')
->andReturn(true);
$this->app->instance('App\Http\Controllers\Controller', $controlMock);
but now I can't figure out how to get my call from the other controllers to hit the mocked controller and not a real one. Here is my code for hitting an example controller:
$info = $this->app->make('App\API\Controllers\InfoController');
print_r($info->getInfo('12345'));
How can i make the second block of code hit the mocked controller and not standup a real one like it does in its constructor method?
Finally came on an answer, and it is now fixed. Here's how I did it for those wondering:
$request = new Request;
$controlMock = m::mock('App\API\Controllers\InfoController', [$request])->makePartial();
$controlMock->shouldReceive('userHasPermission')
->with('API_ACCESS')
->andReturn(true);
print_r($controlMock->getInfo('12345'));
Basically, I was trying to Mock the original API controller, and then catch all of the calls thrown at it. Instead, I should've been mocking the controller I'm testing, in this case the InfoController. I can then catch the call 'userHasPermission', which should reach out to the Controller, but I am automatically returning true. This eliminates the need for hitting the database to receive permissions and other info. More information on how I solved it using Mockery can be found here: http://docs.mockery.io/en/latest/cookbook/big_parent_class.html. As you can see, this is referred to as a 'Big Parent Class'. Good luck!
I'm using laravel 5.5, and trying to send emails with an image that is the sign of the client. To make the image accessible from the views I'm copying it into public folder and queued emails will access to it.
With a single action I can send multiple emails to the client, with sign into email, and pdf like the email attached, with sign image too. Then, the same image can be called multiple times from different emails. Is for this that I copy one image with a codified name for each email and passing the name of the image to the Mailable.
The problem is to make a sign of client public with a limited time. Then I'm trying to make listener for Illuminate\Mail\Events\MessageSent event that deletes the image of public folder getting the image name from the event... but I can't access to it.
How can I access to data of mailable from the event?
Do you know a better way to do this?
Thanks in advance.
Mailable class
class SEPA extends Mailable
{
use Queueable, SerializesModels;
public $client;
/**
* Create a new message instance.
*
* #param Client $client
*/
public function __construct(Client $client)
{
$this->client = $client;
}
/**
* Build the message.
*
* #return $this
*/
public function build()
{
$date = Carbon::now();
// Name codified
$fileName = md5(microtime()).".png";
// Making the image accessible from views
Storage::copy("clients/{$this->client->id}/firma.png", "public/tmp/{$fileName}");
$pdfName = "SEPA - {$this->client->name}{$this->client->cognom1}{$this->client->cognom2}.pdf";
$dades = [
'data' => $date,
'client' => $this->client,
'firma' => $fileName
];
// Generating PDF
$pdf = PDF::loadView('pdfs.SEPA', $dades);
if (!Storage::has("tmp/clients/{$this->client->id}")) Storage::makeDirectory("tmp/clients/{$this->client->id}");
$pdf->save(storage_path()."/app/tmp/clients/{$this->client->id}/".$pdfName);
return $this
->from(['address' => 'email#random.com'])
->view('emails.SEPA')
->with($dades)
->attach(storage_path()."/app/tmp/clients/{$this->client->id}/".$pdfName);
}
}
EventServiceProvider.php
protected $listen = [
'Illuminate\Mail\Events\MessageSent' => [
'App\Listeners\DeleteTempResources'
]
];
Listener
public function handle(MessageSent $event)
{
// Trying to access on data message
Log::info($event->message->firma);
}
You might be able to just set the additional data you need to access from the event via the withSwiftMessage() method, as extra fields on the actual swiftMessage, since that's what will be accessible in the event, as $message.
I saw someone did this here, e.g. to add a $user object:
$this->withSwiftMessage(function ($message) {
$message->user = $this->user; // any crazy field of your choosing
});
This seemed pretty unorthodox to me - adding rogue fields like that.
Note you don't need to use the $user object to get it into the closure as it's available in scope via $this, so long as it's a member property of the containing class.
To see it in the event when the message comes off the queue, you can Log::info('The user: ', [$event->message->user]) in the MessageSending event.
I've just tested this, and it works (I'm on 5.5), but I'm not yet using this myself in code as it does seem a little strange, adding a rogue field like that. I mention it though as it might actually solve your problem if you're comfortable with the method! If anyone knows a less ugly way to do it, I'm all ears...
P.S. I may consider just tacking on $message->lara_user_id = $this->user->id in the closure, for my own case, as that seems unlikely to collide with anything, and can be conveniently pulled back in the event. discussion welcome!
I have a custom routing class that checks versioning of an object to allow for creation of draft versions of pages that wont appear on the live site. When an admin clicks to preview a draft version of a page my PublishingHelper class on the front-end (called from the routing class) checks the user's permissions to see if they are allowed to view the draft version of this page.
I am using this code:
$context = sfContext::getInstance();
$user = $context->getUser();
But $user is NULL.
Does anyone have any ideas? All my searches seem to say this is the right way of getting the user object.
Thanks,
Tom
Thanks for the comments Till/Jon, Ive managed to fix this now. The factories fix didnt work because while the user class is instantiated none of the filters have run therefore I was left with a useless user object.
I solved my problems simply by taking pretty much all the code in the matchesUrl() function of my custom routing class and putting in a new function doRouting() in the same class. matchesUrl() now looks like this:
public function matchesUrl($url, $context = array())
{
if (false === $parameters = parent::matchesUrl($url, $context))
{
return false;
}
$parameters['module'] = 'content';
$parameters['action'] = 'route';
$this->url = $url;
$this->context = $context;
return $parameters;
}
and the routing is deferred to after the factories and filters by using my "content" module/controller:
class contentActions extends sfActions
{
public function executeRoute(sfWebRequest $request)
{
$router = $this->getRoute();
$router->doRouting($router->url, $router->context);
}
And the doRouting() function now forwards directly to the appropriate module/action (correctly taking into account user permissions).
I think implementing such a restrictions would be easier to implement as a filter: http://www.symfony-project.org/reference/1_4/en/12-Filters