i use ratchet to create chat. but i want to send message from controller to specific user. how i can access to $connections to find the user and sent the message
public function onOpen(ConnectionInterface $conn)
{
$this->connections[] = $conn;
}
i run server like this
$server = IoServer::factory(new HttpServer(
new WsServer(
new Notification($this->getContainer())
)
), 8080);
$server->run();
The best solution would probably be to use the SessionProvider:
http://socketo.me/docs/sessions
There are also simpler solutions, like using the connection id or a name for each connection.
Also, here's a great article on Ratchet with Symfony:
https://medium.com/#nihon_rafy/create-a-websocket-server-with-symfony-and-ratchet-973a59e2df94
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 2 years ago.
Improve this question
I am developing an application that have a Symfony Messenger component installed to handle async messages. The handler of message need to check some permissions for some particulars users, like if one determinate user should receive an email with information if have edition permissions for example.
To achieve that we use Symfony voters, but when we haven't any user logged into the system like in console commands and async messages is very annoying. What is the best solution to that? That are my main ideas:
Force a "login" with the security context for message
Pro: One way to check permissions without additional services. Voter is the service.
Cons: When I have a collection of users check I should do "security context login" action multiple times. I think that is hard.
Design a domain service to handle that.
Pros: Solves the problem without force a login
Cons: Duplicate code or differents ways to do the same things depending on the context (request, console command or async queue)
A service that should be called by voter and domain service
Cons: I think that add complexity to more simple issue
What is the best way? Any ideas outside of the previous three points?
Thank you so much
I would probably prefer to check user's permissions before dispatching a message, but let's think how we can approach if it's not a suitable case.
In order to check user permissions, you need to authenticate a user. But in case you're consuming a message asynchronously or executing a console command it's not straightforward, as you don't have an actual user. However, you can pass user id with your message or to a console command.
Let me share my idea of a simple solution for Symfony Messenger. In the Symfony Messenger, there is a concept of Stamps, which allows you to add metadata to your message. In our case it would be useful to pass a user id with a message, so we can authenticate a user within the message handling process.
Let's create a custom stamp to hold a user id. It's a simple PHP class, so no need to register it as a service.
<?php
namespace App\Messenger\Stamp;
use Symfony\Component\Messenger\Stamp\StampInterface;
class AuthenticationStamp implements StampInterface
{
private $userId;
public function __construct(string $userId)
{
$this->userId = $userId;
}
public function getUserId(): string
{
return $this->userId;
}
}
Now we can add the stamp to a message.
$message = new SampleMessage($payload);
$this->messageBus->dispatch(
(new Envelope($message))
->with(new AuthenticationStamp($userId))
);
We need to receive and handle the stamp in order to authenticate a user. Symfony Messenger has a concept of Middlewares, so let's create one to handle stamp when we receive a message by a worker. It would check if the message contains the AuthenticationStamp and authenticate a user if the user is not authenticated at the moment.
<?php
namespace App\Messenger\Middleware;
use App\Messenger\Stamp\AuthenticationStamp;
use App\Repository\UserRepositoryInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class AuthenticationMiddleware implements MiddlewareInterface
{
private $tokenStorage;
private $userRepository;
public function __construct(TokenStorageInterface $tokenStorage, UserRepositoryInterface $userRepository)
{
$this->tokenStorage = $tokenStorage;
$this->userRepository = $userRepository;
}
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
/** #var AuthenticationStamp|null $authenticationStamp */
if ($authenticationStamp = $envelope->last(AuthenticationStamp::class)) {
$userId = $authenticationStamp->getUserId();
$token = $this->tokenStorage->getToken();
if (null === $token || $token instanceof AnonymousToken) {
$user = $this->userRepository->find($userId);
if ($user) {
$this->tokenStorage->setToken(new UsernamePasswordToken(
$user,
null,
'provider',
$user->getRoles())
);
}
}
}
return $stack->next()->handle($envelope, $stack);
}
}
Let's register it as a service (or autowire) and include into the messenger configuration definition.
framework:
messenger:
buses:
messenger.bus.default:
middleware:
- 'App\Messenger\Middleware\AuthenticationMiddleware'
That's pretty much it. Now you should be able to use your regular way to check user's permissions, for example, voters.
As for console command, I would go for an authentication service, which would authenticate a user if the user id is passed to a command.
in my web api application I get the acess token from http:applicationpath/connect/token with some parameters (this endpoint is from Identity I think, since we dont create it neither can see it).
But now I need to generate the token from a specific controller but cant see how to do this.
Someone knows how this can be made? Or even if it's possible?
Thanks
Some more info:
My application is an integrator (is this the word?) between an android app(app1) and other web application(app2).
1- The app1 user will send the login and password to my application .
2- Then my application will send then to the app2 who will, if everything goes well, return the app2 token .
3- Then I have to save this token in my db.
4- Then verify if the user exists in my db, and if not, save it.
5- And finally generate an token for my application and return it to the user.
Based on your comment:
But can I, instead of change de default endpoint, make another
endpoint that do the same (generate the token)?
it seems that you are rather looking for Extending discovery. This is quite easy actually.
Add a custom entry in the configuration of startup:
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("custom_token", "~/customtoken");
});
And add a controller that handles the request:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
// In case a token is required for login, like the UserInfo endpoint:
//[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
public class CustomTokenController : ControllerBase
{
[Route("customtoken")]
public IActionResult CustomTokenEndpoint()
{
return Ok();
}
}
Update
You can 'replace' the endpoint by disabling the default authorization endpoint and adding a custom endpoint as described above.
Disable the endpoint:
services
.AddIdentityServer(options =>
{
options.Endpoints.EnableAuthorizeEndpoint = false;
})
You may want to use the Authorize path constant.
public const string Authorize = ConnectPathPrefix + "/authorize";
Add the new endpoint:
services.AddIdentityServer(options =>
{
options.Discovery.CustomEntries.Add("authorization_endpoint", $"~/{Authorize}");
});
Please note, I didn't test it, but I think this should work.
I'm trying to create calendar event via PHP for one particular user - say developement#example.com.
I've created Service Account in Google Developers Console, got ClientID, E-mail address and private key. The authentication is done with code:
$client = new Google_Client();
if (isset($_SESSION['service_token'])) {
$client->setAccessToken($_SESSION['service_token']);
}
$cred = new Google_Auth_AssertionCredentials(
'somelongstring#developer.gserviceaccount.com.',
array('https://www.googleapis.com/auth/calendar','https://www.googleapis.com/auth/calendar.readonly'),
file_get_contents('p12 file'));
$client->setAssertionCredentials($cred);
if ($client->getAuth()->isAccessTokenExpired()) {
$client->getAuth()->refreshTokenWithAssertion($cred);
}
$_SESSION['service_token'] = $client->getAccessToken();
This type of authentication seems pretty OK. But all events are created as user with E-mail address somelongstring#developer.gserviceaccount.com instead of developement#example.com.
I've tried setting sub parameter:
$cred = new Google_Auth_AssertionCredentials(
....
$cred->sub = 'developement#example.com';
$client->setAssertionCredentials($cred);
But this piece of code throws exception:
Google_Auth_Exception: Error refreshing the OAuth2 token, message: '{ "error" : "access_denied", "error_description" : "Requested client not authorized." }'
And now I'm lost. Any advice?
OK, resolved ;-)
Problem was with developement on own domain.
As mentioned in other question and in Google SDK Guide I have to grant access for service account to all scopes I request access. I forgot to add read-only scope.
I'm trying to use 2 servers using DDP.connect.
My subscription works well, but methods called using Meteor.call needs the user to be authenticated.
How can i connect the user to the remote server ?
You can authenticate this way:
var DDPConnection = DDP.connect(<url>);
DDPConnection.call("login", {
"password":"qwerty",
"user" : {
"email":"email#email.com"
}
},
function(err,result) {
//Check result
}
);
Check out my other answer on the different login options depending on the setup you have/want to use.
I have been having a look to a digest authentication example at:
http://hc.apache.org/httpcomponents-client-4.3.x/examples.html
In my scenario the there are several threads issuing HTTP requests and each of them has to be authenticated with their own set of credentials. Additionally, please consider this question is probably very specific for the Apache HTTP client 4.3 onwards, 4.2 handles authentication probably in a different way, although I didn't check it myself. That said, there goes the actual question.
I want to use just one client instance (static member of the class, that is threadsafe) and give it a connection manager to support several concurrent requests. The point is that each request will provide different credentials and I am not seeing the way to assign credentials per request as the credentials provider is set when building the http client. From the link above:
[...]
HttpHost targetHost = new HttpHost("localhost", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(targetHost.getHostName(), targetHost.getPort()),
new UsernamePasswordCredentials("username", "password"));
CloseableHttpClient httpclient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build();
[...]
Checking:
http://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html#d5e600
The code sample in point 4.4 (seek 4.4. HTTP authentication and execution context), seems to say that the HttpClientContext is given the auth cache and the credentials provider and then is passed to the HTTP request. Next to it the request is executed and it seems that the client will get credentials filtering by the host in the HTTP request. In other words: if the context (or the cache) has valid credentials for the target host of the current HTTP request, he will use them. The problem for me is that different threads will perform different requests to the same host.
Is there any way to provide custom credentials per HTTP request?
Thanks in advance for your time! :)
The problem for me is that different threads will perform different requests to the same host.
Why should this be a problem? As long as you use a different HttpContext instance per thread, execution contexts of those threads are going to be completely indepenent
CloseableHttpClient httpclient = HttpClients.createDefault();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("user:pass"));
HttpClientContext localContext = HttpClientContext.create();
localContext.setCredentialsProvider(credentialsProvider);
HttpGet httpget = new HttpGet("http://localhost/");
CloseableHttpResponse response = httpclient.execute(httpget, localContext);
try {
EntityUtils.consume(response.getEntity());
} finally {
response.close();
}
I have a similar issue.
I must call n-times a service with a single system user, authenticated with NTLM. I want to do this using multiple threads.
What I came up with is creating a single HTTPClient with no default credential provider. When a request needs to be performed I use an injected CredentialProviderFactory into the method performing the request (in a specific thread). Using this I get a brand new CredentialsProvider and I put this into a Context (created in the thread).
Then I call the execute method on the client using the overload execute(method, context).
class MilestoneBarClient implements IMilestoneBarClient {
private static final Logger log = LoggerFactory.getLogger(MilestoneBarClient.class);
private MilestoneBarBuilder builder;
private CloseableHttpClient httpclient;
private MilestoneBarUriBuilder uriBuilder;
private ICredentialsProviderFactory credsProviderFactory;
MilestoneBarClient(CloseableHttpClient client, ICredentialsProviderFactory credsProviderFactory, MilestoneBarUriBuilder uriBuilder) {
this(client, credsProviderFactory, uriBuilder, new MilestoneBarBuilder());
}
MilestoneBarClient(CloseableHttpClient client, ICredentialsProviderFactory credsProviderFactory, MilestoneBarUriBuilder uriBuilder, MilestoneBarBuilder milestoneBarBuilder) {
this.credsProviderFactory = credsProviderFactory;
this.uriBuilder = uriBuilder;
this.builder = milestoneBarBuilder;
this.httpclient = client;
}
// This method is called by multiple threads
#Override
public MilestoneBar get(String npdNumber) {
log.debug("Asking milestone bar info for {}", npdNumber);
try {
String url = uriBuilder.getPathFor(npdNumber);
log.debug("Building request for URL {}", url);
HttpClientContext localContext = HttpClientContext.create();
localContext.setCredentialsProvider(credsProviderFactory.create());
HttpGet httpGet = new HttpGet(url);
long start = System.currentTimeMillis();
try(CloseableHttpResponse resp = httpclient.execute(httpGet, localContext)){
[...]
For some reasons I sometimes get an error, but I guess it's an NTLMCredentials issue (not being thread-safe...).
In your case, you could probably pass the factory to the get methods instead of passing in creation.