Meteor page reload with Roles - meteor

I am having a problem with Roles in my application when I reload the page. If I use the buttons and so on and navigate like you should on a website the problem is not there.
But what is the problem? In my meteor template, I should show a different view for admins and normal users, so in my template onRendered function I check the role and react on that. Here follows the code:
/* Predefined changes to current html are done in this function!*/
Template.Planning.onRendered(function () {
var userid = Meteor.userId();
if (Roles.userIsInRole(userid,'admin', Roles.GLOBAL_GROUP)){
//do something
}
});
For a specific reason the code inside the if-block is not executed when I am logged in as admin but reload the page. It does work when I visit it using the navigationbar, so I guess the Roles are not yet loaded when the onRendered-function is called. How can I fix this issue?

The package alanning:roles works in a way, that it "autopublishes" the roles definitions to the client, so that you are able to have them available "immediately".
Immediately means in this case, when their subscription is ready. Until then the function Roles.userIsInRole will not return anything truthy.
In order to check of they are available use the reactive Roles.subscription.ready() method.
If you have trouble with reactivity in onRendered you may check for the roles in autorun of onCreated.
For example:
Template.Planning.onCreated(function () {
const instance = this
instance.state = new ReactiveDict()
instance.autorun(() => {
if (Roles.subscription.ready()) {
var userid = Meteor.userId()
if (Roles.userIsInRole(userid,'admin', Roles.GLOBAL_GROUP)){
instance.state.set('isAdmin', true) // reactive data source
}
}
})
})
Using it in a router
You can even use this method on the router level in order to "wait" for all the roles to be loaded. This makes sense especially when your client routing logic makes heavy use of roles to manage access.
Note on Security
Don't forget, that this is just UI Candy. Roles (as well as routes) on the client can be bypassed. Re-check every method call and subscription that are sensitive twice in side the methods / publications using Roles.userIsInRole.

Related

Add custom field on signin form

Using meteor useraccounts package, I would like to add a custom field in signIn form (named token) to enable 2fa authentication.
Unfortunatly on AccountTemplates.addField only work with signUp form, as far as I have worked on it.
Any hint?
The accounts package has an Accounts.onLogin function that you can use to call a method and update the user account.
Accounts.onLogin(function(user) {
Meteor.call('setToken', user)
})
and then
Meteor.methods({
setToken: function(user) {
// Do some clever check
Meteor.users.update(/* Set your token */);
},
});
The advantage of using a method is that you can do some server-side check to ensure your token has not been hacked.
The answer i managed to implement was to change the pattern with a different approach, using directly Meteor API:
Set a special field on user collection: tokenverified: false
Add Meteor.onLogin (called each time you login or refresh manually the page) and Meteor.onLogout callback to set this field to false
Create a template verify2FA to deal with token and set tokenverified: true
Create an iron-router plugin ensure2FA that will check this token and redirect to the verify2FA template.

Meteor: How do I show a page when a user signs up

When a person signs up (accounts entry) it takes them to a page called "dashboard". I want it so that after they sign-up if it is the first time (for that account) that they are seeing the page it will show some sort of welcome message. Is this possible?
Thanks.
Update:
I tried this:
Accounts.onCreateUser(function(options, user) {
console.log('New account created!');
});
But it gave:
Exception while invoking method 'entryCreateUser' Error: insert requires an argument
I am using Accounts Entry. Is there any fix for this?
Without knowing more about your app it's difficult to advise on the best way for you to do this. But here are three possible approaches.
If you are creating your own signup/login events, just route to a 'welcome' route/template on signup event, and 'dashboard route/template on login.
If you are wanting to use default accounts-ui, you can use the Accounts.onCreateUser hook server side to add {'isNewUser' : true} to the user account document. Then check for this property client side to decide what template to display.
Or you can try wrapping Accounts.createUser to include the extra logic you require client side to go to your welcome route/template rather than the dashboard.
The simplest way is to set a session variable then use a helper in your template to key off of that:
In your new account code:
Session.set('isNewUser',true);
Router.go('dashboard')
HTML:
<template name='dashboard'>
{{#if newUser}}Welcome!!{{/if}
... rest of your template ...
</template>
js:
Template.dashboard.helpers({
newUser: function(){
return Session.get('isNewUser');
}
});
You'll also need code to later either delete that Session variable or set it to false.

Create OAUTH accounts prior to signin

I have an app I am working on that has one or two "main" accounts linked to other accounts. I am using accounts-password, accounts-google, and alanning:roles currently, but I may add other OAuth providers later.
The idea is that there will be one or two users that are "admins" for a larger group, and as such I'd like to be able to allow them to add users. I don't mind if I have to go through the OAuth authorization page on first login, but I would like to be able to add the users to the Meteor.users collection and allow the admins to set things up prior to their logging in, but I am not sure how to do it at all. Can I simply add a user to the Meteor.users collection with only the email attribute of the google sub-document populated? I would guess that the email is NOT what Meteor uses to connect a stored Meteor.users document to the corresponding Google OAuth account as there is also an id attribute that seems like it might be more useful for that purpose.
So based on what you say on the comment i elaborate this DEMO, since you want to create users and add that users to some kind of "role" or "group" im using here the meteor-roles package.
allow people to create a group of users
This could be done on some differentes ways, for the demo purpose im creating 1x1 user and leater assign them a Role.
to avoid the autologin behavior you should use a Meteor.method.
//server
Meteor.methods({
createSimpleUser:function(email){
return Accounts.createUser({
email:email,
password:"test123" //you can force the user in the first login to change the password.
})
}
})
//Client
Meteor.call('createUser',"test#gmail.com")
Now that you have the user created, you should assign them a role.
So on some event handler you want,do something like this.
//this should be incide an {{#each}} or {{#with}} in order to this._id works, if not use Sessions
Meteor.users.update({_id:this._id},{$set:{group:newGroup}})
This is just the global idea, you should protect in the allow rules, who can edit the users group, you can also use template helpers like
{{if userIsInRole 'nameGroup'}}
<!-- Show content only available to users in this group -->
{{else}}
<!-- Some warning access denied template -->
{{/if}}
This process will add some stuff to the profile of the user so the app
can associate all the users together
Filter by groups.
Template.example.helpers({
groupX:function(){
return Meteor.users.find({group:"X"})
}
})
when they first login without erasing the stuff I setup for them prior
to them signing in.
Here again there could be many reasons, for example you can add some field inside the current user created.
Meteor.methods({
createSimpleUser:function(email){
return Accounts.createUser({
email:email,
password:"test123",
firstLogin:false //by default the users login is false.
})
}
})
and in the iron route, create a function(why on the router? see Overworked helpers on the David Weldon webpage).
requireEdit = function(){
var query = Meteor.users.findOne({_id:this.params._id})
if(query.firstLogin == false){
this.render('profileEditin') //do some change password here and other stuff
}else{
this.next()'
}
}
And call it on the onBeforeAction() method
This is just the idea and demo is a ugly-fast example of how it should work, good luck

How can I access another users (not current user) profile from the userId in Meteor?

I doing the following. Adding the current users id as userId in a collection as the author. I would like to be able to access other information in that users (not necessarily the logged in users but could be) so that I could display a profile page. The only work around I have found is to just duplicate the fields from the profile and place them in the collection.
This seems to be less than ideal. Is there a way to access this directly? I am using autopublish so I don't think there should be any permission issues. I am also using iron-router so ideally I would have a route set up like:
Router.map(function() {
...
this.route('profile', {path: '/profile', data: function() { this.params._id}});
});
If you've got autopublish on, then Meteor.users.findOne({_id: 'USERID'}) will give you that user's profile. Obviously, if you turn off autopublish you'll have to work out what information to publish.

Serving 'static' node pages with Meteor

I'm working on a meteor app, and as part of it it would be very nice to return some static pages containing JSON.
The JSON they return is generated by running some node (connecting to the Twitter API), however it does not reflect any underlying Meteor collection, so I don't think any of the packages that allow you to build an API on your meteor app would be appropriate.
I can see that one solution is to do this part outside of meteor, however I like the idea of only having one thing to deploy and wondered if there is a solution in meteor, possibly by making a package?
Yes, as justswim already said in the comments, I think you are looking for something like this:
Router.map(function () {
this.route('serverFile', {
path: '/posts/:user',
where: 'server',
action: function () {
var user = this.params.user
// get your data from Twitter API, e.g., using the HTTP package.
this.response.end(JSON.stringify(yourobject));
}
});
});
You can easily define a meteor API using the Meteor iron-router. Just define the route that you want to serve as the call for your api. When a user hits this route, your app will render the corresponding template (in which you can place the static json).
In your Router's map function, you might have something like this:
Router.map(function () {
/**
* The route's name is "jsonTemplate"
* The route's template is also "jsonTemplate"
* The template will be rendered at http://yourapp.com/apiEndpoint
*/
this.route('jsonTemplate', {
path: '/apiEndpoint',
template: 'jsonTemplate'
});
});

Resources