Моя идея сервера Node + Mon goose, для управления ролями для авторизации и аутентификации - PullRequest
0 голосов
/ 06 апреля 2020

Я уже проделал эту работу, но я хотел бы знать, можно ли считать это хорошим способом.

Мой сервер используется веб-приложением, используемым 3 различными типами пользователей: admin , shop, client. Каждая роль может видеть определенные c вещи и выполнять определенные c API (маршруты). Таким образом, у меня есть 3 проблемы с сетью:

  1. Role Perms : пользователи могут выполнять (или не могут) определенные c маршруты в соответствии с их roles
  2. Доступ к ресурсам : пользователи могут видеть некоторые экземпляры ресурса (или не могут) в зависимости от того, кем он является. Например, клиенты могут видеть только магазины, расположенные на расстоянии не более 5 км.
  3. Прогноз ресурса : пользователи могут видеть только некоторые свойства ресурса в соответствии с их ролью

Аутентификация управляется с помощью токенов JWT, поэтому пользователь отправляет свой токен на каждый запрос. Сервер проверяет токен и, если он исправен, проверяет, что запрашиваемый API и ресурс доступны для этого пользователя. Ресурсы указываются с использованием моделей Mon goose.

=== 1. Роль Perms

Чтобы проверить авторизацию для API, у меня есть таблица, которая указывает для каждой функции (только те, которые представлены как API), какие пользователи могут запрашивать ее.

    auth_table['getItem()'] : {
                          read: ['admin', 'shop', 'client'], 
                          write:['client', 'admin']
                        };

И упомянутый маршрут:

routes.get('/user/:user_id/shop/:shop_id/item/:item_id',  function(req, res)
{
 // it's a GET, so I want to 'read'
 var user_id = req.params.user_id;
 var shop_id = req.params.shop_id;
 var item_id = req.params.item_id;

 var data = checkAuthorization(user_id, shop_id, 'getItem()', 'read');
 if(data.is_auth) 
  { 
   getItem(data.user, data.shop, data.item); /* goes on... */
  }
});

Функция checkAuthorization() находит пользователя, извлекает его роль и проверяет, имеет ли эта роль разрешение «чтение» для функции «getItem». Кроме того, в этом методе, если perm верно, я добавил логи c для доступа к ресурсам.

Пример , пользователь Даниэле хочет увидеть элемент 001 магазина Fiat поэтому он вызывает маршрут /user/daniele/shop/fiat/item/001

Сервер:

  1. проверяет токен, прикрепленный к заголовку req
  2. проверяет роль пользователя (' в этом случае)
  3. проверьте, имеет ли роль client разрешение read для функции getItem()
function checkAuthorization(user_id, shop_id, function_name, asked_perm)
{
 var user    = User.findById(user_id); // it's async in reality
 var is_auth = auth_table[function_name][asked_perm].contains(user.role);

}

=== 2. Ресурсы Доступ

После шага 3 выше, у меня есть шаг 4:

проверить, может ли клиент Даниэле увидеть магазин Fiat (внутренняя бизнес-логика c)

Так что в функции checkAuthorization(), после проверки функции perms, я проверяю, может ли этот клиент увидеть этот магазин. Итак, я сделаю что-то похожее на это:

// if(is_auth)  
var shop = Shop.findById(shop_id); // it's async in reality

if( distanceBetween(shop.location, user.location) < 5km)
  { has_resource_access = true; }. // ok user can see items of that shop

Так как в checkAuthorization() я всегда выполняю User.find (), Shop.find () ... Я решил оставить этот Mon goose объекты, и передавать их среди других функций, вместо того, чтобы каждый раз запрашивать у БД ресурсы. Итак, функция checkAuthorization() вернет

function checkAuthorization(user_id, shop_id, function_name, asked_perm)
{
 // if(is_auth) check Role Perms
 // if(has_resource_access ) check Resources Access
 return {user:user, shop:shop, is_auth:is_auth}
}

Поэтому я запрашиваю эти основные ресурсы только в первый раз, при проверке подлинности, и я беру с собой эти ресурсы во всех следующих вызываемых подфункциях. Я делаю это для каждого запроса API, думая, что в 99% случаев я должен выполнить User.find () и Shop.find (), поэтому всегда делайте это в начале, так что я могу сохранить весь остальной код чистым и легким, предполагая, что у меня уже есть необходимые ресурсы.

=== 3. Прогнозирование ресурсов

Потому что из этого, то есть я спрашиваю ресурсы БД в начале, я должен спросить их в соответствии с ролью пользователя. Поэтому я решил указать различные проекции в зависимости от роли в модели Mon goose и использовать пользовательскую функцию для поиска документов. Примерно так:

var mongoose    = require('mongoose');
var Schema      = mongoose.Schema;

var ShopSchema = new Schema({
 name         : {type:String},             // public 
 money_earned : {type:String},             // just to admin and shop
 items        :    [ {type: item_schema} ],// public
 platform_paid: {type:boolean}             // admin
});

var model = mongoose.model('Shop', ShopSchema);

var client_projection = 
{
 'name' : 1,
 'items': 1
}

/* I will use this function in my code, not the native 'find()' */
function model.custom_findById(shop_id, role)
{
 var projection;
switch (role) 
  {
    case shop:  projection   = shop_projection; break;
    case client:  projection = client_projection; break;
    case admin:  projection  = admin_projection; break;
  }
return Shop.findById(shop_id, projection);
}

Резюме: если пользователь запрашивает элемент, я должен:

  1. проверить, может ли роль пользователя читать элементы
  2. если да, проверьте, может ли этот пользователь видеть этот магазин (и, следовательно, его товары)
  3. , если да, вернуть документы, скрывая некоторые свойства.

Звучит хорошо

...