permissionMiddlewareCreator throws an error: "Cannot read properties of undefined (reading 'renderingId')"

Feature(s) impacted

I am trying to extend the route based on this docs

Observed behavior

When I try to add a forest route for creating new record, I need to use:

router.post('/forest/workshops', permissionMiddlewareCreator.create()

instead of

router.post('/workshops', permissionMiddlewareCreator.create() in /routes/workshops.

The problem is that permissionMiddlewareCreator.create() throws an error “Cannot read properties of undefined (reading ‘renderingId’)”. If I remove permissionMiddlewareCreator.create() middlware, the route is triggered.

Expected behavior

permissionMiddlewareCreator shouldn’t throw an error.

Failure Logs

package.json

{
  "name": "",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js",
    "dev": "nodemon index.js",
    "debug": "nodemon --inspect index.js",
    "lint": "npx eslint './**'",
    "format": "prettier --write \"src/**/*.js\""
  },
  "repository": {
    "type": "git",
    "url": ""
  },
  "author": "",
  "license": "ISC",
  "homepage": "",
  "dependencies": {
    "@mailchimp/mailchimp_transactional": "^1.0.47",
    "@slack/web-api": "^6.7.1",
    "aws-sdk": "^2.1102.0",
    "axios": "^0.26.1",
    "bcrypt": "^5.0.1",
    "body-parser": "^1.19.2",
    "cookie-parser": "^1.4.6",
    "cors": "^2.8.5",
    "docusign-esign": "^5.17.0",
    "dotenv": "^16.0.0",
    "express": "^4.17.3",
    "forest-express-mongoose": "^8.6.5",
    "fs": "0.0.1-security",
    "googleapis": "^100.0.0",
    "jsonwebtoken": "^8.5.1",
    "moment": "^2.29.2",
    "mongoose": "^6.2.8",
    "multer": "^1.4.4",
    "multer-s3": "^2.10.0",
    "path": "^0.12.7",
    "prompt-sync": "^4.2.0",
    "stripe": "^8.210.0",
    "uuid": "^8.3.2"
  },
  "devDependencies": {
    "eslint": "8.17.0",
    "eslint-config-prettier": "8.5.0",
    "eslint-plugin-import": "2.26.0",
    "eslint-plugin-prettier": "4.0.0",
    "nodemon": "^2.0.15",
    "prettier": "2.6.2"
  }
}

index.js

require('dotenv').config();
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const express = require('express');
const app = express();
const forest = require('forest-express-mongoose');
const Mongoose = require('mongoose');

const mongoose = require('./config/mongoose');
const routes = require('./routes');

const PORT = process.env.PORT || 3000;

const conn = mongoose.connect();

forest
  .init({
    envSecret: process.env.FOREST_ENV_SECRET,
    authSecret: process.env.FOREST_AUTH_SECRET,
    objectMapping: Mongoose,
    connections: { default: conn }
  })
  .then((FAMiddleware) => {
    app.use(FAMiddleware);
  });

app.use(cookieParser());
app.listen(PORT, () => console.log(`Server listening in port ${PORT}`));
app.get('/', (req, res) => res.send('Hello World'));
app.use(bodyParser.json({ limit: '20mb' }));
app.use(bodyParser.urlencoded({ limit: '50mb', extended: true }));
app.use('/', routes);

/routes/index.js

const cors = require('cors');
const express = require('express');
const { PermissionMiddlewareCreator, RecordsGetter } = require('forest-express-mongoose');

const auth = require('../middleware/auth');
const { workshops } = require('../model/workshops_model');

const permissionMiddlewareCreator = new PermissionMiddlewareCreator('workshops');

const subscription = require('./subscriptions_routes');

const allowed_origins = process.env.ALLOWED_CORS.split(',');

const corsOptions = {
  origin: allowed_origins,
  credentials: true
};

const router = express.Router();

router.use('/subscriptions', cors(corsOptions), subscription);

router.post('/forest/workshops', permissionMiddlewareCreator.create(), (request, response, next) => {
  console.log('run workshops api');
  const { query, user } = request;
  console.log({ user });
  next();
});

module.exports = router;

/model/workshops_model.js

const mongoose = require('mongoose');

const enums = require('../utils/enums');

const workshops = mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  description: {
    type: String,
    required: true
  },
  location: {
    type: String,
    required: true,
    enum: enums.locationEnums
  },
  members: {
    type: [mongoose.Schema.Types.ObjectId],
    ref: 'members'
  },
  sector: {
    type: [String],
    enum: enums.sectorEnums
  },
  event: {
    type: [mongoose.Schema.Types.ObjectId],
    ref: 'event'
  },
  slack_channel: {
    type: String
  },
  chair: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'members'
  },
  co_chair: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'members'
  }
});

exports.workshops = mongoose.model('workshops', workshops);

I fixed this by adding:

app.use(
  '/forest/authentication',
  cors({
    ...corsConfig,
    // The null origin is sent by browsers for redirected AJAX calls
    // we need to support this in authentication routes because OIDC
    // redirects to the callback route
    origin: corsConfig.origin.concat('null')
  })
);
app.use(
  jwt({
    secret: process.env.FOREST_AUTH_SECRET,
    credentialsRequired: false,
    algorithms: ['HS256']
  })
);

but it would be nice to add this information to documentation.

1 Like

Thanks for this precious feedback @and_g!