index.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. (async() => {
  2. const Sequelize = require("sequelize");
  3. const Op = Sequelize.Op
  4. const sequelize = new Sequelize("test", "","",{
  5. host: 'localhost',
  6. dialect: 'mysql',
  7. pool: {
  8. max: 5,
  9. min: 0,
  10. idle: 10000
  11. },
  12. //logging: false
  13. });
  14. const User = sequelize.define("user", {
  15. login: Sequelize.STRING,
  16. password: Sequelize.STRING,
  17. })
  18. const Content = sequelize.define("content", {
  19. title: Sequelize.STRING,
  20. data: Sequelize.TEXT
  21. })
  22. async function filldb(){
  23. await sequelize.sync()
  24. console.log('synced')
  25. let [vasya, petya, kolya] = await Promise.all([
  26. User.create({login: "Vasya", password: "qwe"}),
  27. User.create({login: "Petya", password: "qwe"}),
  28. User.create({login: "Kolya", password: "qwe"}),
  29. ])
  30. let groupSlice = await Slice.create({
  31. permission: 'group',
  32. model: 'group',
  33. slice: [`${vasya.id}`, `${petya.id}`]
  34. })
  35. let [vasyaSlice, petyaSlice, kolyaSlice] = await Promise.all([
  36. Slice.create({model: 'user', permission: 'user', modelId: vasya.id, slice: ["user", `#${groupSlice.id}`]}),
  37. Slice.create({model: 'user', permission: 'user', modelId: petya.id, slice: ["user", `#${groupSlice.id}`]}),
  38. Slice.create({model: 'user', permission: 'user', modelId: kolya.id, slice: ["user" ]})
  39. ])
  40. let [hiddenContent, roleContent, groupContent] = await Promise.all([
  41. Content.create({title: 'Hidden', data: 'HIDDEN'}),
  42. Content.create({title: 'Role', data: 'ROLE'}),
  43. Content.create({title: 'Group', data: 'GROUP'}),
  44. ])
  45. let [hiddenSlice, roleSlice, groupContentSlice] = await Promise.all([
  46. Slice.create({model: 'contents', permission: 'read', modelId: hiddenContent.id, slice: [], ownerId: kolya.id}),
  47. Slice.create({model: 'contents', permission: 'read', modelId: roleContent.id, slice: ["user"], ownerId: vasya.id}),
  48. Slice.create({model: 'contents', permission: 'read', modelId: groupContent.id, slice: [`#${groupSlice.id}`], ownerId: petya.id}),
  49. ])
  50. let createSlice = await Slice.create({model: 'contents', permission: 'create', slice: ['user']})
  51. }
  52. //filldb()
  53. //
  54. const Slice = sequelize.define("slice",{
  55. permission: Sequelize.STRING, //create, update, delete, read, etc
  56. model: Sequelize.STRING,
  57. modelId: Sequelize.INTEGER,
  58. ownerId: Sequelize.INTEGER,
  59. //plain list of: "tags" like: admin, manager, user, anon, User can be tagged by this word in string list variable
  60. //OR: just userId.
  61. //OR, if negative number (or hash #100500) - other slice id (use abs to get proper table id)
  62. //this way optimizing
  63. slice: {type: Sequelize.TEXT, //PROBABLY STRING
  64. get(){
  65. if (this._slice) return this._slice
  66. let result = []
  67. for (let item of this.getDataValue("slice").split(",")){
  68. if (!result.includes(item)){
  69. result.push(item)
  70. }
  71. }
  72. this._slice = result;
  73. return this._slice
  74. },
  75. set(newValue){ //TODO: update users before with groups
  76. newValue = ("length" in newValue) ? newValue.join(",") : newValue
  77. return this.setDataValue("slice", newValue)
  78. }
  79. }
  80. },{
  81. getterMethods: {
  82. async all(){
  83. if (this._all) return this._all
  84. this._subSlicesList = this.slice.filter(id => id[0] == "#" || id[0] == "-") //detect - or #
  85. .map( id => +id.substr(1)) //cut first char + toInt
  86. this._roles = this.slice.filter(id => id[0] >= 'a' && id[0] <= 'z' )
  87. this._userIds = this.slice.filter(id => id[0] >= '0' && id[0] <= '9' )
  88. if (this._subSlicesList.length) {
  89. let subSlices = await Slice.findAll({where: {id: {[Op.in]: this._subSlicesList}}})
  90. if (subSlices) for (let subSlice of subSlices){
  91. this._subSlicesList = this._subSlicesList.filter(id => subSlice.id !== id)
  92. let subSliceAll = await subSlice.all
  93. this._subSlicesList = [...this._subSlicesList,... subSliceAll.slices]
  94. this._roles = [...this._roles, ...subSliceAll.roles]
  95. this._userIds = [...this._userIds, ...subSliceAll.userIds]
  96. }
  97. }
  98. this._all = {slices: this._subSlicesList, roles: this._roles, userIds: this._userIds}
  99. return this._all
  100. },
  101. async allRoles(){
  102. return (await this.all).roles
  103. },
  104. async allSlices(){
  105. return (await this.all).slices
  106. },
  107. async allUserIds(){
  108. return (await this.all).userIds
  109. },
  110. roles(){
  111. return this.slice.filter(id => id[0] >= 'a' && id[0] <= 'z' )
  112. },
  113. groups(){
  114. return this.slice.filter(id => id[0] == "#" || id[0] == "-") //detect - or #
  115. .map( id => +id.substr(1)) //cut first char + toInt
  116. }
  117. },
  118. indexes: [
  119. {
  120. fields: ["modelId", "model", "permission"]
  121. },
  122. ]
  123. })
  124. function sliced(model){
  125. return async userId => {
  126. let user = await User.findByPk(userId)
  127. if (!user) throw new ReferenceError(`user with userId ${userId} doesn't exists`)
  128. let userSlice = await Slice.findOne({where: {
  129. model: 'user', //TODO configure this
  130. modelId: userId,
  131. permission: 'user'
  132. }})
  133. let [userRoles, userGroups] = [userSlice.roles, userSlice.groups]
  134. //console.log(userRoles, userGroups)
  135. let mapMethodToPermission = {
  136. read: ["count", "findAll", "findAndCountAll", "findByPk", "findOne", "max", "min", "sum"],
  137. write: [ "destroy","update",],
  138. create: ["create", "findCreateFind", "findOrCreate","upsert" ]
  139. }
  140. function writeHook(instance, options){
  141. return sequelize.Promise.reject(new ReferenceError("No Permissions"));
  142. }
  143. //sequelize.addHook('beforeCreate', (...params) => console.log(params))
  144. let modelProxy = new Proxy(model, {
  145. get(model, method){
  146. let found = false
  147. for (var permission in mapMethodToPermission) {
  148. if (mapMethodToPermission[permission].includes(method)){
  149. found = true
  150. break;
  151. }
  152. }
  153. if (!found){
  154. console.log(`not found ${method}`)
  155. return model[method]
  156. }
  157. console.log('PERMISSION', permission)
  158. let checker = async slice => {
  159. if (!slice) return false
  160. console.log('CHECKER', 'slice ok')
  161. if (slice.ownerId === userId) return true
  162. console.log('CHECKER', 'not owner', await slice.allRoles, userRoles)
  163. let intersect = (await slice.allRoles).filter(role => userRoles.includes(role))
  164. console.log('CHECKER', intersect)
  165. return (intersect.length || (await slice.allUserIds).includes(userId + ''))
  166. }
  167. let wrapSave = instance => {
  168. let save = instance.save
  169. instance.save = async function(...params){
  170. let readSlice = await Slice.findOne({where: {
  171. model: model.getTableName(),
  172. modelId: instance.id,
  173. permission: 'read'
  174. }})
  175. if (!readSlice) throw ReferenceError('No access to write')
  176. if (readSlice.ownerId === userId){
  177. console.log('SAVE because owner')
  178. return save.apply(instance,params)
  179. }
  180. let writeSlice = await Slice.findOne({where: {
  181. model: model.getTableName(),
  182. modelId: instance.id,
  183. permission: 'write'
  184. }})
  185. if (await checker(writeSlice)){
  186. return save.apply(instance, params)
  187. }
  188. throw ReferenceError('No access to write')
  189. return null;
  190. }
  191. }
  192. let wrappers = {
  193. async read(...params){
  194. console.log('read wrapper')
  195. let result = await model[method](...params)
  196. if (result instanceof Array){
  197. let ids = result.map(instance => instance.id)
  198. let slices = await Slice.findAll({where: {
  199. model: model.getTableName(),
  200. modelId: {[Op.in]: ids},
  201. permission,
  202. }})
  203. let filteredResult = []
  204. for (let slice of slices){
  205. let instance = result.filter(instance => instance.id === slice.modelId)[0]
  206. //role check
  207. if (await checker(slice)) {
  208. wrapSave(instance)
  209. filteredResult.push(instance)
  210. continue;
  211. }
  212. }
  213. return filteredResult
  214. }
  215. if ('id' in result){ //one record
  216. let slice = await Slice.findOne({where: {
  217. model: model.getTableName(),
  218. modelId: result.id,
  219. permission,
  220. }})
  221. wrapSave(result)
  222. return (await checker(slice)) ? result : null;
  223. }
  224. },
  225. async create(...params){
  226. let createSlice = await Slice.findOne({where: {model: model.getTableName(),
  227. permission}})
  228. if (await checker(createSlice)){
  229. console.log('CHECKER YAYA')
  230. let result = await model[method](...params)
  231. if ('id' in result){ //new record
  232. let newReadSlice = await Slice.create({
  233. model: model.getTableName(),
  234. modelId: result.id,
  235. ownerId: userId,
  236. permission: 'read',
  237. slice: createSlice.slice, //default read permissions from create
  238. })
  239. wrapSave(result)
  240. return result
  241. }
  242. }
  243. return null
  244. },
  245. async write(...params){
  246. console.log('WRITE', method)
  247. }
  248. }
  249. return wrappers[permission]
  250. },
  251. })
  252. return modelProxy
  253. }
  254. }
  255. let SlicedContent = await sliced(Content)(2)
  256. console.log(JSON.stringify(await SlicedContent.findAll({}),null, 4))
  257. //let newContent = await SlicedContent.create({title: "SLiced", data: "SLICED"})
  258. //console.log(newContent)
  259. //
  260. let content = await SlicedContent.findByPk(7)
  261. content.data = `SLICED by WRITE permission for groupzzz #1`
  262. content.save()
  263. //newContent.data = `SLICED ${newContent.id}`
  264. //await newContent.save()
  265. let delay = ms => new Promise(r => setTimeout(r, ms))
  266. })()