These are chat archives for Automattic/mongoose

4th
Dec 2016
Eddie Bracho
@ebracho
Dec 04 2016 20:58
Hello, I'm having trouble figuring out why this query is returning null. https://github.com/ebracho/groupdoodle/blob/master/app/doodle/websocket.js#L34-L37
data.session.token is a token that was created here: https://github.com/ebracho/groupdoodle/blob/master/app/auth/router.js#L38-L42
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:00
can you log out when the token is created
and then open your command line or if you use a GUI for mongo
search for that token in the SessionToken collection
and make sure it exists
Eddie Bracho
@ebracho
Dec 04 2016 21:02
I don't currently have a way to access mongodb outside of my application, I'll figure that out one sec
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:02
what do you mean?
is this in your development environment or in production?
Eddie Bracho
@ebracho
Dec 04 2016 21:02
dev environment, inside a docker image
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:02
just open up a terminal tab, type mongo and then use yourDbName
Eddie Bracho
@ebracho
Dec 04 2016 21:10
@varunjayaraman one sec, almost got this figured out
Eddie Bracho
@ebracho
Dec 04 2016 21:16
@varunjayaraman It doesn't show in the mongo cli either https://gist.github.com/ebracho/a23004dfe9160f4a080ef348b672cd2f
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:17
is that not the correct token?
Eddie Bracho
@ebracho
Dec 04 2016 21:19
That's the token that is in data.session.token, I verified with a log statement
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:19
you said it doesn't show in the mongo cli, are you saying it does or it doesn't? kinda confused
Eddie Bracho
@ebracho
Dec 04 2016 21:20
Sorry, the token is created but it doesn't show in the mongo cli
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:20
ah gotcha
ok
sessiontoken.save() is asynchronous
first off
you should only be sending a 201 status code response if .save() is successful
i wonder if sessiontoken.save is failing
what version of mongoose are you using? It returns a promise in 4.x i believe so you can chain .save().then(() => res.status(201).json({ things: data}).catch(logError)
Eddie Bracho
@ebracho
Dec 04 2016 21:22
I added sessiontoken.save when I was debugging this problem, but I thought that SessionToken.create should've saved it already
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:22
well take out sessiontoken.save then
Eddie Bracho
@ebracho
Dec 04 2016 21:22
ok
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:23
so you're saying the console log does show the session token
Eddie Bracho
@ebracho
Dec 04 2016 21:23
correct
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:23
are you handling the error in SessionToken.create
try logging to see if there's any potential error
Eddie Bracho
@ebracho
Dec 04 2016 21:23
oh, good idea
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:24
this is why i prefer to use promises :P
Eddie Bracho
@ebracho
Dec 04 2016 21:24
I'm new to js but I would like to learn about promises soon
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:24
ah ok, then yeah get acquainted with callback style
and then once you switch to promises youll understand why callbacks can be annoying
but until you do that, always always handle the error in your callbacks
and make sure you return from the error handler or else your success handler will run after you handle the error
Eddie Bracho
@ebracho
Dec 04 2016 21:25
right
SessionToken.create didn't return an error https://gist.github.com/ebracho/56215f31d9f212f9886237dcc9b5f9b2
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:30
ok i need to see 3 things
1) this entire file. I want to see how you get access to the SessionToken model
2) the SessionToken schema
3) How the SessionToken model is created
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:34
can you nest a findOne operation in the callback to SessionToken.create
to retrieve the session token that was just created
nothing jumps out at me from your schemas
also at the top of the file
require in mongoose and do this
const mongoose = require('mongoose').set('debug', true)
ok also i noticed something else you're doing wrong
i wonder if this has something to do with it
you're doing user.setPassword
and then immediately doing SessionToken.create
Eddie Bracho
@ebracho
Dec 04 2016 21:39
Ah
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:39
user.setPassword is async though bc it calls user.save()
Eddie Bracho
@ebracho
Dec 04 2016 21:40
can I do something like user.setPassword.then(...)?
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:40
although i'm not sure that would cause this issue
yeah
.save returns a promise
but you have to .catch the error
also in user.setPassword you have to return user.save()
Eddie Bracho
@ebracho
Dec 04 2016 21:41
I was just about to ask
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:41
if you don't return the promise it'll error and say .then is not a function or something
Eddie Bracho
@ebracho
Dec 04 2016 21:42
What params does .save pass to the callback?
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:42
i think just an options object, i forget
Eddie Bracho
@ebracho
Dec 04 2016 21:42
or promise...?
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:42
ok so it works like this
save() doesn't actually return the object you just created
but it will error if it fails
so you would do it like this
just return user.save() from user.setPassword
and then in the function calling user.setPassword
user.setPassword()
   .then(function() {
      // do stuff with `user`
   }
   .catch(function(error) {
      // handle error
   }
Eddie Bracho
@ebracho
Dec 04 2016 21:44
I like that syntax
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:45
yeah, much more clear than callbacks
Eddie Bracho
@ebracho
Dec 04 2016 21:45
register is turning into spaghetti now though
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:45
yeah
here's how i would refactor it
Eddie Bracho
@ebracho
Dec 04 2016 21:49
wow
that's so clean
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:50
yep
tricky part is that user.setPassword returns a promise but that promise itself doesn't return user
one way to do it is the way i did it
the other way is, inside user.setPassword
to do:
    return user.save()
      .then(function() {
         return user;
       }
and the error will bubble up to the .catch
so you only need to write one error handler
Eddie Bracho
@ebracho
Dec 04 2016 21:52
Oh that's kind of tricky
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:52
yeah i mean it's fine the way it is now
Eddie Bracho
@ebracho
Dec 04 2016 21:52
I'll have read the docs for user.save some more
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:53
oh actually i stand corrected
save does resolve with the thing being saved
Eddie Bracho
@ebracho
Dec 04 2016 21:53
That makes the most sense to me
Varun Jayaraman
@varunjayaraman
Dec 04 2016 21:53
so you can just do return user.save() and the .then callback can take the thing .save resolves with as a parameter
Tank.findById(id, function (err, tank) {
  if (err) return handleError(err);

  tank.size = 'large';
  tank.save(function (err, updatedTank) {
    if (err) return handleError(err);
    res.send(updatedTank);
  });
});
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:01
(this is an example with the callback-style, but most promisified versions of callbacks resolve with the successful param and reject with the error param)
Eddie Bracho
@ebracho
Dec 04 2016 22:01
I like the promisified version better. I'm going to go refactor everything once I figure this bug out
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:02
yeah, also it seems like you never put a token on the session token
so you're using the default shortid.generate?
Eddie Bracho
@ebracho
Dec 04 2016 22:02
right
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:03
in the default funciton in your schema
try doing this
```
default: function() {
}
whoops
default: function() {
   var val = shortid.generate();
   console.log('val', val);
   return val;
}
and then try searching your collection in the mongo cli as db.sessiontokens.find({ token: whateverValWas })
Eddie Bracho
@ebracho
Dec 04 2016 22:07
one sec, testing out the new promiseafied register
ty for all your help by the way, I've been trying to learn this stack all by myself lol
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:08
no worries
Eddie Bracho
@ebracho
Dec 04 2016 22:10
Okay, still not showing up in the mongo cli. I'll try recording the shortid now
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:19
you have to do db.sessiontokens.find
not db.session.find
Eddie Bracho
@ebracho
Dec 04 2016 22:19
oops
result was the same though
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:23
hmm
idk :/
Eddie Bracho
@ebracho
Dec 04 2016 22:23
Its so strange :/
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:26
is the user getting created?
Eddie Bracho
@ebracho
Dec 04 2016 22:26
yeah
I'm starting to suspect it might be a problem with my environment
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:27
maybe
Eddie Bracho
@ebracho
Dec 04 2016 22:33
This debug output should mean its being written to the db, correct?
Screen Shot 2016-12-04 at 2.32.33 PM.png
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:41
doesn't seem like there's an insert op happening
so it never gets created
Eddie Bracho
@ebracho
Dec 04 2016 22:42
ah
I wonder why
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:42
if you pass in { upsert: true } to a mongodb update query
it will create the doc if it doesn't exist
but mongoose doesn't seem to be doing that
whatversion of mongoose are you usin
Eddie Bracho
@ebracho
Dec 04 2016 22:43
"mongoose": "^4.7.0"
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:43
try using 4.6.5
that's what i use
and .create most definitely works in 4.6.5
not sure why User.create works and SessionToken.create doesn't though
Eddie Bracho
@ebracho
Dec 04 2016 22:45
Its so strange. It's gotta be my code though, there's no way its a bug with mongoose
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:46
can you do this
before SessionToken.create
do const newToken = new SessionToken(user)
and then console.log newToken
also the way youre creating the token is a bit weird
instead of passing it user
be extremely clear about the properties you want to set

so do like:

const theToken = {
    // properties and values here
}
SessionToken.create(theToken).then(...).catch(...)
i've never created an object from another collection by passing it a mongoose object from another collection
Eddie Bracho
@ebracho
Dec 04 2016 22:49
function register(req, res) {
  if(req.user) {
    res.status(400).send('User already exists');
    return;
  }
  User.create({userId: req.body.userId})
    .then(function(user) {
      return user.setPassword(req.body.password);
    })
    .then(function(user) {
      req.session.user = user;
      const newToken = new SessionToken({userId: user.userId});
      console.log(newToken);
      return SessionToken.create({userId: user.userId});
    })
    .then(function(token) {
      return res.status(201).json(token);
    })
    .catch(function(err) {
      console.error(err);
      return res.sendStatus(500);
    });
}
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:50
you have to put it on a new line and wrap the entire thing in ```
so on top and on bottom
Eddie Bracho
@ebracho
Dec 04 2016 22:50
pressing enter submits it though haha
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:50
it shouldnt
if you do ``` it won't auto enter
youll have to cmd + enter on a mac, not sure about pc
you can also do shift+enter
Eddie Bracho
@ebracho
Dec 04 2016 22:51
shift+enter did it, thanks
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:51
anyway yeah, that code looks better
try it and see
Eddie Bracho
@ebracho
Dec 04 2016 22:53
That worked
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:53
cool yeah
Eddie Bracho
@ebracho
Dec 04 2016 22:53
Its doing an insert instead of an update now
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:54
never create a mongoose document in one collection by just passing it the mongoose object from anoterh collection
if anything, do user.toObject() but even then, better to just be clear about what properties you want to set
Eddie Bracho
@ebracho
Dec 04 2016 22:54
Ohhh because it was passing the _id maybe?
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:54
yeah
mongoose objects also have a bunch of properties on them that can be set by their respective schemas
Eddie Bracho
@ebracho
Dec 04 2016 22:55
the mystery is solved
I see
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:55
including a buuuuunch of getters and setters
Eddie Bracho
@ebracho
Dec 04 2016 22:56
Thats my one gripe about js, theres not really any protection or control
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:56
what do you mean
Eddie Bracho
@ebracho
Dec 04 2016 22:56
Like things that shouldn't be used by the user shouldn't be visible.
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:57
there's ways to implement private properties
Eddie Bracho
@ebracho
Dec 04 2016 22:57
Builtin ways?
Varun Jayaraman
@varunjayaraman
Dec 04 2016 22:57
no
well sort of
the builtin ways are hacks
if you're on node and using a recent version and have access to WeakMaps
then yes built ins
but mongoose is built to be backwards compatible bc its so widely used, and i don't think mongoose internally uses WeakMaps
Eddie Bracho
@ebracho
Dec 04 2016 22:58
Javacript has come a long way I think
its definitely interesting to use
but yeah, thank you one last time for working through this with me. I learned a bunch of better practices from this
Varun Jayaraman
@varunjayaraman
Dec 04 2016 23:00
yep
Fernando Vega
@vegafx12
Dec 04 2016 23:20
so.. I wonder, how common is it for someone to use multiple database connections with authentication enabled in a JS app? For example, say I have two users setup on a db. One 'admin' user with read/write privileges and one 'webUser' with just read privileges. Say I want to build a registration form for user to create a new account. When user submits this form, the model will use the 'admin' connection so it can write to the database. In another example, say the user is logged in and clicks on the "News & Events" section. These items are stored in database and will be retrieved by the app, but instead will use the 'webUser' login when connecting to the database to ensure that the app will only be able to read the data.
Do people do this? I've having trouble finding any documentation/examples of this scenario...
Is my logic fundamentally flawed...?!
Eddie Bracho
@ebracho
Dec 04 2016 23:38
@vegafx12 In my limited experience user permissions are usually handled within the application
This isn't javascript, but django comes with a permissions/authorization module that gives you control over what a user is able to do https://docs.djangoproject.com/en/1.10/topics/auth/default/#topic-authorization
Fernando Vega
@vegafx12
Dec 04 2016 23:43
@ebracho thanks for the info! However, that seems odd to me... Application should NOT be telling the database "who" can do "what", application to should be sending request to database and database should give the application the "yes" or "no" on whether the application is allowed to proceed with that transaction.
Yeah?
Eddie Bracho
@ebracho
Dec 04 2016 23:44
I've never come across a model like that
Usually the database just assumes that whatever queries are coming in are meant to be executed
Because you don't have users accessing the database directly
only your application
Fernando Vega
@vegafx12
Dec 04 2016 23:45
well yeah, but the application must first authenticate
Eddie Bracho
@ebracho
Dec 04 2016 23:46
What do you mean by authenticate
Fernando Vega
@vegafx12
Dec 04 2016 23:47
You're right lol I think we're saying the same thing, I'm just "thinking" about it in a different way, but yeah... Application will connect to the database as user A or user B, but it's the database itself that is configured to say what permissions user A has vs. what user B has
Eddie Bracho
@ebracho
Dec 04 2016 23:48
I'm sure it depends on the context, but usually its: user -> app -> db
The application provides an interface to the user, not direct access to the db
If there are parts of the interface that the user should be restricted from, the application should manage that restriction
Fernando Vega
@vegafx12
Dec 04 2016 23:49
So, in my example above if application connects to database as 'webUser' (which only has read permissions), then tries to perform a query that will attempt to create a new document or update a value, the database will say, "no permissions to perform write action" or something along those lies
but, if application connects as 'admin' with read/write permissions, then the database will allow the application to perform those ttransactions
Eddie Bracho
@ebracho
Dec 04 2016 23:50
I personally wouldn't use multiple database accounts for a single app
unless the context reeally called for it
but I can't imagine a situation like that
In both scenarios, your application is managing what the user can and can't do (either by restricting the application interface or changing database connections)
The former scenario keeps logic within the application
Fernando Vega
@vegafx12
Dec 04 2016 23:53
fair enough, I'm not "experienced" at any level with this stuff yet, but trying my best to learn! How would you go about doing it then...? I guess my goal is to create an additional layer of security at the database level.
Eddie Bracho
@ebracho
Dec 04 2016 23:53
Of course, this was an interesting idea
Can you give me more context to your situation?
Fernando Vega
@vegafx12
Dec 04 2016 23:54
So, if for some reason my application were to get compromised in one portion, the culprit may be able to hurt my app/web page, but still not be able to get into database
sure thing

Well let's just use most examples I see in documentation and other examples online... everyone just says to connect using:

```

Eddie Bracho
@ebracho
Dec 04 2016 23:56
use shift+enter :)
Fernando Vega
@vegafx12
Dec 04 2016 23:59
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var Cat = mongoose.model('Cat', { name: String });

var kitty = new Cat({ name: 'Zildjian' });
kitty.save(function (err) {
  if (err) {
    console.log(err);
  } else {
    console.log('meow');
  }
});
this is from mongoose home page