2014-06-18 14 views
7

Sto creando un sito abbastanza semplice con Node, Express e Mongoose. Il sito deve avere ruoli utente e autorizzazioni. I miei pensieri sono che convaliderò le autorizzazioni in base all'interazione dell'utente con il database.Express.js/Mongoose ruoli utente e permessi

In manera c'è un modo per determinare il tipo di operazione CRUD attualmente eseguita da un utente?

+0

C'è un modo senza utilizzare le sessioni? – Foreever

+2

Ho trovato una soluzione migliore a questo. Ora utilizzerò il middleware espresso per verificare le autorizzazioni degli utenti prima che la rotta venga resa. Peresempio 'app.get ('/ users', permissions.check ('leggere', 'utenti'), func() {});' Questo ware mezzo poi i controlli nei confronti di un oggetto di autorizzazioni di configurazione come: 'roles.superAdmin = { id: "superadmin", nome: "Super Admin", descrizione: "", risorsa: [ { id: 'post', permesso: [ 'creare', 'leggere ',' aggiorna ',' cancella ') } ] }; ' – tkiddle

+2

ya, esattamente. Stavo arrivando a quel punto. Ma come stai seguendo l'utente? Inoltre, se hai la risposta, dovresti postarla qui per gli altri. – Foreever

risposta

0

Sì, è possibile accedervi tramite l'argomento request.

app.use(function(req,res,next){ 
    console.log(req.method); 
}); 

http://nodejs.org/api/http.html#http_message_method

Edit:

letto male la tua domanda. Probabilmente sarebbe meglio assegnare le autorizzazioni utente e consentire l'accesso al database in base alle autorizzazioni. Non capisco cosa intendi per convalidare tramite l'interazione con il database. Se stai già permettendo loro di interagire con il database e non hanno le autorizzazioni appropriate per farlo, non è un problema di sicurezza?

6

Ho trovato una soluzione. Sarebbe bello sentire le opinioni della gente su questo.

Possiedo un oggetto config di autorizzazioni che definisce ciascun ruolo e le relative autorizzazioni.

autorizzazioni config oggetto

roles.admin = { 
    id: "admin", 
    name: "Admin", 
    description: "", 
    resource : [ 
     { 
      id : 'blog', 
      permissions: ['create', 'read', 'update', 'delete'] 
     }, 
     { 
      id : 'user', 
      permissions: ['create', 'read', 'update', 'delete'] 
     }, 
     { 
      id : 'journal', 
      permissions: ['create', 'read', 'update', 'delete'] 
     }, 

    ] 
}; 

roles.editor = { 
    id: "editor", 
    name: "Editor", 
    description: "", 
    resource : [ 
     { 
      id : 'blog', 
      permissions: ['create', 'read', 'update', 'delete'] 
     }, 
     { 
      id : 'user', 
      permissions: ['read'] 
     }, 
     { 
      id : 'journal', 
      permissions: ['create', 'read', 'update'] 
     }, 

    ] 
}; 

funzione Middleware

var roles = require('./config'); 


var permissions = (function() { 

    var getRoles = function (role) { 

    var rolesArr = []; 

    if (typeof role === 'object' && Array.isArray(role)) { 

     // Returns selected roles 
     for (var i = 0, len = role.length; i < len; i++) { 
      rolesArr.push(roles[role[i]]); 
     }; 
     return rolesArr; 

    } else if (typeof role === 'string' || !role) { 

     // Returns all roles 
     if (!role) { 
      for (var role in roles) { 
       rolesArr.push(roles[role]); 
      }; 
     } 

     // Returns single role 
     rolesArr.push(roles[role]); 
     return rolesArr; 

    } 

}, 
check = function (action, resource, loginRequired) { 

    return function(req, res, next) { 

     var isAuth = req.isAuthenticated(); 

     // If user is required to be logged in & isn't 
     if (loginRequired && !isAuth) { 
      return next(new Error("You must be logged in to view this area")); 
     } 

     if (isAuth || !loginRequired) { 

      var authRole = isAuth ? req.user.role : 'user', 
       role = get(authRole), 
       hasPermission = false; 

      (function() { 
       for (var i = 0, len = role[0].resource.length; i < len; i++){ 
        if (role[0].resource[i].id === resource && role[0].resource[i].permissions.indexOf(action) !== -1) { 
         hasPermission = true; 
         return; 
        } 
       }; 
      })(); 

      if (hasPermission) { 
       next(); 
      } else { 
       return next(new Error("You are trying to " + action + " a " + resource + " and do not have the correct permissions.")); 
      } 

     } 
    } 
} 

return { 
    get : function (role) { 

     var roles = getRoles(role); 

     return roles; 
    }, 
    check : function (action, resource, loginRequired) { 
     return check(action, resource, loginRequired); 
    } 
} 

})(); 

module.exports = permissions; 

Poi ho creato una funzione middleware, quando il metodo controllo viene chiamato si ottiene il ruolo utenti dal req oggetto (req.user.role). Quindi esamina i parametri passati al middleware e li incrocia con quelli nell'oggetto config di autorizzazioni.

percorso con middlware

app.get('/journal', `**permissions.check('read', 'journal')**`, function (req, res) { 
    // do stuff 
}; 
1

ho personalmente preso ispirazione da fantasma. Nella mia configurazione ci sono i permanenti, e permissions.js esportare una funzione canThis che prende l'utente registrato corrente. Here is the whole project

Parte del mio file di configurazione

"user_groups": { 
    "admin": { 
     "full_name": "Administrators", 
     "description": "Adminsitators.", 
     "allowedActions": "all" 
    }, 
    "modo": { 
     "full_name": "Moderators", 
     "description": "Moderators.", 
     "allowedActions": ["mod:*", "comment:*", "user:delete browse add banish edit"] 
    }, 
    "user": { 
     "full_name": "User", 
     "description": "User.", 
     "allowedActions": ["mod:browse add star", "comment:browse add", "user:browse"] 
    }, 
    "guest": { 
     "full_name": "Guest", 
     "description": "Guest.", 
     "allowedActions": ["mod:browse", "comment:browse", "user:browse add"] 
    } 
    }, 

mongoose = require("mongoose") 
### 
This utility function determine whether an user can do this or this 
using the permissions. e. g. "mod" "delete" 

@param userId the id of the user 
@param object the current object name ("mod", "user"...) 
@param action to be executed on the object (delete, edit, browse...) 
@param owner the optional owner id of the object to be "actionned" 
### 

# **Important this is a promise but to make a lighter code I removed it** 
exports.canThis = (userId, object, action, ownerId, callback) -> 
    User = mongoose.model("User") 
    if typeof ownerId is "function" 
    callback = ownerId 
    ownerId = undefined 
    if userId is "" 
    return process(undefined, object, action, ownerId, callback) 
    User.findById(userId, (err, user) -> 
    if err then return callback err 
    process(user, object, action, ownerId, callback) 
) 


process = (user, object, action, ownerId, callback) -> 
    if user then role = user.role or "user" 
    group = config.user_groups[role or "guest"] 
    if not group then return callback(new Error "No suitable group") 

    # Parses the perms 
    actions = group.allowedActions 
    for objAction in actions when objAction.indexOf object is 0 
    # We get all the allowed actions for the object and group 
    act = objAction.split(":")[1] 
    obj = objAction.split(":")[0] 
    if act.split(" ").indexOf(action) isnt -1 and obj is object 
     return callback true 

    callback false 

config = require "../config" 

Esempio di utilizzo:

exports.edit = (userid, name) -> 
    # Q promise 
    deferred = Q.defer() 
    # default value 
    can = false 
    # We check wheteher it can or not 
    canThis(userid, "user", "edit").then((can)-> 
    if not userid 
     return deferred.reject(error.throwError "", "UNAUTHORIZED") 
    User = mongoose.model "User" 
    User.findOne({username: name}).select("username location website public_email company bio").exec() 
).then((user) -> 
    # Can the current user do that? 
    if not user._id.equals(userid) and can is false 
     return deferred.reject(error.throwError "", "UNAUTHORIZED") 
    # Done! 
    deferred.resolve user 
).fail((err) -> 
    deferred.reject err 
) 
    deferred.promise 

Forse quello che ho fatto non è buona, ma funziona bene per quanto posso vedere.

0

Controllare il modulo nodo permission per quella materia. È un concetto abbastanza semplice, spero che permetteranno anche tutti i metodi CRUD.

3

Questa è la mia implementazione.Il codice è riutilizzabile per client e server. Io lo uso per il mio espresso/sito web angolare

  1. ridurre il codice duplicato, una migliore coerenza tra client/server
  2. vantaggio Bonus: sull'adattatore del cliente, possiamo semplicemente restituire true per concedere l'accesso massimo per testare la robustezza dei server di (dal momento che gli hacker e facilmente superare lato client limitare)

in app/sia/both.js

var accessList = { 
    //note: same name as controller's function name 
    assignEditor: 'assignEditor' 

    ,adminPage: 'adminPage' 
    ,editorPage: 'editorPage' 
    ,profilePage: 'profilePage' 

    ,createArticle: 'createArticle' 
    ,updateArticle: 'updateArticle' 
    ,deleteArticle: 'deleteArticle' 
    ,undeleteArticle: 'undeleteArticle' 
    ,banArticle: 'banArticle' 
    ,unbanArticle: 'unbanArticle' 

    ,createComment: 'createComment' 
    ,updateComment: 'updateComment' 
    ,deleteComment: 'deleteComment' 
    ,undeleteComment: 'undeleteComment' 
    ,banComment: 'banComment' 
    ,unbanComment: 'unbanComment' 

    ,updateProfile: 'updateProfile' 

} 
exports.accessList = accessList 

var resourceList = { 
    //Note: same name as req.resource name 
    profile: 'profile' 
    ,article: 'article' 
    ,comment: 'comment' 
} 
exports.resourceList = resourceList 

var roleList = { 
    admin: 'admin' 
    ,editor: 'editor' 
    ,entityCreator: 'entityCreator' 
    ,profileOwner: 'profileOwner' //creator or profile owner 
    ,normal: 'normal' //normal user, signed in 
    ,visitor: 'visitor' //not signed in, not used, open pages are uncontrolled 
} 

var permissionList = {} 

permissionList[accessList.assignEditor]  = [roleList.admin] 

permissionList[accessList.adminPage]  = [roleList.admin] 
permissionList[accessList.editorPage]  = [roleList.admin, roleList.editor] 
permissionList[accessList.profilePage]  = [roleList.admin, roleList.editor, roleList.normal] 

permissionList[accessList.createArticle] = [roleList.admin, roleList.editor, roleList.normal] 
permissionList[accessList.updateArticle] = [roleList.admin, roleList.editor, roleList.entityCreator] 
permissionList[accessList.deleteArticle] = [roleList.admin, roleList.editor, roleList.entityCreator] 
permissionList[accessList.undeleteArticle] = [roleList.admin, roleList.editor, roleList.entityCreator] 
permissionList[accessList.banArticle]  = [roleList.admin, roleList.editor] 
permissionList[accessList.unbanArticle]  = [roleList.admin, roleList.editor] 

permissionList[accessList.createComment] = [roleList.admin, roleList.editor, roleList.normal] 
permissionList[accessList.updateComment] = [roleList.admin, roleList.editor, roleList.entityCreator] 
permissionList[accessList.deleteComment] = [roleList.admin, roleList.editor, roleList.entityCreator] 
permissionList[accessList.undeleteComment] = [roleList.admin, roleList.editor, roleList.entityCreator] 
permissionList[accessList.banComment]  = [roleList.admin, roleList.editor] 
permissionList[accessList.unbanComment]  = [roleList.admin, roleList.editor] 

permissionList[accessList.updateProfile] = [roleList.admin, roleList.profileOwner] 



var getRoles = function(access, resource, isAuthenticated, entity, user) { 
    var roles = [roleList.visitor] 
    if (isAuthenticated) { 
     roles = [roleList.normal] 
     if (user.username === 'admin') 
      roles = [roleList.admin] 
     else if (user.type === 'editor') 
      roles = [roleList.editor] 


     if (resource) { 
      if (resource === resourceList.profile) { 
       //Note: on server _id is a object, client _id is string, which does not have equals method 
       if (entity && entity._id.toString() === user._id.toString()) 
        roles.push(roleList.profileOwner) 
      } 
      else if (resource === resourceList.article) { 
       if (entity && entity.statusMeta.createdBy._id.toString() === user._id.toString()) 
        roles.push(roleList.entityCreator) 
      } 
      else if (resource === resourceList.comment) { 
       if (entity && entity.statusMeta.createdBy._id.toString() === user._id.toString()) 
        roles.push(roleList.entityCreator) 
      } 
     } 
    } 
    return roles 
} 


exports.havePermission = function(access, resource, isAuthenticated, entity, user) { 
    var roles = getRoles(access, resource, isAuthenticated, entity, user) 


    //Note: we can implement black list here as well, like IP Ban 

    if (!permissionList[access]) 
     return true 

    for (var i = 0; i < roles.length; i++) { 
     var role = roles[i] 
     if (permissionList[access].indexOf(role) !== -1) 
      return true 
    } 
    return false 

} 

Poi app/Server/helper.js (agire come adattatore

var both = require(dir.both + '/both.js') 
exports.accessList = both.accessList 
exports.resourceList = both.resourceList 
exports.havePermission = function(access, resource, req) { 
    return both.havePermission(access, resource, req.isAuthenticated(), req[resource], req.user) 
} 


//todo: use this function in other places 
exports.getPermissionError = function(message) { 
    var err = new Error(message || 'you do not have the permission') 
    err.status = 403 
    return err 
} 

exports.getAuthenticationError = function(message) { 
    var err = new Error(message || 'please sign in') 
    err.status = 401 
    return err 
} 

exports.requiresPermission = function(access, resource) { 
    return function(req, res, next) { 
     if (exports.havePermission(access, resource, req)) 
      return next() 
     else { 
      if (!req.isAuthenticated()) 
       return next(exports.getAuthenticationError()) 
      else 
       return next(exports.getPermissionError()) 
     } 
    } 
} 

su app/client/helper.js, funge anche da adattatore.

exports.accessList = both.accessList 
exports.resourceList = both.resourceList 
exports.havePermission = function(access, resource, userService, entity) { 
    //Note: In debugging, we can grant client helper all access, and test robustness of server 
    return both.havePermission(access, resource, userService.isAuthenticated(), entity, userService.user) 
} 
+0

Hai un meccanismo per assicurarti che solo il proprietario possa cancellare o aggiornare una risorsa? Potrebbe essere proprio qui a fissarmi in faccia ma non lo vedo. Grazie per il tuo contributo! – WillyC

Problemi correlati