Создайте иерархию разрешений в стиле firebase в mongoDB - PullRequest
0 голосов
/ 05 января 2019

Новичок в MongoDB и, работая с mongodb пакетом NodeJS , я пытаюсь получить и обновить элементы на основе уровней разрешений пользователя, которые также хранятся в коллекции.

Я понимаю, как получить все предметы:

 const collection = db.collection('events');
  // Find some documents
  collection.find({}).toArray(function(err, docs) {
    assert.equal(err, null);
    console.log("Found the following records");
    console.log(docs)
    callback(docs);
  });

Но мне интересно, учитывая ли следующую структуру данных:

{
    events: {
        1: {
            id: 1,
            name: "first event",
        },
        2: {
            id: 2,
            name: "2nd event",
        },
        3: {
            id: 3,
            name: "3rd event",
        },
    },
    permissions: {
        events: {
            1: {
                user1: "read",
                user2: "write",
            },
            2: {
                user2: "write",
            },
            3: {
                user1: "readwrite",
                user2: "write",
            },
        },
    },
 }

и если вы вошли в систему как Пользователь1, приведенный выше код извлечения может извлекать только соответствующие события, к которым пользователь1 имеет доступ на чтение, в соответствии с указанными разрешениями?

Для CRUDS, таких как обновления, удаления и вставки, я могу выполнить отдельный запрос и посмотреть, есть ли у пользователя необходимый доступ. Тем не менее, запросы get распространены, и IMHO должен быть простым, поэтому мне интересно, как эта фильтрация может быть выполнена элегантно?

1 Ответ

0 голосов
/ 06 января 2019

Прежде всего, я бы упростил вашу модель данных, поскольку есть кое-что, что может вас укусить в будущем. Вы не должны использовать значения в качестве ключей, как вы это делали в документах мероприятия. Ясно, что это массивы, и элементы массива уже имеют идентификатор, на который они могут ссылаться.

Кроме того, ваша модель данных (и впоследствии моя производная) подходит только для пары тысяч событий, поскольку для документов существует ограничение 16 МБ. Чтобы смягчить это, я покажу вам обновленную модель данных и то, как с ней бороться.

Один документ для событий

Модель данных

Вот моя обновленная модель данных. Мы в основном избавились от ненужных значений, выдаваемых за ключи.

{
  "events": [{
      "id": 1,
      "name": "first event",
    },
    {
      "id": 2,
      "name": "2nd event",
    },
    {
      "id": 3,
      "name": "3rd event",
    }
  ],
  "permissions": [{
      "event": 1,
      "perms": [{
          "user": "user1",
          "permission": "read",
        },
        {
          "user": "user2",
          "permission": "write"
        }
      ]
    },
    {
      "event": 2,
      "perms": [{
        "user": "user2",
        "permission": "write"
      }]
    },
    {
      "event": 3,
      "perms": [{
          "user": "user1",
          "permission": "readwrite"
        },
        {
          "user": "user2",
          "permission": "write"
        },
      ]
    }
  ],
}

Что по-прежнему дает вам возможность искать конкретные события с помощью db.events.find({"events.id":1},{"events.$":1}). Это, конечно, работает для каждого другого свойства события.

Проверка прав доступа

Основной вопрос о том, имеет ли пользователь разрешение на выполнение определенной операции с конкретным документом, можно перефразировать как

Получите мне все документы со интересующими меня свойствами И , для которых пользователь Y имеет право делать Z.

Это переводит к довольно простому запросу:

db.events.find({
  // The event we want...
  "events.id": 1,
  // ...shall be returned provided the following conditions are met
  "permissions": {
    $elemMatch: {
      // The permissions for event with id 1...
      "event": 1,
      "perms": {
        $elemMatch: {
          // ...allow the user in question... 
          "user": "user1",
          // ... to read said event.
          "permission": "read"
        }
      }
    }
  }
}, {
  // Subsequently, we return all matching events in all documents...
  "events.$": 1
})

Здесь мы используем то, что условия запроса оцениваются как логическое И. Если у пользователя нет запрошенного нами разрешения (в данном примере «чтение»), документ не возвращается.

Один документ на событие

Модель данных

Однако, как уже было сказано, для документов MongoDB существует ограничение в 16 МБ. Поэтому, если у вас много событий, или даже если вы не совсем уверены в количестве событий, которые у вас могут возникнуть, мы должны подойти к проблеме с другой (и даже более простой) точки зрения: один документ на событие. Модель данных становится глупо простой:

// db.events.insertMany([
{
  "_id": 1,
  "name": "first event",
  "permissions":[
    {
      "user":"user1",
      "permission": "read"
    },
    {
      "user":"user2",
      "permission":"write"
    }
  ]
},
{
  "_id":2,
  "name": "2nd event",
  "permissions":[
    {
      "user": "user2",
      "permission": "write"
    }
  ]
},
{
  "_id":3,
  "name": "3rd event",
  "permissions":[
    {
        "user": "user1",
        "permission": "readwrite"
      },
      {
        "user": "user2",
        "permission": "write"
      },
  ]
}
// ])

Проверка прав доступа

Теперь, допустим, мы хотим проверить, может ли пользователь1 действительно читать (включая чтение)) событие 3 и, если да, вернуть его. Становится еще менее сложным, чем раньше:

db.events.find({
  // The event we want...
  "_id": 3,
  // ...in case...
  "permissions": {
    $elemMatch: {
      // ... user1...
      "user": "user1",
      // ..has either the permission...
      $or: [{
        // ...to read or...
        "permission": "read"
      }, {
        // ... to readwrite.
        "permission": "readwrite"
      }]
    }
  }
}, {
  // Either way, do not return the permissions.
  permissions: 0
})

Заключение

Независимо от того, какую модель данных из двух вы выберете, вы можете использовать вышеуказанные запросы в качестве части запроса ваших обновлений :

db.events.update(<queryFromAbove>,{[...]})

Излишне говорить, что я настоятельно рекомендую использовать подход «один документ на событие».

Однако нужно помнить о двух вещах.

Если что-то идет не так, вам нужно выполнить дополнительные запросы, чтобы выяснить, что пошло не так. Возможно, событие либо вообще не существует, либо пользователь может не иметь необходимых разрешений для выполнения запроса / обновления. Однако, так как это должно быть исключением, я лично мог бы жить с этим.

Еще одна вещь, которую нужно иметь в виду, это то, что вам нужно убедиться, что пользователь никогда не сможет изменить массив разрешений. По довольно очевидным причинам.

...