0

I have a question about best practice and how to add user authorization functionality. Should it be in model, controller or elsewhere.

Currently,

I have been building validation functions within my Mongoose Models

I have been building authentication/authorization checks using middleware and called from my routes.

My current challenge is when an an authenticated and authorized user attempts to update a model for which they are NOT the owner.

My authenticated user has been attached to my request, but that data is not going to be available from within the Mongoose Model so I am thinking that I should probably create some sort of validation function on the model that can be called from my controller, so that my logic lives nicely with the model but can be called from the controller.

enter image description here

Controller

exports.create = function (req, res) {

  try {

    if (!_.isEmpty(req.body.entity.ordererAccountId) && !_.isEqual(req.user.accountId.toString(), req.body.entity.ordererAccountId)) {
      var err = mong.formatError({ message: 'Invalid Account Access' });

      return res.status(403).json(err);
    }

    OrderSchema.create(req.body.entity, function (err, entity) {

      if (err) {
        return mong.handleError(res, err);
      }

      return res.status(201).json(mong.formatSuccess(entity));
    });

  } catch (e) {
    console.log(e);
  }
};

Model

'use strict';

// ------------------------------------------------------------
// Order Model
// ------------------------------------------------------------

var mongoose = require('mongoose');
var findOneOrCreate = require('mongoose-find-one-or-create');

var Schema = mongoose.Schema;

var OrderSchema = new Schema({
  created_at: { type: Date },
  updated_at: { type: Date },

  ordererAccountId:
    {
      type: Schema.ObjectId, ref: 'Account',
      required: true
    },
  supplierAccountId:
    {
      type: Schema.ObjectId, ref: 'Account'
    },
  userId:
    {
      type: Schema.ObjectId, ref: 'User',
      required: true
    },
  status: {
    type: String,
    enum: ['Open', 'Sent'],
    default: 'Open'
  },
  notes: String,
  supplierCompanyName: String,
  supplierEmail: String,
  supplierContactName: String,
  supplierPhone1: String,
  supplierPhone2: String,
  deliveryCompanyName: String,
  deliveryEmail: String,
  deliveryFirstName: String,
  deliveryLastName: String,
  deliveryAddress1: String,
  deliveryAddress2: String,
  deliveryCity: String,
  deliveryState: String,
  deliveryPostCode: String,
  deliveryCountry: String,
  deliveryPhone1: String,
  deliveryPhone2: String,
});

OrderSchema.plugin(findOneOrCreate);

// ------------------------------------------------------------
// Validations
// ------------------------------------------------------------

// Validate only one open order at a time per user
OrderSchema
  .path('status')
  .validate(function (status, respond) {

    var Order = mongoose.model('Order');

    // Excluding this Order, make sure there are NO other orders for this user with the status of 'Open'
    var condition = {
      userId: this.userId,
      status: 'Open',
      _id: { $ne: this._id }
    };

    Order.count(condition, function (err, count) {

      if (err) {
        console.log(err);
      }
      else {
        respond(count === 0);
      }

    });

  }, 'There can be only one open order at a time.');

// ------------------------------------------------------------
// Pre-Save Hook
// ------------------------------------------------------------

OrderSchema.pre('save', function (next) {
  var now = new Date().getTime();

  this.updated_at = now;
  if (!this.created_at) {
    this.created_at = now;
  }

  next();
});

module.exports = mongoose.model('Order', OrderSchema);
David Cruwys
  • 6,262
  • 12
  • 45
  • 91

1 Answers1

0

you can use your "create" function as a validation middleware in your router,by doing something like this:

      app.post('/yourRoute', create, function(req, res) {
            //if validation success
            //do somthing
        });

not forgetting to pass the "next" function as a third argument to your create function

aitnasser
  • 1,216
  • 1
  • 9
  • 23