Is it possible to modify the routing based on the logged in user's id?
For example, lets say I have the route "/users/1/comments" to get all comments for user number 1, and "users/1/settings" for their settings.
Is there anyway "users/current/comments" and "users/current/settings" could go to the same endpoints if the logged in users {id} was 1?
I thought of using a listener and redirect, but that's sounds horrible.
You can base this on the logged in user without specifying its id in the url, so you would have
/users/comments
which redirects to a controller. In that controller you could just get the user id
$this->getUser()->getId()
For the 2 routes, you could just link those both to the same action.
In fact, I let it go to the same action in the controller as the the numbered {id} values.
ie. users/{id}/comments and users/current/comments both went to:
commentsAction($id)
{
if ($id=='current') {
$securityContext = $this->get('security.context');
$currentuser = $securityContext->getToken()->getUser();
$id=$currentuser->getId();
}
// etc...
}
Related
First of all thank you for taking your time takling this difficult topic.
Goal:
My URL structure is like facebook/twitter: site.com/Username
To let the user set the username, I need to check if a path of a route matches a username.
Possibilities & problems:
If somebody is interested, the possible solutions I came up with:
1.) Using getRouteCollection()
Problem: This method shouldn't be used because it rebuilds the route cache and kills performance (see https://github.com/symfony/symfony-docs/issues/6710)
2.) Using a blacklist as RegExp in the User (for Username) Entity to blacklist all sites like "settings", "about", "login", "register"...
* #Assert\Regex(pattern="/^(?!register)(?!login)...
Problem: This is nearly guaranteed to explode because sometimes a URL is forgotten to add into this RegExp
3.) Using CURL to check if the site.com/username results in a 404
Problem: This seems to be a "hack" for me and unprofessional.
Question:
Whats the professional, secure way of checking if a route doesn't already exists, so that the username can be set and is guaranteed to be "free" (via absolute path like site.com/Username and no other route is in the way)?
By example something like $app->hasURL('/username')
Add: use \Symfony\Component\RoutingException\ResourceNotFoundException;
Then, where $router is the routing service, you can attempt to match the url to a route. If it doesn't match anything it will throw an exception, but if you have a default catch-all route it will always match something so you would need to just detect if it matches the catch-all instead:
$routeIsAvailable = false;
try {
$route = $router->match( '/' . $username );
// If you are using a catch-all route to load profiles
if ( $route[ '_route' ] === 'name_of_catch_all_route' )
$routeIsAvailable = true;
} catch ( ResourceNotFoundException ) {
// If the url didn't match any route at all
$routeIsAvailable = true;
}
Make sure that you validate $username first so that it doesn't have any characters like ? or /. You can also urlencode it, but you probably don't want your user's profile urls to have encoded characters in them, so it's better to just restrict the valid usernames.
At the moment this is a general question with no code as I am looking for a BEST practices example to my question:
User issues an email change request. (done)
A link is sent to the new address to confirm the new email. (done)
User clicks the confirmation link and the DB update is complete. (done)
What also needs to happen is when the confirmation link is sent for the change, an email should also be sent to the original email address where the user can click a link to reverse the process for whatever reason. I would think also that even if the new email address was accepted, if the original link denies the change it reverts and 2) if the original email reverts and then the new email link is confirmed, that the request would then be denied.
Any direction or code on this matter would be greatly appreciated.
Seems like a simple bit field in the database user record would suffice, or an associated database record would work too. When both emails are sent, mark the field for that user, let's call it "ChangeEmailSent" to 1. When either email is clicked, the field should be updated to 0. The actual changing of the email should only occur if the field is 1.
Some pseudo-code if you like
private void CancelEmailChange(email)
{
var user = Database.GetUser(email);
user.ChangeEmailSent = false;
Database.Save();
}
private void ProcessEmailChange(email)
{
var user = Database.GetUser(email);
if (user.ChangeEmailSent)
{
user.email = getNewEmailAddress(); //whatever logic for a new email
user.ChangeEmailSent = false;
Database.Save();
}
}
Assuming I want to create users upon authorizing the app, how would I grab their email during the onAuth callback...? Looks like the callback assumes the user is already logged in. Am I thinking about it correctly?
I noticed when installing the Fishbowl Prizes app, after auth I can click on the accounts tab and see that all my account info is pre-populated from my shopify store account (name, email, address, etc).
I'm not sure if I should go by the title or the content of the post in terms of answering your question, so I'll provide a very simple example of how to get the info from the API and do something with it here.
I have provided a more in depth answer related specifically to grabbing the details from the API for user account creation here: https://github.com/froatsnook/meteor-shopify/issues/15#issuecomment-177413630
Looks like the callback assumes the user is already logged in.
The userId param is undefined if there is no user. If your onAuth operations don't need to do anything with the user, you can just leave it out of the params. In your case you'll just want to handle it conditionally using an if/else block:
if(!userId){
// do stuff
} else {
// do other stuff
}
On to the example of grabbing those details from the API:
All the prepopulated information you are seeing is available from the Shopify API in the shop object. You already have the access token when onAuth callbacks are fired, so you can just grab it from the API immediately after you have inserted the shop's Keyset.
For the sake of simplicity, in this example we'll assume the user already exists and is logged in. In your server-side onAuth callback (after you have inserted the keyset) you can do something like this to add those fields to the user's profile object:
Shopify.onAuth(function(access_token, authConfig, userId) {
var shopUUID = uuid.new(); // Not secure to name keyset same as the shop!
Shopify.addKeyset(shopUUID, {
access_token: access_token
});
var api = new Shopify.API({
shop: authConfig.shop,
keyset: shopUUID
});
// get the Shop object from the API
var shopObj = api.getShop();
var userInfo = {
'profile.name': shopObj.shop_owner,
'profile.email': shopObj.email,
'profile.phone': shopObj.phone,
'profile.shopName': shopObj.name
};
Meteor.users.update({_id: userId}, {$set: userInfo})
});
Then you can use them in templates like this:
{{currentUser.profile.name}} or {{currentUser.profile.email}}
Or in functions like so:
var realName = Meteor.user().profile.name
or
var userEmail = Meteor.user().profile.email etc
For a more about using this data for user creation, see my explanation here:
https://github.com/froatsnook/meteor-shopify/issues/15#issuecomment-177413630
Is there any way to show each registered user's post in user side like http://mydomain.com/registered_username? ie, if registered_username is a registered user, then his posts must be accessed by any normal users like http://mydomain.com/registered_username
function functionName(registered_username)
{
window.location = "mydomain.com/?registered_username=" + registered_username;
}
Meteor has a loginWithToken method, and there are resume tokens in the user object. So one can login using one of these tokens with loginWithToken. That works.
Is there a way to generate new login tokens, or should I just use the resume tokens to create a quick login link?
As Johnny said, you can use the Accounts._generateStampedLoginToken() function, which is actually nothing special, just the following function:
_generateStampedLoginToken = function () {
return {
token: Random.secret(),
when: new Date
};
}
anyway, to use it, here is an example:
// Server //
// Creates a stamped login token
var stampedLoginToken = Accounts._generateStampedLoginToken();
/**
* Hashes the stamped login token and inserts the stamped login token
* to the user with the id specified, adds it to the field
* services.resume.loginTokens.$.hashedToken.
* (you can use Accounts._hashLoginToken(stampedLoginToken.token)
* to get the same token that gets inserted)
*/
Accounts._insertLoginToken(user._id, stampedLoginToken);
// Client //
// Login with the stamped loginToken's token
Meteor.loginWithToken(stampedLoginToken.token);
Yes, you can generate new tokens by calling Accounts._generateStampedLoginToken(). You can call it from within a login handler.
https://github.com/meteor/meteor/blob/master/packages/accounts-base/accounts_server.js#L114
it's 2015 - use one of these packages:
poetic:accounts-passwordless
acemtp:accounts-passwordless
http://fastosphere.meteor.com/?q=passwordless