DB API Reads
This commit is contained in:
@@ -7,6 +7,10 @@ const uuid = require('uuid/v4');
|
||||
* Put some description here!
|
||||
*/
|
||||
class Node extends Model {
|
||||
static get services() {
|
||||
return [...super.services, 'models']
|
||||
}
|
||||
|
||||
static get schema() {
|
||||
// Return a flitter-orm schema here.
|
||||
return {
|
||||
@@ -26,7 +30,7 @@ class Node extends Model {
|
||||
|
||||
// Static and instance methods can go here
|
||||
get page() {
|
||||
const Page = this.model.get("api:Page")
|
||||
const Page = this.models.get('api:Page')
|
||||
return this.belongs_to_one(Page, "PageId", "_id")
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,64 @@ class Page extends Model {
|
||||
|
||||
static scopes = [new ActiveScope]
|
||||
|
||||
static async visible_by_user(user) {
|
||||
const user_root = await user.get_root_page()
|
||||
const user_root_children = await user_root.visible_flat_children(user)
|
||||
|
||||
const view_only_trees = await this.find({ shared_users_view: user._id })
|
||||
let view_only_children = []
|
||||
for ( const tree of view_only_trees ) {
|
||||
if ( await tree.is_accessible_by(user) ) {
|
||||
view_only_children.push(tree)
|
||||
view_only_children = [...view_only_children, await tree.visible_flat_children(user)]
|
||||
}
|
||||
}
|
||||
|
||||
const update_trees = await this.find({ shared_users_update: user._id })
|
||||
let update_children = []
|
||||
for ( const tree of update_trees ) {
|
||||
if ( await tree.is_accessible_by(user) ) {
|
||||
update_children.push(tree)
|
||||
update_children = [...update_children, await tree.visible_flat_children(user)]
|
||||
}
|
||||
}
|
||||
|
||||
const manage_trees = await this.find({ shared_users_manage: user._id })
|
||||
let manage_children = []
|
||||
for ( const tree of manage_trees ) {
|
||||
if ( await tree.is_accessible_by(user) ) {
|
||||
manage_children.push(tree)
|
||||
manage_children = [...manage_children, await tree.visible_flat_children(user)]
|
||||
}
|
||||
}
|
||||
|
||||
const all_children = [...user_root_children, ...view_only_children, ...update_children, ...manage_children]
|
||||
const unique_children = []
|
||||
const seen_UUIDs = []
|
||||
all_children.forEach(child => {
|
||||
if ( !seen_UUIDs.includes(child.UUID) ) {
|
||||
unique_children.push(child)
|
||||
seen_UUIDs.push(child.UUID)
|
||||
}
|
||||
})
|
||||
|
||||
return unique_children
|
||||
}
|
||||
|
||||
async visible_flat_children(user) {
|
||||
const children = await this.childPages
|
||||
let visible = []
|
||||
if ( children ) {
|
||||
for ( const child of children ) {
|
||||
if ( !(await child.is_accessible_by(user)) ) continue
|
||||
visible.push(child)
|
||||
visible = [...visible, ...(await child.visible_flat_children(user))]
|
||||
}
|
||||
}
|
||||
|
||||
return visible
|
||||
}
|
||||
|
||||
is_shared() {
|
||||
return this.shared_users_view.length > 0 || this.shared_users_update.length > 0 || this.shared_users_manage.length > 0
|
||||
}
|
||||
|
||||
95
app/models/api/Token.model.js
Normal file
95
app/models/api/Token.model.js
Normal file
@@ -0,0 +1,95 @@
|
||||
const Model = require('flitter-orm/src/model/Model')
|
||||
const { ObjectId } = require('mongodb')
|
||||
const User = require('../auth/User.model')
|
||||
const ValidScope = require('../scopes/Valid.scope')
|
||||
const uuid = require('uuid/v4')
|
||||
const jwt = require('jsonwebtoken')
|
||||
|
||||
/*
|
||||
* Token Model
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
class Token extends Model {
|
||||
static #default_grants = ['database']
|
||||
|
||||
static get services() {
|
||||
return [...super.services, 'configs']
|
||||
}
|
||||
|
||||
static get schema() {
|
||||
// Return a flitter-orm schema here.
|
||||
return {
|
||||
user_id: ObjectId,
|
||||
token: String,
|
||||
issued: { type: Date, default: () => new Date },
|
||||
expires: {
|
||||
type: Date,
|
||||
default: () => {
|
||||
const d = new Date
|
||||
d.setDate(d.getDate() + 7)
|
||||
return d
|
||||
},
|
||||
},
|
||||
valid: { type: Boolean, default: true },
|
||||
grants: [String],
|
||||
}
|
||||
}
|
||||
|
||||
static scopes = [new ValidScope]
|
||||
|
||||
static async create(user) {
|
||||
const token = new this({ user_id: user._id, grants: this.#default_grants })
|
||||
await token.save()
|
||||
token.token = token.generate()
|
||||
return await token.save()
|
||||
}
|
||||
|
||||
static async for_user(user) {
|
||||
const token = await this.findOne({ user_id: user._id })
|
||||
return token ? token : await this.create(user)
|
||||
}
|
||||
|
||||
static verify(token) {
|
||||
const secret = this.prototype.configs.get('server.session.secret')
|
||||
const server = this.prototype.configs.get('app.url')
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
jwt.verify(token, secret, (err, decoded) => {
|
||||
if ( err ) rej(err)
|
||||
else if ( decoded.iss !== server ) rej(new Error('Invalid token issuer: '+decoded.iss))
|
||||
else {
|
||||
this.findOne({ user_id: ObjectId(decoded.sub) }).then(result => {
|
||||
if ( !result ) rej(new Error('Unable to find associated token. It may have been manually invalidated.'))
|
||||
else res(result)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
generate() {
|
||||
const secret = this.configs.get('server.session.secret')
|
||||
const server = this.configs.get('app.url')
|
||||
|
||||
const payload = {
|
||||
sub: String(this.user_id),
|
||||
iss: server,
|
||||
permissions: this.grants.join(','),
|
||||
exp: (this.expires.getTime()/1000 | 0),
|
||||
iat: (this.issued.getTime()/1000 | 0),
|
||||
}
|
||||
|
||||
return jwt.sign(payload, secret)
|
||||
}
|
||||
|
||||
user() {
|
||||
return this.has_one(User, 'user_id', '_id')
|
||||
}
|
||||
|
||||
can(grant) {
|
||||
return this.grants.includes(grant)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = Token
|
||||
@@ -33,6 +33,16 @@ class ColumnDef extends Model {
|
||||
data() {
|
||||
return JSON.parse(this.additionalData ? this.additionalData : '{}')
|
||||
}
|
||||
|
||||
to_api_object() {
|
||||
return {
|
||||
name: this.headerName,
|
||||
uuid: this.UUID,
|
||||
database_id: this.DatabaseId,
|
||||
type: this.Type,
|
||||
metadata: this.data()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = ColumnDef
|
||||
|
||||
@@ -17,6 +17,22 @@ class DBEntry extends Model {
|
||||
}
|
||||
|
||||
// Static and instance methods can go here
|
||||
to_api_object() {
|
||||
return {
|
||||
uuid: this.UUID,
|
||||
database_id: this.DatabaseId,
|
||||
data: this.RowData,
|
||||
}
|
||||
}
|
||||
|
||||
static async from_cursor(cursor) {
|
||||
const arr = await cursor.toArray()
|
||||
const collection = []
|
||||
for ( const rec of arr ) {
|
||||
collection.push(new this(rec))
|
||||
}
|
||||
return collection
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = DBEntry
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
const Model = require('flitter-orm/src/model/Model')
|
||||
const uuid = require('uuid/v4')
|
||||
const ColumnDef = require('./ColumnDef.model')
|
||||
const Page = require('../Page.model')
|
||||
const Node = require('../Node.model')
|
||||
const ActiveScope = require('../../scopes/Active.scope')
|
||||
|
||||
/*
|
||||
* Database Model
|
||||
@@ -16,18 +19,50 @@ class Database extends Model {
|
||||
PageId: String,
|
||||
ColumnIds: [String],
|
||||
UUID: { type: String, default: () => uuid() },
|
||||
Active: { type: Boolean, default: true },
|
||||
}
|
||||
}
|
||||
|
||||
accessible_by(user, mode = 'view') {
|
||||
return user.can(`database:${this.UUID}:${mode}`)
|
||||
static scopes = [new ActiveScope]
|
||||
|
||||
static async visible_by_user(user) {
|
||||
const page_ids = (await Page.visible_by_user(user)).map(x => x.UUID)
|
||||
return this.find({PageId: {$in: page_ids}})
|
||||
}
|
||||
|
||||
async is_accessible_by(user, mode = 'view') {
|
||||
const page = await this.page
|
||||
return page.is_accessible_by(user, mode)
|
||||
}
|
||||
|
||||
async get_columns() {
|
||||
return ColumnDef.find({DatabaseId: this.UUID});
|
||||
const cols = await ColumnDef.find({DatabaseId: this.UUID})
|
||||
const assoc_cols = {}
|
||||
cols.forEach(col => assoc_cols[col.UUID] = col)
|
||||
return this.ColumnIds.map(x => assoc_cols[x])
|
||||
}
|
||||
|
||||
get page() {
|
||||
return this.belongs_to_one(Page, 'PageId', 'UUID')
|
||||
}
|
||||
|
||||
get node() {
|
||||
return this.belongs_to_one(Node, 'NodeId', 'UUID')
|
||||
}
|
||||
|
||||
async delete() {
|
||||
this.Active = false
|
||||
await this.save()
|
||||
}
|
||||
|
||||
to_api_object() {
|
||||
return {
|
||||
name: this.Name,
|
||||
uuid: this.UUID,
|
||||
page_id: this.PageId,
|
||||
column_ids: this.ColumnIds,
|
||||
}
|
||||
}
|
||||
|
||||
// Static and instance methods can go here
|
||||
}
|
||||
|
||||
module.exports = exports = Database
|
||||
|
||||
11
app/models/scopes/Valid.scope.js
Normal file
11
app/models/scopes/Valid.scope.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const Scope = require('flitter-orm/src/model/Scope')
|
||||
|
||||
class ValidScope extends Scope {
|
||||
async filter(to_filter) {
|
||||
return to_filter.equal('valid', true)
|
||||
.greater_than('expires', new Date)
|
||||
.less_than('issued', new Date)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = ValidScope
|
||||
Reference in New Issue
Block a user