Meteor-Community-Packages / meteor-roles

Authorization package for Meteor, compatible with built-in accounts packages
http://meteor-community-packages.github.io/meteor-roles/
MIT License
920 stars 164 forks source link

Error: You must specify at least 1 values in users update #310

Closed charlesdeb closed 2 years ago

charlesdeb commented 4 years ago

I have been trying to upgrade from roles 1.2.16 to 3.2 and not having much success.

Meteor did not want to upgrade beyond 1.2.19, so I removed the roles package and re-added it, which brought me to version 3.2 - rather than your recommended v2 migration. Anyway, after reimporting the production (i.e. non-migrated) version of the database just in case I corrupted it, I now have roles@2.1 installed. Whenever I try to do the migration, this is what happens in meteor shell:

> Package['alanning:roles'].Roles._forwardMigrate()
Error: You must specify at least 1 values in users update
    at evalCommandPromise.then (packages/shell-server/shell-server.js:249:21)
    at runBound (domain.js:314:12)
    at bound (domain.js:301:14)
    at defaultEval (repl.js:240:29)
    at ContextifyScript.Script.runInThisContext (vm.js:50:33)
    at repl:1:-29
    at Object._forwardMigrate (packages/alanning:roles/roles/roles_server.js:275:25)
    at Cursor.(anonymous function) [as forEach] (packages/mongo/mongo_driver.js:887:44)
    at SynchronousCursor.forEach (packages/mongo/mongo_driver.js:1107:16)
    at packages/alanning:roles/roles/roles_server.js:277:9
    at _defaultUpdateUser (packages/alanning:roles/roles/roles_server.js:213:18)
    at Collection.Mongo.Collection.(anonymous function) [as update] (packages/aldeed:collection2/collection2.js:196:14)
    at doValidate (packages/aldeed:collection2/collection2.js:463:13)
    at getErrorObject (packages/aldeed:collection2/collection2.js:491:17)

When I look inside my mongo DB I can see that the structure of the roles collection has changed - the ids are now the names of the old roles, but the children are empty. But there has been no change to the roles field in my users collection. I am using collections2@3.0.5. I have had a bit of a look at what the migration is trying to update and new roles like this:

roles: [ { _id: 'user', scope: null, assigned: true } ] }

are being passed to _defaultUpdateUser(). The old roles in the collection were like this:

"roles" : [ 
        "user"
    ],

So, the error message Error: You must specify at least 1 values in users update doesn't appear to be accurate - unless I am missing something. Digging more deeply into the collection2 package is not for the faint-hearted...

Suggestions?

SimonSimCity commented 4 years ago

My first guess would be that you've assigned a schema to the users collection, can you please confirm this - and if - post the schema here? If you need the schema in place, I'd advice you to define it as blackbox, then update to v2 and then to v3 after which you can remove the property completely from the users collection.

charlesdeb commented 4 years ago

Yes, I am assigning a schema to the Users collection. Doesn't pretty much everyone?

When you talk about blackbox, are you saying that I need to define the roles property as blackbox? Right now it is: type: Array. And are you saying that once the upgrade is done I can remove the roles property completely from my Users schema?

This is an edited (and simplified version) of my code:

const Users = {};

Users.schema = new SimpleSchema(
  {
    username: {
      type: String,
      optional: true
    },
    emails: {
      type: Array,
      optional: true,
      maxCount: 1,
      minCount: 1
    },
    "emails.$": {
      type: Object
    },
    "emails.$.address": {
      type: String,
      regEx: SimpleSchema.RegEx.Email,
      label: "Email"
    },
    "emails.$.verified": {
      type: Boolean,
      label: "Email verified?",
      autoform: {
      }
    },
.
.  // some custom fields here that should be irrelevant 
.
    // Add `roles` to your schema if you use the meteor-roles package.
    // Option 1: Object type
    // If you specify that type as Object, you must also specify the
    // `Roles.GLOBAL_GROUP` group whenever you add a user to a role.
    // Example:
    // Roles.addUsersToRoles(userId, ["admin"], Roles.GLOBAL_GROUP);
    // You can't mix and match adding with and without a group since
    // you will fail validation in some cases.
    // roles: {
    //   type: Object,
    //   optional: true,
    //   blackbox: true
    // },
    // Option 2: [String] type
    // If you are sure you will never need to use role groups, then
    // you can specify [String] as the type
    roles: {
      type: Array,
      minCount: 1,
      defaultValue: [LT.roles.USER]
    },
    "roles.$": {
      type: String,
      allowedValues: [LT.roles.USER, LT.roles.ADMIN]
    },
    // needed for mizzao:meteor-user-status
    status: {
      type: Object,
      optional: true,
      blackbox: true
    },
    // In order to avoid an 'Exception in setInterval callback' from Meteor
    heartbeat: {
      type: Date,
      optional: true
    },
  },
  { tracker: Tracker }
);

Meteor.users.after.insert(function(userId, doc) {
  if (Meteor.isServer) {
    if (!Meteor.isTest && Meteor.users.find().fetch().length == 1) {
      // set up very first user
      console.log(`---> Adding admin role to first user, "${doc._id}"`);
      Roles.addUsersToRoles(doc._id, [LT.roles.ADMIN, LT.roles.USER]);
    }
  }
});

Users.schema.extend(namesSchema);

Meteor.users.attachSchema(Users.schema);
SimonSimCity commented 4 years ago

Well ... your assumptions are all correct 😊

In version 3 of this plugin, the assigning of a role is no longer stored in the users collection, but in a separate collection. But I think it's the schema blocking the update script because of the schema ...

So, I'd deploy a version where the roles property on the users collection is defined as a blackbox object, upgrade the database structure first to 2.x then to 3.x, after which you can remove the property of your schema. Just make sure first that everything works with 3.x - otherwise do one step at a time 😉

charlesdeb commented 4 years ago

I made the change to blackbox and verified that permssions in my app were still working as expected. They were. Then I updated the package from 1.2.19 to 2.1.0 and ran the shell command. It appeared to run correctly, but produced no output at all. Is that normal? I checked the mongo database and could see that changes had been made - the roles property in the users collection is now an empty array. The roles collection has two documents in it with ids corresponding to the two roles I had - but the children property for both of these roles is empty. This seems wrong to me. I also notice a new role-assignment collection. It has no documents in it at all.

When I run the application, permissions no longer work - but this is hardly surprising since all the permissions data appears to have been stripped from the database during the migration. Any suggestions?

SimonSimCity commented 4 years ago

Well, if you said that you now see a new collection role-assignment, you've updated to v3, because this is the first version I moved the assignments out of the users collection.

It's normal for either of the migration scripts to execute without any output, but once they're done, it should be done.

The roles collection looks quite well, if you ask me. From your config, I can guess that you have two roles, LT.roles.USER and LT.roles.ADMIN. I guess that the roles are not nested (e.g. a user having the LT.roles.ADMIN role does not automatically have the LT.roles.USER, right? This way the collection there is correct.

You said that the roles property on the documents of the users collection is an empty array - this leads me to that you've only ran the first migration script, upgrading the database structure from v1 to v2, since an upgrade to v3 would have dropped the property (see: https://github.com/Meteor-Community-Packages/meteor-roles/blob/master/roles/roles_server.js#L304)

Do you still have a backup of the users and roles collection and could revert it back and run the first upgrade script once more?

charlesdeb commented 4 years ago

Thanks for getting back to me. This is a line from .meteor/packages: alanning:roles@2.1.0 . So, I am running a 2.x version as it says in the docs. And I definitely ran the _forward_migration (and not _forward_migration2 which is in v3. I may have the name slightly mixed up here, but I am aware there are two migration scripts, and I definitely used the first one). Maybe I should try 2.0? It is possible that the role-assignment collection is a remnant from a previous (failed) attempt of mine at doing this - although I am fairly sure I removed the entire database last time I tried this. Since 2.1.0 is the last in the 2.x series, maybe roles-assignment was in there for beta purposes?

You are right, my two roles are not nested. And I do have a backup, so I will try 2.0 as opposed to 2.1 and try again over the weekend.

SimonSimCity commented 4 years ago

It shouldn't matter what minor version you have - all versions of 2.x share the same data-structure (equally so all versions of 3.x).

The collection is created as soon as you once tried the version 3.x - so it could well be a left over from the time you previously tried it - it even happens without running the migration script.

I'd even advise you to take the latest version of a major release because also the migration scripts might receive updates.

I myself have never used the first major version and am therefore not familar with the data-structure before v2 and also sincerely hope the migration script was written with all the edge cases in mind - at least it looked like it from a first glance. Please let me know how it goes with a second attempt of migration.

charlesdeb commented 4 years ago

OK, I reloaded from a back-up, removed the roles-assignment collection and ran Package['alanning:roles'].Roles._forwardMigrate() again. No change. Same result whether I used 2.0.0 or 2.1.0: the roles collection has my old roles in it, but in the _id property and the children property was empty. The roles property in my users collection still has a roles property, but now, instead of this property being an array containing 1 or 2 of the roles, the array is empty for all documents in the collection. What is the expected behaviour? Should the roles property in the users collection be an array of roles like ['user', 'admin']?

Any idea what might be happening? Or will I need to debug this myself?

charlesdeb commented 4 years ago

I have had a look at this myself, and for some (as yet unknown) reason, Roles.addUsersToRoles doesn't seem to work properly on my codebase for 2.0.0 - or the original 1.2.19. There is something screwy in my set-up which I'll need to debug at some stage... If it is a problem with meteor-roles I'll let you know.

StorytellerCZ commented 2 years ago

I'm closing this issue because we haven't heard anything from the original poster for a while.