反向匹配Express路由到数据库条目

时间:2017-05-27 22:23:31

标签: node.js regex express acl

我正在尝试为我的Express应用程序构建一种基于数据库的ACL。我目前在数据库中有一个包含类似内容的权限表:

  • 资源:*
  • 方法:*
  • RoleID:1
  • isAllowed:true
  • 资源:/users
  • 方法:GET
  • RoleID:2
  • isAllowed:false
  • 资源:/users/id/*
  • 方法:GET
  • RoleID:2
  • isAllowed:true

我的目标是构建一个中间件,用于检查请求对象,并根据数据库中的规则允许或拒绝路由。我的实际问题是,我如何匹配,说/users/id/1与数据库条目/users/id/*?如果我使用数据库条目作为我的正则表达式的基础,那么/users/id/1显然是匹配的,但是,我认为为每个请求提取和测试所有数据库条目是不切实际的。您认为根据请求的URL从数据库中获取正确规则的最佳方法是什么?

谢谢你的时间!

1 个答案:

答案 0 :(得分:0)

好的,经过一番思考和研究,我发现你可以在MySQL查询中使用正则表达式,所以我想出了这个中间件(我正在使用Sequelize):

module.exports = function (req, res, next) {
  // If a wildcard is in place, skip the rest
  return models.Permissions.findAll({
    where: {
      resource: '*',
      GID: req.session.role,
      isAllowed: 1
    }
  }).then(function (result) {
    if (result[0]) {
      return next()
    }

    // If the URL contains more than one element, replace the last item with [item, *]
    // to match eventual wildcards in the database entries
    let urlItems = req.url.split('/').filter(Boolean)
    let url = req.url
    if (urlItems.length > 1) {
      let lastItem = '[' + urlItems[urlItems.length - 1] + ', *]'
      url = req.url.split('/')
      url[url.length - 1] = lastItem
      url = url.join('/')
    }

    let query = 'SELECT * FROM Permissions '
    query += 'WHERE resource RLIKE "^' + url + '?$" '
    query += 'AND GID = ' + req.session.role

    return models.sequelize.query(query, {
      type: models.sequelize.QueryTypes.SELECT
    }).then(function (result) {
      let policy = result[0]

      function return403 () {
        res.status(403).send('Forbidden')
      }

      // Forbid everything by default
      if (!policy) {
        return403()
        return
      }

      let methods = policy.method.toUpperCase().split(' ')

      // Forbid all methods which are not allowed
      if (policy.method === '*' || methods.includes(req.method)) {
        if (!policy.isAllowed) {
          return403()
          return
        }
      }

      // When other methods are explicitly allowed, forbid everything else
      if (policy.method !== '*' && !methods.includes(req.method) && policy.isAllowed) {
        return403()
        return
      }

      // Standard behaviour: allow explicitly allowed methods (or *) that are allowed.
      next()
    })
  })
}