123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- (async() => {
- const Sequelize = require("sequelize");
- const Op = Sequelize.Op
- const sequelize = new Sequelize("test", "","",{
- host: 'localhost',
- dialect: 'mysql',
- pool: {
- max: 5,
- min: 0,
- idle: 10000
- },
- //logging: false
- });
- const User = sequelize.define("user", {
- login: Sequelize.STRING,
- password: Sequelize.STRING,
- })
- const Content = sequelize.define("content", {
- title: Sequelize.STRING,
- data: Sequelize.TEXT
- })
- async function filldb(){
- await sequelize.sync()
- console.log('synced')
- let [vasya, petya, kolya] = await Promise.all([
- User.create({login: "Vasya", password: "qwe"}),
- User.create({login: "Petya", password: "qwe"}),
- User.create({login: "Kolya", password: "qwe"}),
- ])
- let groupSlice = await Slice.create({
- permission: 'group',
- model: 'group',
- slice: [`${vasya.id}`, `${petya.id}`]
- })
- let [vasyaSlice, petyaSlice, kolyaSlice] = await Promise.all([
- Slice.create({model: 'user', permission: 'user', modelId: vasya.id, slice: ["user", `#${groupSlice.id}`]}),
- Slice.create({model: 'user', permission: 'user', modelId: petya.id, slice: ["user", `#${groupSlice.id}`]}),
- Slice.create({model: 'user', permission: 'user', modelId: kolya.id, slice: ["user" ]})
- ])
- let [hiddenContent, roleContent, groupContent] = await Promise.all([
- Content.create({title: 'Hidden', data: 'HIDDEN'}),
- Content.create({title: 'Role', data: 'ROLE'}),
- Content.create({title: 'Group', data: 'GROUP'}),
- ])
- let [hiddenSlice, roleSlice, groupContentSlice] = await Promise.all([
- Slice.create({model: 'contents', permission: 'read', modelId: hiddenContent.id, slice: [], ownerId: kolya.id}),
- Slice.create({model: 'contents', permission: 'read', modelId: roleContent.id, slice: ["user"], ownerId: vasya.id}),
- Slice.create({model: 'contents', permission: 'read', modelId: groupContent.id, slice: [`#${groupSlice.id}`], ownerId: petya.id}),
- ])
- let createSlice = await Slice.create({model: 'contents', permission: 'create', slice: ['user']})
- }
- //filldb()
- //
-
- const Slice = sequelize.define("slice",{
- permission: Sequelize.STRING, //create, update, delete, read, etc
- model: Sequelize.STRING,
- modelId: Sequelize.INTEGER,
- ownerId: Sequelize.INTEGER,
- //plain list of: "tags" like: admin, manager, user, anon, User can be tagged by this word in string list variable
- //OR: just userId.
- //OR, if negative number (or hash #100500) - other slice id (use abs to get proper table id)
- //this way optimizing
- slice: {type: Sequelize.TEXT, //PROBABLY STRING
- get(){
- if (this._slice) return this._slice
- let result = []
- for (let item of this.getDataValue("slice").split(",")){
- if (!result.includes(item)){
- result.push(item)
- }
- }
- this._slice = result;
- return this._slice
- },
- set(newValue){ //TODO: update users before with groups
- newValue = ("length" in newValue) ? newValue.join(",") : newValue
- return this.setDataValue("slice", newValue)
- }
- }
- },{
- getterMethods: {
- async all(){
- if (this._all) return this._all
- this._subSlicesList = this.slice.filter(id => id[0] == "#" || id[0] == "-") //detect - or #
- .map( id => +id.substr(1)) //cut first char + toInt
- this._roles = this.slice.filter(id => id[0] >= 'a' && id[0] <= 'z' )
- this._userIds = this.slice.filter(id => id[0] >= '0' && id[0] <= '9' )
- if (this._subSlicesList.length) {
- let subSlices = await Slice.findAll({where: {id: {[Op.in]: this._subSlicesList}}})
- if (subSlices) for (let subSlice of subSlices){
- this._subSlicesList = this._subSlicesList.filter(id => subSlice.id !== id)
- let subSliceAll = await subSlice.all
- this._subSlicesList = [...this._subSlicesList,... subSliceAll.slices]
- this._roles = [...this._roles, ...subSliceAll.roles]
- this._userIds = [...this._userIds, ...subSliceAll.userIds]
- }
- }
- this._all = {slices: this._subSlicesList, roles: this._roles, userIds: this._userIds}
- return this._all
- },
- async allRoles(){
- return (await this.all).roles
- },
- async allSlices(){
- return (await this.all).slices
- },
- async allUserIds(){
- return (await this.all).userIds
- },
- roles(){
- return this.slice.filter(id => id[0] >= 'a' && id[0] <= 'z' )
- },
- groups(){
- return this.slice.filter(id => id[0] == "#" || id[0] == "-") //detect - or #
- .map( id => +id.substr(1)) //cut first char + toInt
- }
- },
- indexes: [
- {
- fields: ["modelId", "model", "permission"]
- },
- ]
- })
- function sliced(model){
- return async userId => {
- let user = await User.findByPk(userId)
- if (!user) throw new ReferenceError(`user with userId ${userId} doesn't exists`)
- let userSlice = await Slice.findOne({where: {
- model: 'user', //TODO configure this
- modelId: userId,
- permission: 'user'
- }})
- let [userRoles, userGroups] = [userSlice.roles, userSlice.groups]
- //console.log(userRoles, userGroups)
- let mapMethodToPermission = {
- read: ["count", "findAll", "findAndCountAll", "findByPk", "findOne", "max", "min", "sum"],
- write: [ "destroy","update",],
- create: ["create", "findCreateFind", "findOrCreate","upsert" ]
- }
- function writeHook(instance, options){
- return sequelize.Promise.reject(new ReferenceError("No Permissions"));
- }
- //sequelize.addHook('beforeCreate', (...params) => console.log(params))
- let modelProxy = new Proxy(model, {
- get(model, method){
- let found = false
- for (var permission in mapMethodToPermission) {
- if (mapMethodToPermission[permission].includes(method)){
- found = true
- break;
- }
- }
- if (!found){
- console.log(`not found ${method}`)
- return model[method]
- }
- console.log('PERMISSION', permission)
- let checker = async slice => {
- if (!slice) return false
- console.log('CHECKER', 'slice ok')
- if (slice.ownerId === userId) return true
- console.log('CHECKER', 'not owner', await slice.allRoles, userRoles)
- let intersect = (await slice.allRoles).filter(role => userRoles.includes(role))
- console.log('CHECKER', intersect)
- return (intersect.length || (await slice.allUserIds).includes(userId + ''))
- }
- let wrapSave = instance => {
- let save = instance.save
- instance.save = async function(...params){
- let readSlice = await Slice.findOne({where: {
- model: model.getTableName(),
- modelId: instance.id,
- permission: 'read'
- }})
- if (!readSlice) throw ReferenceError('No access to write')
- if (readSlice.ownerId === userId){
- console.log('SAVE because owner')
- return save.apply(instance,params)
- }
- let writeSlice = await Slice.findOne({where: {
- model: model.getTableName(),
- modelId: instance.id,
- permission: 'write'
- }})
- if (await checker(writeSlice)){
- return save.apply(instance, params)
- }
- throw ReferenceError('No access to write')
- return null;
- }
- }
- let wrappers = {
- async read(...params){
- console.log('read wrapper')
- let result = await model[method](...params)
- if (result instanceof Array){
- let ids = result.map(instance => instance.id)
- let slices = await Slice.findAll({where: {
- model: model.getTableName(),
- modelId: {[Op.in]: ids},
- permission,
- }})
- let filteredResult = []
- for (let slice of slices){
- let instance = result.filter(instance => instance.id === slice.modelId)[0]
- //role check
- if (await checker(slice)) {
- wrapSave(instance)
- filteredResult.push(instance)
- continue;
- }
- }
- return filteredResult
- }
- if ('id' in result){ //one record
- let slice = await Slice.findOne({where: {
- model: model.getTableName(),
- modelId: result.id,
- permission,
- }})
- wrapSave(result)
- return (await checker(slice)) ? result : null;
- }
- },
- async create(...params){
- let createSlice = await Slice.findOne({where: {model: model.getTableName(),
- permission}})
- if (await checker(createSlice)){
- console.log('CHECKER YAYA')
- let result = await model[method](...params)
- if ('id' in result){ //new record
- let newReadSlice = await Slice.create({
- model: model.getTableName(),
- modelId: result.id,
- ownerId: userId,
- permission: 'read',
- slice: createSlice.slice, //default read permissions from create
- })
- wrapSave(result)
- return result
- }
- }
- return null
- },
- async write(...params){
- console.log('WRITE', method)
- }
- }
- return wrappers[permission]
- },
- })
- return modelProxy
- }
- }
- let SlicedContent = await sliced(Content)(2)
- console.log(JSON.stringify(await SlicedContent.findAll({}),null, 4))
- //let newContent = await SlicedContent.create({title: "SLiced", data: "SLICED"})
- //console.log(newContent)
- //
- let content = await SlicedContent.findByPk(7)
- content.data = `SLICED by WRITE permission for groupzzz #1`
- content.save()
- //newContent.data = `SLICED ${newContent.id}`
- //await newContent.save()
- let delay = ms => new Promise(r => setTimeout(r, ms))
- })()
|