parent
b427a16601
commit
46f60a671a
@ -0,0 +1,74 @@
|
||||
const Controller = require('libflitter/controller/Controller')
|
||||
|
||||
/*
|
||||
* Sharing Controller
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
class Sharing extends Controller {
|
||||
static get services() {
|
||||
return [...super.services, 'models']
|
||||
}
|
||||
|
||||
async share_page(req, res) {
|
||||
const level = req.form.level
|
||||
await req.form.page.share_with(req.form.user, level)
|
||||
return res.api({})
|
||||
}
|
||||
|
||||
async revoke_page(req, res) {
|
||||
await req.form.page.unshare_with(req.form.user)
|
||||
return res.api({})
|
||||
}
|
||||
|
||||
async page_info(req, res) {
|
||||
const data = {
|
||||
view: (await req.form.page.view_users).map(x => {
|
||||
return {username: x.uid, id: x.id, level: 'view'}
|
||||
}),
|
||||
update: (await req.form.page.update_users).map(x => {
|
||||
return {username: x.uid, id: x.id, level: 'update'}
|
||||
}),
|
||||
manage: (await req.form.page.manage_users).map(x => {
|
||||
return {username: x.uid, id: x.id, level: 'manage'}
|
||||
}),
|
||||
}
|
||||
|
||||
return res.api(data)
|
||||
}
|
||||
|
||||
async get_link(req, res) {
|
||||
const KeyAction = this.models.get('auth:KeyAction')
|
||||
const in_1_week = new Date
|
||||
in_1_week.setDate(in_1_week.getDate() + 7)
|
||||
|
||||
const action = new KeyAction({
|
||||
handler: 'controller::api:v1:Sharing.accept_link',
|
||||
expires: in_1_week,
|
||||
auto_login: false,
|
||||
no_auto_logout: true, // THIS IS FINE. It's because the MW requires a traditional sign-in.
|
||||
})
|
||||
|
||||
await action.save()
|
||||
action.data_set('level', req.form.level)
|
||||
action.data_set('PageId', req.form.page.UUID)
|
||||
await action.save()
|
||||
|
||||
return res.api({ link: action.auth_url() })
|
||||
}
|
||||
|
||||
async accept_link(req, res) {
|
||||
if ( !req.user ) return req.security.kickout()
|
||||
const Page = this.models.get('api:Page')
|
||||
const PageId = req.key_action.data_get('PageId')
|
||||
const level = req.key_action.data_get('level')
|
||||
|
||||
const page = await Page.findOne({UUID: PageId})
|
||||
await page.share_with(req.user, level)
|
||||
|
||||
return res.redirect(`/i/editor;id=${PageId}`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = exports = Sharing
|
@ -0,0 +1,40 @@
|
||||
const Middleware = require('libflitter/middleware/Middleware')
|
||||
|
||||
/*
|
||||
* PageRoute Middleware
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
class PageRoute extends Middleware {
|
||||
static get services() {
|
||||
return [...super.services, 'models']
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the middleware test.
|
||||
* This method is required by all Flitter middleware.
|
||||
* It should either call the next function in the stack,
|
||||
* or it should handle the response accordingly.
|
||||
*/
|
||||
async test(req, res, next, args = {}){
|
||||
const Page = this.models.get('api:Page')
|
||||
const PageId = req.form.PageId ? req.form.PageId : req.params.PageId
|
||||
if ( !PageId ) return res.status(400).message(`Missing PageId.`).api({})
|
||||
|
||||
const level = args.level ? args.level : 'view'
|
||||
const page = await Page.findOne({UUID: PageId})
|
||||
|
||||
if ( !page ) return res.status(404).message(`Unable to find page with that id.`).api({})
|
||||
if ( !(await page.is_accessible_by(req.user, level)) ) return req.security.deny()
|
||||
|
||||
if ( !req.form ) req.form = {}
|
||||
req.form.page = page
|
||||
|
||||
/*
|
||||
* Call the next function in the stack.
|
||||
*/
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = PageRoute
|
@ -0,0 +1,87 @@
|
||||
const Middleware = require('libflitter/middleware/Middleware')
|
||||
|
||||
/*
|
||||
* RequiredFields Middleware
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
class RequiredFields extends Middleware {
|
||||
static get services() {
|
||||
return [...super.services, 'configs', 'output', 'utility']
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the middleware test.
|
||||
* This method is required by all Flitter middleware.
|
||||
* It should either call the next function in the stack,
|
||||
* or it should handle the response accordingly.
|
||||
*/
|
||||
async test(req, res, next, args){
|
||||
// Do stuff here
|
||||
const search_fields = args.search ? (Array.isArray(args.search) ? args.search : [args.search]) : ['params', 'body', 'query']
|
||||
const form_config = this.configs.get('api:forms:'+args.form)
|
||||
const values = {}
|
||||
|
||||
for ( const field in form_config.fields ) {
|
||||
if ( !form_config.fields.hasOwnProperty(field) ) continue
|
||||
const field_config = form_config.fields[field]
|
||||
let field_value = this.get_field({ request: req, field, search_fields })
|
||||
|
||||
if ( !field_value ) {
|
||||
if ( field_config.required ) {
|
||||
return this.fail({ response: res, reason: `Missing required field: ${field}`})
|
||||
}
|
||||
} else {
|
||||
if ( field_config.infer !== false ) {
|
||||
field_value = this.utility.infer(field_value)
|
||||
}
|
||||
|
||||
if ( field_config.coerce ) {
|
||||
field_value = field_config.coerce(field_value)
|
||||
|
||||
if ( field_config.coerce === Number && isNaN(field_value) ) {
|
||||
return this.fail({ response: res, reason: 'Invalid numerical value for field: '+field })
|
||||
}
|
||||
}
|
||||
|
||||
if ( field_config.in_set ) {
|
||||
if ( Array.isArray(field_config.in_set) ) {
|
||||
if ( !field_config.in_set.includes(field_value) ) {
|
||||
return this.fail({ response: res, reason: `Invalid value for ${field}. Value must be one of: ${field_config.join(', ')}`})
|
||||
}
|
||||
} else {
|
||||
this.output.warn(`[Middleware RequiredFields] Invalid in_set for ${field} in ${args.form}. Must be array.`)
|
||||
}
|
||||
}
|
||||
|
||||
values[field] = field_value
|
||||
}
|
||||
}
|
||||
|
||||
req.form = values
|
||||
|
||||
/*
|
||||
* Call the next function in the stack.
|
||||
*/
|
||||
next()
|
||||
}
|
||||
|
||||
get_field({ request, field, search_fields }) {
|
||||
for ( const search_field of search_fields ) {
|
||||
if ( Object.keys(request).includes(search_field) ) {
|
||||
if ( Object.keys(request[search_field]).includes(field) ) {
|
||||
return request[search_field][field]
|
||||
}
|
||||
} else {
|
||||
this.output.warn(`[Middleware RequiredFields] Requested search of request field that does not exist: ${search_field}`)
|
||||
this.output.debug(`[Middleware RequiredFields] Available request keys: ${Object.keys(request).join(', ')}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fail({ response, reason }) {
|
||||
return response.status(400).message(reason).api({})
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = RequiredFields
|
@ -0,0 +1,35 @@
|
||||
const Middleware = require('libflitter/middleware/Middleware')
|
||||
|
||||
/*
|
||||
* UserRoute Middleware
|
||||
* -------------------------------------------------------------
|
||||
* Put some description here!
|
||||
*/
|
||||
class UserRoute extends Middleware {
|
||||
static get services() {
|
||||
return [...super.services, 'models']
|
||||
}
|
||||
|
||||
/*
|
||||
* Run the middleware test.
|
||||
* This method is required by all Flitter middleware.
|
||||
* It should either call the next function in the stack,
|
||||
* or it should handle the response accordingly.
|
||||
*/
|
||||
async test(req, res, next, args = {}){
|
||||
const User = this.models.get('auth:User')
|
||||
const user_id = req.form.user_id ? req.form.user_id : req.params.user_id
|
||||
|
||||
if ( !user_id ) return res.status(400).message('Midding user_id.').api({})
|
||||
|
||||
const user = await User.findById(user_id)
|
||||
if ( !user ) return res.status(404).message('Unable to find user with that ID.').api({})
|
||||
|
||||
if ( !req.form ) req.form = {}
|
||||
req.form.user = user
|
||||
|
||||
next()
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = UserRoute
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* API v1 Routes
|
||||
* -------------------------------------------------------------
|
||||
* Description here
|
||||
*/
|
||||
const index = {
|
||||
|
||||
prefix: '/api/v1/share',
|
||||
|
||||
middleware: [
|
||||
'auth:UserOnly',
|
||||
],
|
||||
|
||||
get: {
|
||||
'/page/:PageId/info': [
|
||||
['middleware::api:RequiredFields', { form: 'sharing.page' }],
|
||||
['middleware::api:PageRoute', {level: 'manage'}],
|
||||
'controller::api:v1:Sharing.page_info',
|
||||
],
|
||||
'/page/:PageId/link/:level': [
|
||||
['middleware::api:RequiredFields', { form: 'sharing.page_link'}],
|
||||
['middleware::api:PageRoute', {level: 'manage'}],
|
||||
'controller::api:v1:Sharing.get_link',
|
||||
],
|
||||
},
|
||||
|
||||
post: {
|
||||
// Share a page with the specified user.
|
||||
'/page/:PageId/share': [
|
||||
['middleware::api:RequiredFields', { form: 'sharing.page_level' }],
|
||||
['middleware::api:PageRoute', {level: 'manage'}],
|
||||
'middleware::api:UserRoute',
|
||||
'controller::api:v1:Sharing.share_page',
|
||||
],
|
||||
|
||||
// Unshare a page with the specified user.
|
||||
'/page/:PageId/revoke': [
|
||||
['middleware::api:RequiredFields', { form: 'sharing.page_user' }],
|
||||
['middleware::api:PageRoute', {level: 'manage'}],
|
||||
'middleware::api:UserRoute',
|
||||
'controller::api:v1:Sharing.revoke_page',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
module.exports = exports = index
|
@ -0,0 +1,55 @@
|
||||
module.exports = exports = {
|
||||
page: {
|
||||
fields: {
|
||||
PageId: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
page_level: {
|
||||
fields: {
|
||||
PageId: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
},
|
||||
user_id: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
},
|
||||
level: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
in_set: ['view', 'update', 'manage'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
page_link: {
|
||||
fields: {
|
||||
PageId: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
},
|
||||
level: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
in_set: ['view', 'update', 'manage'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
page_user: {
|
||||
fields: {
|
||||
PageId: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
},
|
||||
user_id: {
|
||||
required: true,
|
||||
coerce: String,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
Loading…
Reference in new issue