How to properly specify options for select drop down with autoform?

Using the following section of SimpleSchema with an Autoform, I have the following question:

Do I need to specify both allowedValues AND the autoform options? I think I found this example somewhere… but it seems like overkill to use both as the numbers 11,13, and 14 are listed twice. Do you need to do it this way to achieve proper validation, or could I drop the “allowedValues” and just use the autoform “Options”

Thanks!

, 'demographics.chartStatus': {
  type: String,
  label: "Chart Status"
    , allowedValues: [
      11,
      13,
      14
    ]
  ,
  autoform: {
      options: [
        {
          label: "Pending",
          value: 11
        },
        {
          label: "Complete",
          value: 13
        },
        {
          label: "On Hold",
          value: 14
        }
      ]
  }}

Edit: yes, I found this example here:

What I would do:

GlobalAppContainer = {}; //refered to GAC from now on
GAC.ChartStatus = [
{
          label: "Pending",
          value: 11
        },
        {
          label: "Complete",
          value: 13
        },
        {
          label: "On Hold",
          value: 14
        }
]

Then your code will look like:

label: "Chart Status"
    , allowedValues: _.map(GAC.ChartStatus, function (x) { return x.value; })
  ,
  autoform: {
      options: GAC.ChartStatus
  }}

Yes, I was thinking along those lines as well, however, this still seems like extra work. isn’t this probably something autoform could do internally? ie, take the values from the options and use them for the allowedvalues… ??

You could argue it either way imo.

Neither of us know what would happen if you removed allowedValues… have you tried that as a first test?

But as of right now, I see your options are:

  1. Workaround it (w/o accepting it as expected behaviour) - 1 min
  2. Submit a PR with a well formed argument and work on a fork - 1-4 hrs
  3. Create a issue on github and if aldeed thinks it makes sense, he’ll do it in his time. But this would mean breaking all existing forms by overriding any setting of allowedValues with options. - Indefinite

Before posting, I had tried removing allowedValues, and things still worked, but now I just ran a more extensive test:

Without AllowedValues, you still get the values in the drop down, but, if you hack the HTML page to pass a different value for one of the select options, it will go into the database without a validation error in the form… this was what I was afraid of… the autoForm Options just takes care of showing the correct values and labels in the drop down, but is not responsible for the validation.

I’ll post a note on the github repo about this… I think the best solution would be for autoform to only take the values from the options if allowedValues is unset… ie, your option 3 but with a twist so it doesn’t break all existing forms that are setting allowedValues directly.

Thanks for the feeback!

Here’s what I ended up doing, based on @mordrax suggestion:

The ChartDictionary collection just contains misc. values used for validation, so I do a query into it for a certain “lookup_type”, sorting by the lookup_label, and for the allowedValues, I map the results into an array of just the ID numbers, and for the autoform options, I map the results into an array of labels and values.

, 'demographics.chartStatus': {
    type: String,
    label: "Chart Status"
    , allowedValues: function () {
        return ChartDictionary.find({"lookup_type": "chartStatus"}, {sort: {"lookup_label": 1}}).map(function (c) {
          return c.lookup_id;
        });
      }
    ,
    autoform: {
      options: function () {
        return ChartDictionary.find({"lookup_type": "chartStatus"}, {sort: {"lookup_label": 1}}).map(function (c) {
          return {label: c.lookup_label, value: c.lookup_id};
        });
      }
    }
  }

Or better yet, pull into 2 functions:

getAllowedValuesForAutoform = function (lookupType) {
  return ChartDictionary.find({"lookup_type": lookupType}, {sort: {"lookup_label": 1}}).map(function (c) {
    return c.lookup_id;
  });
};

getOptionsForAutoform = function (lookupType) {
  return ChartDictionary.find({"lookup_type": lookupType}, {sort: {"lookup_label": 1}}).map(function (c) {
    return {label: c.lookup_label, value: c.lookup_id};
  });
};

then:

 'demographics.chartStatus': {
    type: String,
    label: "Chart Status"
    , allowedValues: getAllowedValuesForAutoform('chartStatus')
    ,
    autoform: {
      
      options: getOptionsForAutoform('chartStatus')
    }
  }

AutoForm is a purely client side library, meaning even if you set the allowedValues properly, all that it does is make sure the values are accurate before updating minimongo, however, anyone can still bypass allowedValues by editing minimongo in their browser and that will still get synced to mongo.

This is why you always need to have server side validation no matter what safeguards, allowedValues, client UIs you might disable because they only interact with minimongo which the client has full control over. And this is also why I don’t particularly find allowedValues useful because to me, it’s just guiding the 99% of normal users which options already does.

I think of it as more of a regEx for lists/enums rather than a security feature.

1 Like

My last reply got me thinking maybe allowedValues is ambiguously named and why it exists if it’s superseeded by options.
Reading the doco, it sounds like you use allowedValues as a shorthand if your label and value are the same and if you specify options (for when your label differs), then it overrides allowed values which confirms your tested behaviour.

In summary, you don’t need to specify allowedValues and it’s probably simpler to model your ChartDictionary directly in the format that options expects.

I’m getting lost in the client/server issue here… I am doing my:

Chart = new Meteor.Collection('chart');

and

Chart.attachSchema(new SimpleSchema({
 'fields' ... etc

in a javascript file that is shared between client and server… but you say the validation is only happening on the client.

Are there any examples of using autoform, with SimpleSchema for validation, and getting “server side validation” from that setup?

How exactly would one use the SimpleSchema to validate on the server? I didn’t see any examples with Autoform…

That’s a good question and unfortunately I’m not up to there yet but did a bit of reading (the part about Now that our collection has a schema... and realised that a collection with an attached schema which calls insert/update on either minimongo or mongo are validated.

I realised that I already tested this yesterday when I couldn’t insert a doc using webstorm shell because I hadn’t set a required field. Note: If you go into mongo shell, you can still insert any document you want because it doesn’t conform to the schema you’ve attached in your meteor code.

Therefore I’d like to amend what I said about server side validation to server side security.

In minimongo, you can open up chrome debug and start modifying collection data as per my last post. This will then sync with mongo (beauty of meteor). If you have a schema, the insert/update will be validated against it, and if it passes, you need security rules to block unauthorized access. I plan to use hooks and better (imo) deny rules to secure server insert/updates. It’ll be great if you could update with how you’re planning to do this so I can learn off you! :slight_smile:

Well, for starters, I have autopublish and insecure removed, so something like Chart.insert from the browser console should fail. … now, I am providing a Meteor.method for something like “addChart”, and I’m trying to call this through the browser console, to see how it works. I can’t get the syntax right though…

if this is my method:

Meteor.methods({
  addChart: function (chart) {
    var rc = Chart.insert(chart);
    return rc;
  },

Then I’m having trouble calling it from Chrome:

Meteor.call('addChart', 'bla')

gives:

Exception while invoking method 'addChart' TypeError: Cannot use 'in' operator to search for '$pushAll' in bla

on the server,

and:

Exception while simulating the effect of invoking 'addChart' TypeError: Cannot use 'in' operator to search for '$pushAll' in hi
    at SimpleSchema.clean (http://localhost:3000/packages/aldeed_simple-schema.js?e210968641793751997ffe233af33b53af8566e4:2549:21)
    at doClean (http://localhost:3000/packages/aldeed_collection2.js?fdc7f0975dd1b3446ea4afd5f820cb1242a521af:291:12)
    at doValidate (http://localhost:3000/packages/aldeed_collection2.js?fdc7f0975dd1b3446ea4afd5f820cb1242a521af:317:3)
    at _.each.Mongo.Collection.(anonymous function) [as insert] (http://localhost:3000/packages/aldeed_collection2.js?fdc7f0975dd1b3446ea4afd5f820cb1242a521af:194:31)
    at Meteor.methods.addChart (http://localhost:3000/shared.js?9df34a6360e5cfad1a2b6231b0f79619f76863b6:232:20)
    at http://localhost:3000/packages/ddp.js?d1840d3ba04c65ffade261f362e26699b7509706:4269:25

it all depends on what the 'bla' is. Has to be legit meteor Mongo.insert.

ok, but in my Meteor.method, I’m taking a single parameter “chart” which I expect to be a json formatted document that I will insert to the Chart collection inside the method… so what I’m talking about is, how might some mischievous user in the browser console try to call my Meteor.methods directly… I’m trying to test the security of the application.

I know a user can do Chart.insert(’ some chart document’) in the console, but I’m wondering if I have insecure and autopublish off, could they still call the Metor.methods that I have available? (provided they know the name of them and how to call them properly)

You should never take a chart argument from the client and assume it’s correct. In fact, assume it’s incorrect and only take out the bits that you want.

Meteor.methods({
    insertChart: function (chart) {
        if (!chart) // handle null cases
        var serverChart = {};
        if (chart.status) {
            // perform checking on status value
            
            // everything's ok
            serverChart.status = chart.status
        }

        Charts.insert(serverChart);
    }
});

never insert whole objects by clients, it can contain anything. SimpleSchema goes a long way in specifying what you can insert. Actually, I just realised allowedValues is good for this case, because server inserts will check against schema and only insert values you deem valid.

Anyone can call Meteor.call(‘insertchart’, ‘blah’) from a browser console just like your js is doing.

what I’m wondering is, what is the actual syntax for calling a meteor.method from the browser console? my method takes a javascript json object, but I can’t seem to pass one in…

CORRECTION: I figured out the syntax:

Meteor.call('addChart', { "demographics" : {
        "referralType" : "1","chartStatus":"11", "location":"loc", "lastName" : "Smith", 
        "firstName" : "Billy", "mrn":"333", 'primaryInsurance':"INS", 'dob':'1/1/1970'}})

And, what I’ve also found by doing this, is that the simpleschema validation is happening even though I am doing the Meteor.call from the console. So, I think I am safe from users who might directly try to call my methods to insert records into the db, since it’s going to apply the same validation that simpleschema defines.

IE< if I leave out something required like lastname, it gives me this error:

debug.js:41 insert failed: Error: Last Name is required
    at getErrorObject (http://localhost:3000/packages/aldeed_collection2.js?fdc7f0975dd1b3446ea4afd5f820cb1242a521af:409:15)
    at doValidate (http://localhost:3000/packages/aldeed_collection2.js?fdc7f0975dd1b3446ea4afd5f820cb1242a521af:392:13)
    at _.each.Mongo.Collection.(anonymous function) [as insert] (http://localhost:3000/packages/aldeed_collection2.js?fdc7f0975dd1b3446ea4afd5f820cb1242a521af:194:31)
    at Meteor.methods.addChart (http://localhost:3000/shared.js?2780780cc1d3287a9d5d39714e3454df5dec8d05:233:20)
    at http://localhost:3000/packages/ddp.js?d1840d3ba04c65ffade261f362e26699b7509706:4269:25
    at _.extend.withValue (http://localhost:3000/packages/meteor.js?43b7958c1598803e94014f27f5f622b0bddc0aaf:955:17)
    at _.extend.apply (http://localhost:3000/packages/ddp.js?d1840d3ba04c65ffade261f362e26699b7509706:4260:54)
    at _.extend.call (http://localhost:3000/packages/ddp.js?d1840d3ba04c65ffade261f362e26699b7509706:4138:17)
    at <anonymous>:2:8
    at Object.InjectedScript._evaluateOn (<anonymous>:905:140)
undefined

which is being thrown up from the server side.

Agreed. The validation is applied when you call insert/update on a collection that’s got an attached schema on the server or client side.
However, that’s just data validation, you still need to do user verification (aka security, aka permission etc on server side). If you allow the user to pass in the chart object, they can overwrite other fields chart._id, chart.owner without you realising. Still perfectly valid.

hiii…wesyah234…yach erors code you, because not call method , i have simple example :
var doc = {username: username, email: email, password: password, firstName: fistname, lastName: lastname, tempatLahir: tempatLahir, birthday:birthday, createdAt: Date.now()}

Meteor.call('createNewUser', doc, function(error, id) {

  if (error) {

    Session.set('alert', error.reason);
  
  } else {

    Router.go('signin');
    pageSession.set("alert", "");
    console.log('Data Tersimpan');

  }

can try my code solution :smile: