MeteorJS - sync with MongoDb only for logged in users - meteor

I have an app written in MeteorJS, the functionality is only for logged in users, and all the documents in Mongo have an userId field for each logged in user.
However, I want now to add a "demo" functionality, were on the landing page the user can click instead of "log on" a "try out the demo" button.
The main difference in functionality would be, that the "demo user" doesn't store anything in the MongoDb database and all data and operations are performed only on the local MiniMongo database.
Is there any easy way to achieve this?
I know about new Meteor.Collection(null) that it is only locally, but I define the collection on the global level of the app where I don't have access to Meteor.userId()' orthis.userId` so it would have to check on every place which collection to use.

Ended up with following approach:
On place were I define my collections (shared code between server and client) I define two collections:
var Docs = new Meteor.Collection("docs");
var DocsOffline = new MeteorCollection(null);
no in each meteor method I access the collection using following variable:
let docsSource = Meteor.userId() ? Docs : DocsOffline;
and then operate on docsSource
On the client in the getMeteorData mixin method I have similar approach to make the data available (I use ReactJS)

Related

Check if a user is currently online to meteor server

I'd like to determine if a user is currently "online" or connected to the Meteor server.
I need this information before I send the user message, If the user is not connected I'd like to send the message via email.
I know that for traditional web applications that are totally state-less the definition of "online" use is a bit not clear but since modern web frameworks rely on websocket, a user is supposed to be online if a websocket is open.
The question is does Meteor include a method to determine if a user is connected or not?
Summarized: yes, there is such a mechanism.
There are for example package, that store the active login connections of the users with the meteor server and make them available either via an own collection or as part of the user profile.
See: https://github.com/dburles/meteor-presence
(Creates a new collection, called Presences)
or https://github.com/dan335/meteor-user-presence/
(Creates a user's profile entry, called presence. However, has also a collection to store and update the information in the background)
or https://github.com/mizzao/meteor-user-status
(Thanks to blueren in the comments)
Code example (from the first listed package)
Meteor.onConnection(function(connection) {
// console.log('connectionId: ' + connection.id);
Presences.insert({ _id: connection.id });
connections[connection.id] = {};
tick(connection.id);
connection.onClose(function() {
// console.log('connection closed: ' + connection.id);
expire(connection.id);
});
});
If you don't want to rely on the packages you may make use of that mechanism yourself.
See: https://docs.meteor.com/api/connections.html#Meteor-onConnection

How to access meteor collection through the database

I want to have my application's admin code hosted on a completely different app that shares the same database. However, that means that my collections are defined, at least in the code, in the global namespace of my main application and not my admin application. How can I access my collections, that are in the database, without having the global variables defined in a file shared between the meteor server/client? For reference, I am using this article as the idea to set up my admin tools this way. admin article
To simplify the problem, let's say you have:
two applications: A and B
one shared collection: Posts
one shared database via MONGO_URL
Quick and Dirty
There's nothing complex about this solution - just copy the collection definition from one app to the next:
A/lib/collections.js
Posts = new Mongo.Collection('posts');
B/lib/collections.js
Posts = new Mongo.Collection('posts');
This works well in cases where you just need the collection name.
More Work but Maintainable
Create a shared local package for your Posts collection. In each app: meteor add posts.
This is a little more complex because you'll need to create a pacakge, but it's better for cases where your collection has a model or other extra code that needs to be shared between the applications. Additionally, you'll get the benefits of creating a package, like testing dependency management, etc.
Each application will have its own code but will share the same mongo db. You'll need to define the same collections (or a subset or even a superset) for the admin app. You can rsync certain directories between the two apps if that makes that process either but there isn't anything in Meteor that will do this for you afaik.
Or you could share data between the two servers using DDP:
var conn = DDP.connect('http://admin-server');
Tracker.autorun(function() {
var status = conn.status();
if(status.connection) {
var messages = new Mongo.Collection('messages', {connection: conn});
conn.subscribe('messages', function() { console.log('I haz messages'); });
}
});
This creates a local collection named messages that pulls data from the "admin server" over DDP. This collection only exists in memory - nothing is created in mongo. You can do this on the server or client. Definitely not the best choice for large datasets. Limit the data transfer with publications.

Meteor 1.2 : Sharing Meteor.users between 2 applications over DDP

Let's say I have two web-applications completely different. A being my main application and B, an other forked app pretty heavy already. I want to share my Meteor.users collection between both of them so people can (auto)Login and navigate between both applications without any frictions, wether they are on the main app or the other.
So far, I tried going the DDP way with:
Meteor.connection = DDP.connect('http://localhost:3008/')
Accounts.connection = Meteor.connection
Meteor.users = new Meteor.Collection('users', {connection: Meteor.connection})
Meteor.connection.subscribe('users', function() {
var users = Meteor.users.find()
console.log(users.count())
})
With Meteor's magic, I can now login with my A account on B, it also seems I can update my docs through B via the web-console. Everything seems awesome. My problem is B being a real app too, I have some server-side logic going on there and it appears my users collection, Meteor.user() and so on are undefined. I need those users to be accessible from the server too and modify the collection.
Am I right to use DDP or should I look into solutions like arunoda:meteor-cluster or any other? Is it due to some changes made with 1.2 release?
You should be able to use DDP.connect on the server to access the users from the other server.

How to upload existing mongodb users into Meteor app?

We have an existing mongodb which I imported into my Meteor app using:
mongorestore -h 127.0.0.1 --port 3001 -d meteor dump/mymongodb
Now it seems I can access any collection just find by using the collection name for example:
Meteor.publish 'works', ->
Works.find({}, {limit: 10, sort: {createdAt: -1}})
Seems to work, as we had a collection named works. I didn't even have to define it:
#Works = new Meteor.collection('works')
although it probably wouldn't hurt to define it.
But I am lost when it comes to the Meteor.users collection. We had a collection in our database called users, but I cannot access that one. How can I get our users collection from other mongodb into Meteor.users for the app?
PS. I can access the users collection directly from terminal using 'meteor mongo' and search db.users.findOne() but I cannot seem to access it from the code files.
You need collections to be defined in order to use them. Meteor.users is defined by the accounts packages (e.g. accounts-password). If you are not using any of those packages, you will need to define the collection like so:
#Users = new Mongo.Collection 'users'
or
#Meteor.users = new Mongo.Collection 'users'
I regularly do this in apps which talk to our database but which don't require any user interaction (db migrations, stats reporting, etc.).
Of course, without the accounts packages installed, you get none of the other user functionality, but that may not matter in your case.

How do you secure the client side MongoDB API?

I don't want just all of my users being able to insert/destroy data.
While there is no documented way to do this yet, here's some code that should do what you want:
Foo = new Meteor.Collection("foo");
...
if (Meteor.is_server) {
Meteor.startup(function () {
Meteor.default_server.method_handlers['/foo/insert'] = function () {};
Meteor.default_server.method_handlers['/foo/update'] = function () {};
Meteor.default_server.method_handlers['/foo/remove'] = function () {};
});
}
This will disable the default insert/update/remove methods. Clients can try to insert into the database, but the server will do nothing, and the client will notice and remove the locally created item when the server responds.
insert/update/remove will still work on the server. You'll need to make methods with Meteor.methods that run on the server to accomplish any database writes.
All of this will change when the authentication branch lands. Once that happens, you'll be able to provide validators to inspect and authorize database writes on the server. Here's a little more detail: http://news.ycombinator.com/item?id=3825063
[UPDATE] There is now an official and documented Auth Package which provides different solutions to secure a collection.
On a CRUD level :
[Server] collection.allow(options) and collection.deny(options). Restricts default write methods on this collection. Once either of these are called on a collection, all write methods on that collection are restricted regardless of the insecure package.
And there is also insecureto remove full write access from the client.
source : Getting Started with Auth (thanks to #dan-dascalescu)
[OLD ANSWER]
Apparently there are working on Auth Package(?) that should avoid any users taking full control on the db as it is now. There is also someone suggesting that there is an existing solution (workaround) by defining your own mutations (methods) and make them failed if they attempts to perform an unauthorized action. I didn't get it much better but I think this will often be necessary since I doubt the Auth Package will let you implement the usual auth logic on a row level but probably only on the CRUD methods. Will have to see what the devs have to say.
[EDIT]
Found something that seems to confirm my thoughts :
Currently the client is given full write access to the collection. They can execute arbitrary Mongo update commands. Once we build authentication, you will be able to limit the client's direct access to insert, update, and remove. We are also considering validators and other ORM-like functionality.
Sources of this answer :
Accessing to DB at client side as in server side with meteor
https://stackoverflow.com/questions/10100813/data-validation-and-security-in-meteor/10101516#10101516
A more succinct way:
_.each(['collection1', 'collection2'], function(collection){
_.each(['insert','update', 'remove'], function(method){
Meteor.default_server.method_handlers['/' + collection + '/' + method] = function(){}
});
});
or to make it more idiomatic:
extend meteor:
_.extend(Meteor.Collection.prototype, {
remove_client_access: function(methods){
var self = this;
if(!methods) methods = ['insert','update','remove'];
if(typeof methods === 'String') methods = [methods];
_.each(methods, function(method){
Meteor.default_server.method_handlers[self._prefix + method] = function(){}
});
}
});
Calls are simpler:
List.remove_client_access() // restrict all
List.remove_client_access('remove') //restrict one
List.remove_client_access(['remove','update']) //restrict more than one
I am new to Meteor, but what I have come across so far are these two points
You can limit what a client can access in the database by adding parameters to the find command in the server-side publish command. Then when the client calls Collection.find({}), the results that are returned correspond to what on the server side would be, for example, Collection.find({user: this.userId}) (see also Publish certain information for Meteor.users and more information for Meteor.user and http://docs.meteor.com/#meteor_publish)
One thing that is built in (I have meteor 0.5.9) is that the client can only update items by id, not using selectors. An error is logged to console on the client if there is an attempt that doesn't comply. 403: "Not permitted. Untrusted code may only update documents by ID." (see Understanding "Not permitted. Untrusted code may only update documents by ID." Meteor error).
In view of number 2, you need to use Meteor.methods on the server side to make remote procedure calls available to the client with Meteor.call.

Resources