added svcapi ui and camunda code
[it/otf.git] / otf-frontend / server / src / feathers / hooks / permissions / permissions.js
diff --git a/otf-frontend/server/src/feathers/hooks/permissions/permissions.js b/otf-frontend/server/src/feathers/hooks/permissions/permissions.js
new file mode 100644 (file)
index 0000000..3659bde
--- /dev/null
@@ -0,0 +1,234 @@
+/*  Copyright (c) 2019 AT&T Intellectual Property.                             #\r
+#                                                                              #\r
+#   Licensed under the Apache License, Version 2.0 (the "License");            #\r
+#   you may not use this file except in compliance with the License.           #\r
+#   You may obtain a copy of the License at                                    #\r
+#                                                                              #\r
+#       http://www.apache.org/licenses/LICENSE-2.0                             #\r
+#                                                                              #\r
+#   Unless required by applicable law or agreed to in writing, software        #\r
+#   distributed under the License is distributed on an "AS IS" BASIS,          #\r
+#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   #\r
+#   See the License for the specific language governing permissions and        #\r
+#   limitations under the License.                                             #\r
+##############################################################################*/\r
+\r
+\r
+const { defineAbilitiesFor } = require('./abilities.js');\r
+const { toMongoQuery } = require('@casl/mongoose')\r
+const getPermissions = require('./get-permissions.js');\r
+const { Forbidden } = require('@feathersjs/errors');\r
+//const pick = require('lodash.pick');\r
+var dot = require('dot-object');\r
+const pick = require('object.pick');\r
+const { ObjectID } = require('mongodb');\r
+\r
+permissions = function (name = null) {\r
+       return async context => {\r
+\r
+               if (!context.params.provider) {\r
+                       return Promise.resolve(context);\r
+               }\r
+\r
+               const action = context.method;\r
+               const service = name ? context.app.service(context.path) : context.service;\r
+               const serviceName = name || context.path;\r
+\r
+               let groupQueryParams = JSON.parse(JSON.stringify(context.params));\r
+               groupQueryParams.query = {\r
+                       lookup: 'both'\r
+               }\r
+\r
+               //get groups list\r
+               let groups = await context.app.services[context.app.get('base-path') + 'groups'].find(groupQueryParams);\r
+\r
+               //organize permissions for the groups\r
+               let groupsPermissions = getPermissions(context.params.user, groups);\r
+\r
+               //Define Abilities for the user\r
+               const ability = defineAbilitiesFor(context.params.user, groupsPermissions);\r
+\r
+               //Define the fields that they have access to\r
+               let allowedFields;\r
+               if(service.Model){\r
+                       allowedFields = service.Model.accessibleFieldsBy(ability, context.method);\r
+               }\r
+\r
+               const throwUnlessCan = (action, resource, field = null) => {\r
+                       let instance = resource;\r
+                       if(service.Model && typeof resource === 'object'){\r
+                               instance = new service.Model(resource);\r
+                       }else{\r
+                               instance = serviceName;\r
+                       }\r
+       \r
+                       if (ability.cannot(action, instance, field)) {\r
+                               let message = `You are not allowed to ${action} ${serviceName}`;\r
+\r
+                               if(field){\r
+                                       message += ` on field ${field}`;\r
+                               }\r
+\r
+                               throw new Forbidden(message);\r
+                       }\r
+               }\r
+\r
+               context.params.ability = ability;\r
+\r
+\r
+               if (context.method === 'create') {\r
+                       throwUnlessCan('create', context.data);\r
+               }\r
+\r
+               if (!context.id) {               \r
+\r
+                       throwUnlessCan(context.method, serviceName);\r
+                       \r
+                       const query = toMongoQuery(ability, serviceName, action);\r
+                       \r
+                       if (query !== null) {\r
+                               if(context.params.query.$or && query.$or){\r
+                                       query.$and = [\r
+                                               {$or: Object.assign([], context.params.query.$or)},\r
+                                               {$or: Object.assign([], query.$or)}\r
+                                       ];\r
+                                       delete context.params.query.$or;\r
+                                       delete query.$or;\r
+                               }\r
+\r
+                               Object.assign(context.params.query, query);\r
+\r
+                       } else {\r
+                               context.params.query.$limit = 0;\r
+                       }\r
+\r
+                       if(context.params.query.$select){\r
+                               //context.params.query.$select = context.params.query.$select.filter(elem => allowedFields.includes(elem));\r
+                               context.params.query.$select = context.params.query.$select.filter(elem => {\r
+                                       for(let i = 0; i < allowedFields.length; i++){\r
+                                               \r
+                                               //if there is dot notation, then it only looks at the parent variable name\r
+                                               elem = elem.toString().match(new RegExp(/^(\w+)/))[0];\r
+\r
+                                               if(allowedFields[i] == elem){\r
+                                                       return true;\r
+                                               }\r
+\r
+                                       };\r
+\r
+                                       return false;\r
+                               });\r
+                               \r
+\r
+\r
+                       }else{\r
+                               context.params.query.$select = allowedFields;\r
+                       }\r
+\r
+                       if(context.params.query.$select && context.params.query.$select.length == 0){\r
+                               context.params.query.$select = allowedFields;\r
+                       }\r
+\r
+                       if(!context.params.query.$select){\r
+                               context.params.query.$select = [];\r
+                       }\r
+                       //groupId is used for permissions conditions and must be selected\r
+                       if(!context.params.query.$select.includes('groupId')){\r
+                               context.params.query.$select.push('groupId');\r
+                       }\r
+                       \r
+                       return context;\r
+               }\r
+\r
+               const params = Object.assign({}, context.params, { provider: null });\r
+\r
+               const result = await service.get(context.id, params);\r
+               throwUnlessCan(action, result);\r
+\r
+               if (action === 'get') {\r
+                       context.result = pick(result, allowedFields);\r
+               }else{\r
+                       if(context.data){\r
+                               Object.keys(context.data).forEach(key => {\r
+                                       if(key == "$push"){\r
+                                               Object.keys(context.data['$push']).forEach(k => {\r
+                                                       throwUnlessCan(action, result, k);\r
+                                               });\r
+                                       }else{\r
+                                               throwUnlessCan(action, result, key);\r
+                                       }\r
+                               })\r
+                       }\r
+                       //context.data = pick(context.data, allowedFields);\r
+               }\r
+\r
+               return context;\r
+\r
+       }\r
+}\r
+\r
+makeObjectIdString = function(obj) {\r
+       for (var property in obj) {\r
+               if (obj.hasOwnProperty(property)) {\r
+                       if (typeof obj[property] == "object"){\r
+                               if(ObjectID.isValid(obj[property])) {\r
+                                       obj[property] = obj[property].toString()\r
+                               }else{\r
+                                       makeObjectIdString(obj[property]);\r
+                               }\r
+                       }\r
+               }\r
+       }\r
+}\r
+\r
+myPick = function(elem, allowedFields){\r
+       //when turning the object into dot notation, we loose the\r
+       makeObjectIdString(elem);\r
+\r
+       let d = dot.dot(elem);\r
+       let toPick = [];\r
+       Object.keys(d).forEach((key) => {\r
+               allowedFields.forEach((f, i) => {\r
+                       let r = '^' + f;\r
+                       if(key.replace(/\.([0-9]+)\./g, '.').match(new RegExp(r))){\r
+                               toPick.push(key);\r
+                       }\r
+               })\r
+       });\r
+       let picked = pick(d, toPick);\r
+       let obj = dot.object(picked)\r
+       return obj;\r
+}\r
+\r
+limitFields = function(){\r
+       return async context => {\r
+               if(context.result.data && context.result.data.length != undefined){\r
+                       //checkFields(context.params.ability, context.result.data, context.service.Model);\r
+                       context.result.data.forEach((elem, val) => {\r
+                               let instance = new context.service.Model(elem);\r
+                               const allowedFields = instance.accessibleFieldsBy(context.params.ability);\r
+                               //context.result.data[val] = pick(elem, allowedFields);\r
+                               context.result.data[val] = myPick(elem, allowedFields);\r
+                       });\r
+               }else if(context.result && context.result.length != undefined){\r
+                       context.result.forEach((elem, val) => {\r
+                               let instance = new context.service.Model(elem);\r
+                               const allowedFields = instance.accessibleFieldsBy(context.params.ability);\r
+                               //context.result[val] = pick(elem, allowedFields);\r
+                               context.result[val] = myPick(elem, allowedFields);\r
+                       });\r
+               }else if(context.result){\r
+                       //checkFields(context.params.ability, context.result, context.service.Model); \r
+                       let instance = new context.service.Model(context.result);\r
+                       let allowedFields = instance.accessibleFieldsBy(context.params.ability);\r
+                       //context.result = pick(context.result, allowedFields);\r
+                       context.result = myPick(context.result, allowedFields);\r
+               }\r
+       }\r
+}\r
+\r
+\r
+module.exports = {\r
+       permissions: permissions,\r
+       limitFields: limitFields\r
+}\r