Пример #1
0
      sails.router.bind('/*', function(req, res, next) {

        // If global CSRF is disabled check the whitelist.
        if (sails.config.security.csrf === false) {
          // If nothing in the whitelist matches, continue on without checking for a CSRF token.
          if (!_.any(whitelist, function(whitelistedRoute) {
            return req.path.match(whitelistedRoute.regex) && (!whitelistedRoute.method || whitelistedRoute.method === req.method.toLowerCase());
          })) {
            return next();
          }
        }
        // Otherwise check the blacklist
        else {
          // If anything in the blacklist matches, continue on without checking for a CSRF token.
          if (_.any(blacklist, function(blacklistedRoute) {
            return req.path.match(blacklistedRoute.regex) && (!blacklistedRoute.method || blacklistedRoute.method === req.method.toLowerCase());
          })) {
            return next();
          }
        }

        // Handle session being disabled.
        if (!req.session) {
          // For GET, HEAD and OPTIONS requests, continue on.  These aren't covered by CSRF anyway.
          if (_.contains(['get', 'head', 'options'], req.method.toLowerCase())) {
            return next();
          }
          // In development mode, give a more explicit account of what's happening.
          if (process.env.NODE_ENV === 'development') {
            return next(new Error('Route `' + req.method + ' ' + req.path + '` has CSRF enabled, but the session is disabled!'));
          }
          // In production, just return the same CSRF mismatch error you'd get with a bad/missing token in the request.
          else {
            return res.forbidden('CSRF mismatch');
          }
        }

        return csrfMiddleware(req, res, function(err) {
          if (err) {
            // Only attempt to handle invalid csrf tokens
            if (err.code !== 'EBADCSRFTOKEN') {
              return next(err);
            }
            return res.forbidden('CSRF mismatch');
          }
          // If this is not a socket request, provide the CSRF token in res.locals,
          // so it can be bootstrapped into a view.  For purposes of CSRF, we're
          // treating sockets as inherently insecure (note that we disable
          // the grant-csrf-token action for sockets as well).  You can certainly
          // _spend_ CSRF tokens over sockets, you just can't retrieve them.
          if (!req.isSocket) {
            res.locals._csrf = req.csrfToken();
          }
          next();
        });

      }, null, {_middlewareType: 'CSRF HOOK: CSRF'});
Пример #2
0
          _.each(err.footprint.keys, function(key){

            // Find matching attr name.
            var matchingAttrName;
            _.any(WLModel.schema, function(wlsAttr, attrName) {

              var attrDef = WLModel.attributes[attrName];
              assert(attrDef, 'Attribute (`'+attrName+'`) is corrupted!  This attribute exists as a WLS attr in `schema`, so it should always exist in `attributes` as well-- but it does not!  If you are seeing this message, it probably means your model (`'+modelIdentity+'`) has become corrupted.');

              // If this is a plural association, then skip it.
              // (it is impossible for a key from this error to match up with one of these-- they don't even have column names)
              if (attrDef.collection) { return; }

              // Otherwise, we can expect a valid column name to exist.
              assert(wlsAttr.columnName, 'The normalized `schema` of model `'+modelIdentity+'` has an attribute (`'+attrName+'`) with no `columnName`.  But at this point, every WLS-normalized attribute should have a column name!  (If you are seeing this error, the model definition may have been corrupted in-memory-- or there might be a bug in WL schema.)');

              if (wlsAttr.columnName === key) {
                matchingAttrName = attrName;
                return true;
              }
            });//</_.any>

            // Push it on, if it could be found.
            if (matchingAttrName) {
              namesOfOffendingAttrs.push(matchingAttrName);
            }
            // Otherwise track this as an unmatched key.
            else {
              unmatchedKeys.push(key);
            }

          });//</_.each()>
Пример #3
0
      _.each(archiversInfoByArchiveIdentity, function(archiversInfo, archiveIdentity) {
        var archiveWmd = _.find(wmds, function(wmd){ return wmd.prototype.identity === archiveIdentity; });
        if (!archiveWmd) {
          throw new Error('Invalid `archiveModelIdentity` setting.  A model declares `archiveModelIdentity: \''+archiveIdentity+'\'`, but there\'s no other model actually registered with that identity to use as an archive!');
        }

        // Validate that this archive model can be used for the purpose of Waterline's .archive()
        // > (note that the error messages here should be considerate of the case where someone is
        // > upgrading their app from an older version of Sails/Waterline and might happen to have
        // > a model named "Archive".)
        var EXPECTED_ATTR_NAMES = ['id', 'createdAt', 'fromModel', 'originalRecord', 'originalRecordId'];
        var actualAttrNames = _.keys(archiveWmd.prototype.attributes);
        var namesOfMissingAttrs = _.difference(EXPECTED_ATTR_NAMES, actualAttrNames);

        try {

          if (namesOfMissingAttrs.length > 0) {
            throw flaverr({
              code: 'E_INVALID_ARCHIVE_MODEL',
              because: 'it is missing '+ namesOfMissingAttrs.length+' mandatory attribute'+(namesOfMissingAttrs.length===1?'':'s')+': '+namesOfMissingAttrs+'.'
            });
          }//•

          if (archiveWmd.prototype.primaryKey !== 'id') {
            throw flaverr({
              code: 'E_INVALID_ARCHIVE_MODEL',
              because: 'it is using an attribute other than `id` as its logical primary key attribute.'
            });
          }//•

          if (_.any(EXPECTED_ATTR_NAMES, { encrypt: true })) {
            throw flaverr({
              code: 'E_INVALID_ARCHIVE_MODEL',
              because: 'it is using at-rest encryption on one of its mandatory attributes, when it shouldn\'t be.'
            });
          }//•

          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
          // FUTURE: do more checks (there's a lot of things we should probably check-- e.g. the `type` of each
          // mandatory attribute, that no crazy defaultsTo is provided, that the auto-timestamp is correct, etc.)
          // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

        } catch (err) {
          switch (err.code) {
            case 'E_INVALID_ARCHIVE_MODEL':
              throw new Error(
                'The `'+archiveIdentity+'` model cannot be used as a custom archive, because '+err.because+'\n'+
                'Please adjust this custom archive model accordingly, or otherwise switch to a different '+
                'model as your custom archive.  (For reference, this `'+archiveIdentity+'` model this is currently '+
                'configured as the custom archive model for '+archiversInfo.archivers.length+' other '+
                'model'+(archiversInfo.archivers.length===1?'':'s')+': '+_.pluck(archiversInfo.archivers, 'identity')+'.'
              );
            default:
              throw err;
          }
        }

      });//∞
Пример #4
0
      }, function(err, _sailsApp) {

        if (err) { return done(err); }
        sailsApp = _sailsApp;

        // Assert that NODE_ENV is changed.
        assert.equal(process.env.NODE_ENV, 'production');

        // Assert that sails config is unchanged.
        assert.equal(sailsApp.config.environment, 'production');

        assert (_.any(debugs, function(debug) {
          return debug.indexOf('Detected Sails environment is "production", but NODE_ENV is `undefined`.') > -1;
        }), 'Did not log a warning about NODE_ENV being undefined while sails environment is `production`!');
        assert (_.any(debugs, function(debug) {
          return debug.indexOf('Automatically setting the NODE_ENV environment variable to "production".') > -1;
        }), 'Did not log a warning about NODE_ENV being set to `production`!');

        return done();

      });//</app0.load()>
        _.each(actions, function(action, key) {
          // If this is a blueprint action, only skip it.
          // It'll be handled in the "shortcut routes" section,
          // if those routes are enabled.
          if (action._middlewareType && action._middlewareType.indexOf('BLUEPRINT') === 0) {
            return;
          }
          // If this action belongs to a controller with blueprint action routes turned off, skip it.
          if (_.any(config._controllers, function(config, controllerIdentity) {
            return config.actions === false && key.indexOf(controllerIdentity) === 0;
          })) {
            return;
          }

          // Add the route prefix (if any) and bind the route to that URL.
          var url = config.prefix + '/' + key;
          sails.router.bind(url, key);
        });
        _.each(actions, function(action, key) {
          // Does the key end in `/index` (or is it === `index`)?
          if (key === 'index' || key.match(/\/index$/)) {

            // If this action belongs to a controller with blueprint action routes turned off, skip it.
            if (_.any(config._controllers, function(config, controllerIdentity) {
              return config.actions === false && key.indexOf(controllerIdentity) === 0;
            })) {
              return;
            }

            // Strip the `.index` off the end.
            var index = key.replace(/\/?index$/,'');
            // Replace any remaining dots with slashes.
            var url = '/' + index;
            // Bind the url to the action.
            sails.router.bind(url, key);
          }
        });
Пример #7
0
      _.each(helperDefs, function(helperDef, identity) {
        try {
          // Camel-case every part of the file path, and join with dots
          // e.g. /user-helpers/foo/my-helper => userHelpers.foo.myHelper
          var keyPath = _.map(identity.split('/'), _.camelCase).join('.');

          // Save _loadedFrom property for debugging purposes.
          // (e.g. `financial/calculate-mortgage-series`)
          helperDef._loadedFrom = identity;

          // Save _fromLocalSailsApp for internal use.
          helperDef._fromLocalSailsApp = true;

          // Use filename-derived `identity` REGARDLESS if an explicit identity
          // was set.  (And exclude any extra hierarchy.)  Otherwise, as of
          // machine@v15, this could fail with an ImplementationError.
          helperDef.identity = identity.match(/\//) ? _.last(identity.split('/')) : identity;

          // Check helper def to make sure it doesn't include any obvious signs
          // of confusion with actions -- e.g. no "responseType".  If anything
          // like that is detected, log a warning.
          if (helperDef.files) {
            sails.log.warn(
              'Ignoring unexpected `files` property in helper definition loaded '+
              'from '+helperDef._loadedFrom+'.  This feature can only be used '+
              'by actions, not by helpers!'
            );
          }
          var hasAnyConfusingExitProps = (
            _.isObject(helperDef.exits) &&
            _.any(helperDef.exits, function(exitDef){
              return (
                _.isObject(exitDef) &&
                (
                  exitDef.responseType !== undefined ||
                  exitDef.viewTemplatePath !== undefined ||
                  exitDef.statusCode !== undefined
                )
              );
            })
          );
          if (hasAnyConfusingExitProps) {
            sails.log.warn(
              'Ignoring unexpected property in one of the exits of the helper '+
              'definition loaded from '+helperDef._loadedFrom+'.  Features like '+
              '`responseType`, `viewTemplatePath`, and `statusCode` can only be '+
              'used by actions, not by helpers!'
            );
          }

          // Build & expose helper on `sails.helpers`
          // > e.g. sails.helpers.userHelpers.foo.myHelper
          sails.hooks.helpers.furnishHelper(keyPath, helperDef);
        } catch (err) {
          // If an error occurs building the callable, throw here to bust
          // out of the _.each loop early
          throw flaverr({
            code: 'E_FAILED_TO_BUILD_CALLABLE',
            identity: helperDef.identity,
            loadedFrom: identity,
            raw: err
          }, err);
        }
      });//∞
        _.each(sails.models, function(Model, identity) {

          // If this there is a matching controller with blueprint shortcut routes turned off, skip it.
          if (_.any(config._controllers, function(config, controllerIdentity) {
            return config.rest === false && identity === controllerIdentity;
          })) {
            return;
          }

          // Determine the base REST route for the model.
          var baseRestRoute = (function() {
            // Start with the model identity.
            var baseRouteName = identity;
            // Pluralize it if plurization option is on.
            if (config.pluralize) {
              baseRouteName = pluralize(baseRouteName);
            }
            // Add the route prefix, RESTful route prefix and base route name together.
            return config.prefix + config.restPrefix + '/' + baseRouteName;
          })();

          _bindRestRoute('get %s', 'find');
          _bindRestRoute('get %s/:id', 'findOne');
          _bindRestRoute('post %s', 'create');
          _bindRestRoute('patch %s/:id', 'update');
          _bindRestRoute('delete %s/:id?', 'destroy');

          // Bind the `put :model/:id` route to the update action, first bind a route that
          // logs a warning about using `PUT` instead of `PATCH`.
          // Some route options are set as well, including a deep clone of the model associations.
          // The clone prevents the blueprint action from accidentally altering the model definition in any way.
          sails.router.bind(
            util.format('put %s/:id', baseRestRoute),
            function (req, res, next) {
              sails.log.debug('Using `PUT` to update a record is deprecated in Sails 1.0.  Use `PATCH` instead!');
              return next();
            }
          );
          _bindRestRoute('put %s/:id', 'update');

          // Bind "rest" blueprint/shadow routes based on known associations in our model's schema
          // Bind add/remove for each `collection` associations
          _.each(_.where(Model.associations, {type: 'collection'}), function (association) {
            var alias = association.alias;
            _bindAssocRoute('put %s/:parentid/%s/:childid', 'add', alias);
            _bindAssocRoute('put %s/:parentid/%s', 'replace', alias);
            _bindAssocRoute('delete %s/:parentid/%s/:childid', 'remove', alias);

          });

          // and populate for both `collection` and `model` associations
          _.each(Model.associations, function (association) {
            var alias = association.alias;
            _bindAssocRoute('get %s/:parentid/%s', 'populate', alias );
          });

          function _bindRestRoute(template, blueprintActionName) {
            // Get the URL for the RESTful route
            var restRoute = util.format(template, baseRestRoute);
            // Bind it to the appropriate action, adding in some route options including a deep clone of the model associations.
            // The clone prevents the blueprint action from accidentally altering the model definition in any way.
            sails.router.bind(restRoute, identity + '/' + blueprintActionName, null, { model: identity, associations: _.cloneDeep(Model.associations), autoWatch: sails.config.blueprints.autoWatch  });
          }

          function _bindAssocRoute(template, blueprintActionName, alias) {
            // Get the URL for the RESTful route
            var assocRoute = util.format(template, baseRestRoute, alias);
            // Bind it to the appropriate action, adding in some route options including a deep clone of the model associations.
            // The clone prevents the blueprint action from accidentally altering the model definition in any way.
            sails.router.bind(assocRoute, identity + '/' + blueprintActionName, null, { model: identity, alias: alias, associations: _.cloneDeep(Model.associations), autoWatch: sails.config.blueprints.autoWatch  });
          }

        });
        _.each(sails.models, function(Model, identity) {

          // If this there is a matching controller with blueprint shortcut routes turned off, skip it.
          if (_.any(config._controllers, function(config, controllerIdentity) {
            return config.shortcuts === false && identity === controllerIdentity;
          })) {
            return;
          }

          // Determine the base route for the model.
          var baseShortcutRoute = (function() {
            // Start with the model identity.
            var baseRouteName = identity;
            // Pluralize it if plurization option is on.
            if (config.pluralize) {
              baseRouteName = pluralize(baseRouteName);
            }
            // Add the route prefix and base route name together.
            return config.prefix + '/' + baseRouteName;
          })();

          _bindShortcutRoute('get %s/find', 'find');
          _bindShortcutRoute('get %s/find/:id', 'findOne');
          _bindShortcutRoute('get %s/create', 'create');
          _bindShortcutRoute('get %s/update/:id', 'update');
          _bindShortcutRoute('get %s/destroy/:id', 'destroy');

          // Bind "rest" blueprint/shadow routes based on known associations in our model's schema
          // Bind add/remove for each `collection` associations
          _.each(_.where(Model.associations, {type: 'collection'}), function (association) {
            var alias = association.alias;
            _bindAssocRoute('get %s/:parentid/%s/add/:childid', 'add', alias);
            _bindAssocRoute('get %s/:parentid/%s/replace', 'replace', alias);
            _bindAssocRoute('get %s/:parentid/%s/remove/:childid', 'remove', alias);
          });

          // and populate for both `collection` and `model` associations,
          // if we didn't already do it above for RESTful routes
          if ( !config.rest ) {
            _.each(Model.associations, function (association) {
              var alias = association.alias;
              _bindAssocRoute('get %s/:parentid/%s', 'populate', alias );
              //_bindAssocRoute('get %s/:parentid/%s/:childid', 'populate', alias );
            });
          }

          function _bindShortcutRoute(template, blueprintActionName) {
            // Get the route URL for this shortcut
            var shortcutRoute = util.format(template, baseShortcutRoute);
            // Bind it to the appropriate action, adding in some route options including a deep clone of the model associations.
            // The clone prevents the blueprint action from accidentally altering the model definition in any way.
            sails.router.bind(shortcutRoute, identity + '/' + blueprintActionName, null, { model: identity, associations: _.cloneDeep(Model.associations), autoWatch: sails.config.blueprints.autoWatch });
          }

          function _bindAssocRoute(template, blueprintActionName, alias) {
            // Get the route URL for this shortcut
            var assocRoute = util.format(template, baseShortcutRoute, alias);
            // Bind it to the appropriate action, adding in some route options including a deep clone of the model associations.
            // The clone prevents the blueprint action from accidentally altering the model definition in any way.
            sails.router.bind(assocRoute, identity + '/' + blueprintActionName, null, { model: identity, alias: alias, associations: _.cloneDeep(Model.associations), autoWatch: sails.config.blueprints.autoWatch  });
          }

        });
Пример #10
0
var _ = require('@sailshq/lodash');
var chalk = require('chalk');
var portfinder = require('portfinder');
portfinder.basePort = 2001;

var SHOW_VERBOSE_BENCHMARK_REPORT = _.any(process.argv, function(arg) {
  return arg.match(/-v/);
});

if (process.env.BENCHMARK) {

  describe('benchmarks', function() {

    describe('sails.load()', function() {
      before(setupBenchmarks);
      after(reportBenchmarks);


      //
      // Instantiate
      //

      benchmark('require("sails")', function(cb) {
        var Sails = require('../../lib/app');
        var sails = new Sails();
        return cb();
      });


      //
      // Load