Meteor restrict access to files and collections - meteor

In my app I have an admin area, this area also has some admin-client MiniMongo that goes with it. So the admin is subscribing to a publication, then this publication puts data from several collections on this admin-client MiniMongo collection.
The access to the subscription is restricted, and so is the publication. But I noticed that the collection still registers for every user, though (I believe) it is being populated with data only when an admin logs in.
Still I would like to know if I can create the collection only when an admin logs in and not before?
Now the code is being run on app startup.
Right now I have it like this:
export const AdminData = new Mongo.Collection("admin_data");
I also tried this, but it doesn't work:
let AdminData;
if(Roles.userIsInRole(Meteor.userId(), 'admin'){
AdminData = new Mongo.Collection("admin_data");
}
export default AdminData;
If it make a difference this is my project structure
client/
server/
imports/
--api/
----admin/
------client/
--------adminData.js (the above file)
------server/
--------publication.js
--startup/
----client/
----both/
----server/
--ui/
----adminArea/
------adminComponent.jsx (the collection is only imported here)

You should create the collection on the server. (move adminData.js besides publication.js). Also you should check security in the publication itself. Meteor.userId() will only work in meteor methods on the server and you will have an error if you leave it there
On the client your adminComponent needs to subscribe to the server publication to have access to the collection's data. To do so you'll need to wrap your component in something like react-komposer
Hope this helps

Related

Firebase Cloud Functions onDelete - How to access parent's information?

So, from Firebase functions, I'm listening to this event -
exports.populateVairations_delete =
functions.database.ref('/parentA/parentB/child').onDelete(event =>
{
// I know how to get the previous value for what I'm listening too...
val = event.data.previous.val();
...
}
This function is being invoked also when deleting the parent, which is exactly what I want.
But when deleting a parent, how do I access data from /parentA before it's being deleted?
onDelete triggers are always executed after the delete has occurred. There's no way to prevent a delete from happening with a function. Your onDelete code will be delivered an event that contains only the data that was deleted. The event object itself can't be used to see other parts of the database.
If you need to access other parts of the database inside a database trigger, you can use the Admin SDK to make those queries. There is a lot of official sample code that illustrates how to do this.
With context.resource.name you could get a string containing the data path.
Just use Admin SDK for Firebase, You can have administrator access to the firebase Db.From there, you can do basically anything with the Firebase Db

Meteor Client-side Collection Document Appears and Disappears

After lots of reading, I'm starting to get a better handle on Meteor's publish/subscribe model. I've removed the autopublish training wheels from my first app and while I have most everything working, I am seeing one issue.
When the app first loads, my publish and subscribe hooks work great. I have a block of code that runs in a Tracker.autorun() block which makes the subscribe calls, I am able to sequentially wait for data from the server using ready() on my subscribe handles, etc.
One feature of my app is that it allows the user to insert new documents into a collection. More specifically, when the user performs a certain action, this triggers an insert. At that point, the client-side JS runs and the insert into MiniMongo completes. The reactive autorun block runs and the client can see the inserted documented. The client updates the DOM with the new inserted data and all is well.
Furthermore, when I peek into the server-side MongoDB, I see the inserted document which means the server-side JS is running fine as well.
Here's where it gets weird. The client-side autorun block runs a second time (I'm not sure why) and this time, the client no longer has the inserted item. When the DOM renders, the newly inserted item is now gone. If I reload the page, all is well again.
Has anyone seen this behavior before? I'm also noticing that the server-side publish call runs once on page load but then it doesn't run again after the insert. This seems wrong because how else will the client get the reconciled data from the server after the insertion (i.e. after Meteor's client-side latency compensation)?
The important functions (ComponentInstances is the collection that is bugging out):
Publish block:
Meteor.publish('allComponentInstances', function (documentId, screenIndex) {
console.log(`documentId: ${documentId} screenIndex: ${screenIndex}`)
const screens = Screens.find({ownerDocumentId: documentId})
const selectedScreen = screens.fetch()[screenIndex]
return ComponentInstances.find({_id: {$in: selectedScreen.allComponentInstanceIds}})
})
Subscription block in autorun:
// ... a bunch of irrelevant code above
const allComponentInstancesHandle = Meteor.subscribe('allComponentInstances', document._id, 0)
if (allComponentInstancesHandle.ready()) {
isReady = true
screens = Screens.find({ownerDocumentId: document._id}).fetch()
const componentInstanceObjects = ComponentInstances.find().fetch()
allComponentInstances = {}
componentInstanceObjects.map((componentInstance) => {
allComponentInstances[componentInstance._id] = componentInstance
})
}
This is most probably you're inserting documents from client side. And you have not set up your permission rules properly. When you remove autopublish and insecure from your app, you are not allowed to insert/update/remove documents into collection unless you have allow/deny rules set up in the server side.
Meteor has a great feature called latency compensation which tries emulate your db operations before it gets the actual write operation in the db. And when the server tries to write in the db, it looks for allow/deny rules.If the permission rules doesn't allow the db operation or Whatever the reason( either allow/deny or authentication) for not actually written in the db, then the server data gets synchronized with your client side db.
This is why i assume you are seeing your document being inserted for the first time and gets disappeared within a second.
check this section of meteor docs.
http://docs.meteor.com/#/full/allow
I ended up solving this a different way. The core issue, I believe, has nothing to do with accept/deny rules. In fact, their role is still hazy to me.
I realize now what I've been reading all along in the Meteor docs: the publish functions return cursors. If the cursor itself doesn't change (e.g. if you're passing specific keys you want to fetch), then it won't really work as a reactive data source in the sense that new documents in a collection will not make the data publish again. You are, after all, still requesting the same keys.
The way forward is to come up with a publish cursor that accurately reflects the reactive data you want to retrieve. This sounds abstract but in practice, it means make sure the cursor is general, not specific to the specific keys you are retrieving.

Meteor.JS: Subscribe no working

Im trying to subscribe my client side to my userFriends collection and Chrome's console display: userFriends is not defined
This is my code:
Server side...
userFriends = new Mongo.Collection("friends");
console.log(userFriends.find().fetch())
Meteor.publish("friends", function () {
return userFriends.find();
});
NOTE: The console.log display in the terminal an empty array which is good
Client side...
Meteor.subscribe("friends");
console.log(userFriends.find().fetch())
NOTE: This is where Chrome's console display the error
what am I doing wrong ?
Thank you
UPDATE 1: Now I can see the Friends collection in Chrome's console, but i cant insert data. I have the subscribe in client.js inside my client folder and my insert code is in friend.js inside client folder aswell.
The collection needs to be defined on both the client and the server. Typically this is done by placing the definition in a shared directory like lib:
lib/collections/user-friends.js
userFriends = new Mongo.Collection('friends');
Note the convention is to name the collection with the capitalized camel case version of the collection name. So calling it Friends would be more typical.
You need to declare the collection on both environments using shared code.
lib/user-friends.js
userFriends = new Mongo.Collection("friends");
client/user-friends.js
Meteor.subscribe("friends", function(){
console.log(userFriends.find().fetch());
});
In the client, be aware that collection subscriptions are asynchronous by nature (there's network latency on the client, inherent to fetching the documents from the server).
This is why if you console.log your collection content right after Meteor.subscribeing you'll get [], but if you wait until the subscription is ready using a callback, documents will be displayed correctly.
You have two correct answers but they do assume some knowledge for you. Here's what it looks like using Meteor's file structure (available at http://docs.meteor.com/#/full/structuringyourapp).
In your /lib (shared) directory
Make a file called "collections.js" and in it create your collection.
userFriends = new Mongo.Collection("friends");
I would instead do userFriends = new Mongo.Collection("userfriends"); so that your are always using the same word for your collection and you change the capitalization depending on if you're working on client or server. This is very helpful.
In Your /client directory
Make a file called "subscriptions.js" and in it subscribe to your collection.
Meteor.subscribe('friends');
In Your /server directory
Make a file called "publications.js" and in it publish your collection.
Meteor.publish('friends',function(){
return userFriends.find();
});
You don't need a fetch or anything there.
Essentially your code is failing because of where you're trying to house everything. What I've given you is three points of where you work. Client, Shared, Server. Set your app up that way and it will be easy to immediately figure out where you're working.
Hope that helps.

Running server side helping scripts

I am working on a multi-player card game (think Yu-gi-oh) based on real-world data. I have a collection "data" with data on individual items and another collection "cards" with actual issued cards in the game.
Cards have many-to-1 relationship with data-items (so one data-item is used to fill parameter-data for a number of copies of a single card, but with different owners).
"Cards" are published to client(s) as a local sub-set collection with all the "data"-items needed for all client-side "cards" items from client publication of "cards".
During the game, and especially in test phase, I need to "produce batches of cards" (and perhaps perform other setup and fine-tuning functions) from command-line/terminal/shell using helper functions with parameters (like cards.issue(10) that would create 10 new cards).
I would like to do that from command-line/terminal/shell to avoid writing admin front-end until I am sure about what will be done manually, and what automatically.
Where would I put a .js file with such helping scripts (functions with parameters) and how would I run them from terminal? How can I access meteor (server-side) objects from terminal/shell?
The easiest way to achieve this is a script in node.js.
1) You put those files whenever you want, just make sure they're not in the Meteor's scope of interest. So if you want to put them in your project directory, put them in a hidden (starting with a .) subfolder.
2) You run those files as typical node script: node path/to/file.js.
3) You don't need to access Meteor structure from that script, just the database. To do so, you need a Mongo driver (node mongodb package - here's the handy documentation), then:
Load it:
var MongoClient = require('mongodb').MongoClient;
Connect to the local db:
MongoClient.connect('local_db_url', function(err, db) {
...
});
Inside the connect callback, insert your objects:
var cards = db.collection('cards');
cards.insert(card, {safe: true});

In Meteor how can I publish one server side mongo collection under different names?

I have a server side mongo collection called Profiles.
I need to publish and subscribe to the entire collection of Profiles if user: adminId.
That way the administrator can edit, updated, etc... each Profile collection item.
But I want users to be able to see their Profile record.
So I tried this...
CLIENT SIDE
MyProfile = new Meteor.Collection("myprofile");
Meteor.subscribe('profiles');
Meteor.subscribe('myprofile');
COMMON - CLIENT AND SERVER SIDE
Profiles = new Meteor.Collection("profiles");
SERVER SIDE - The publishing and subscribing of profiles works fine.
// this returns all profiles for this User
// if they belong to an ACL Group that has acl_group_fetch rights
Meteor.publish("profiles", function() {
var user_groups = Groups.find({users: this.userId()});
var user_groups_selector = [];
user_groups.forEach(function (group) {
user_groups_selector.push(group._id);
});
return Profiles.find( {
acl_group_fetch: {
$in: user_groups_selector
}
});
});
Here is where the problem seems to begin. The Profiles.find is returning collection items because I can output them to the console server side. But for some reason the publish and subscribe is not working. The client receives nothing.
// return just the users profile as myprofile
Meteor.publish("myprofile", function() {
return Profiles.find({user: this.userId()});
});
Any ideas what I am doing wrong. I want to be able to publish collections of records that User A can insert, fetch, update, delete but User B (C, D and E) can only see their record.
I think your issue is more on the MongoDB side than with meteor. Given your case I'd do two collections (Group and Profile).
Each document in the Group collection would feature an array containing DBRefs to documents in the Profile collection (actually users so I would think about renaming the Profile collection to User as imo that's more intuitive).
Same for the Profile collection and its documents; each document in the profile collection (representing a user) would have an array field containing DBrefs to groups the user belongs to (documents inside the Group collection).
I'm not entirely sure of how you're checking for the error or not, but I think you may be running into a gotcha I ran into. When you publish data with the Profiles collection, even though the pub/sub calls use the 'myprofile' name, the data always is always available in the collection that you're returning a cursor for... in this case, the data you publish in the 'myprofile' publication will show up in the 'profiles' collection on the client. The publish call doesn't create a 'myprofile' collection on the client. So if you're trying to find() on the 'myprofile' collection you won't see any data. (Meteor/MongoDB won't complain that the collection doesn't exist because they always will lazily create it when you reference it.)
I think the problem here is that you only need the one collection: Profiles.
So if you just remove the offending line
MyProfile = new Meteor.Collection("myprofile");
Everything should work fine (you'll have both datasets within the Profiles collection).

Resources