Add system announcements interface

This commit is contained in:
garrettmills 2020-08-12 21:27:51 -05:00
parent 143fccf179
commit 1458e4126b
No known key found for this signature in database
GPG Key ID: 6ACD58D6ADACFC6E
9 changed files with 270 additions and 4 deletions

View File

@ -38,6 +38,7 @@ const template = `
<h6 class="dropdown-header">Hello, {{ first_name }}.</h6> <h6 class="dropdown-header">Hello, {{ first_name }}.</h6>
<a href="/dash/profile" class="dropdown-item">My Profile</a> <a href="/dash/profile" class="dropdown-item">My Profile</a>
<a href="/dash/c/listing/reflect/Token" v-if="can.api_tokens" class="dropdown-item">API Tokens</a> <a href="/dash/c/listing/reflect/Token" v-if="can.api_tokens" class="dropdown-item">API Tokens</a>
<a href="/dash/c/listing/system/Announcement" v-if="can.messages" class="dropdown-item">System Announcements</a>
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<a href="/auth/logout" class="dropdown-item">Sign-Out of {{ app_name }}</a> <a href="/auth/logout" class="dropdown-item">Sign-Out of {{ app_name }}</a>
</div> </div>
@ -64,6 +65,7 @@ export default class NavBarComponent extends Component {
async vue_on_create() { async vue_on_create() {
this.can.api_tokens = await session.check_permissions('v1:reflect:tokens:list') this.can.api_tokens = await session.check_permissions('v1:reflect:tokens:list')
this.can.messages = await session.check_permissions('v1:message:banners:create')
this.$forceUpdate() this.$forceUpdate()
} }

View File

@ -0,0 +1,98 @@
import CRUDBase from '../CRUDBase.js'
class AnnouncementResource extends CRUDBase {
endpoint = '/api/v1/system/announcements'
required_fields = ['user_ids', 'group_ids', 'title', 'message', 'type']
permission_base = 'v1:system:announcements'
item = 'System Announcement'
plural = 'System Announcements'
listing_definition = {
display: `
System announcements are administrative messages that you want all or some of your users to see. These messages can be delivered via e-mail, as a message after login, or as a system banner announcement.
`,
columns: [
{
name: 'Title',
field: 'title',
},
{
name: 'Message',
field: 'message',
renderer: (message) => String(message).slice(0, 150),
},
],
actions: [
{
type: 'resource',
position: 'main',
action: 'insert',
text: 'Create New',
color: 'success',
},
{
type: 'resource',
position: 'row',
action: 'delete',
icon: 'fa fa-times',
color: 'danger',
confirm: true,
},
],
}
form_definition = {
fields: [
{
name: 'Title',
field: 'title',
type: 'text',
},
{
name: 'Message',
field: 'message',
type: 'textarea',
},
{
name: 'Users',
field: 'user_ids',
type: 'select.dynamic.multiple',
options: {
resource: 'auth/User',
display: (user) => `${user.last_name}, ${user.first_name} (${user.uid})`,
value: 'id',
},
},
{
name: 'Groups',
field: 'group_ids',
type: 'select.dynamic.multiple',
options: {
resource: 'auth/Group',
display: (group) => `${group.name}`,
value: 'id',
},
},
{
name: 'Type',
field: 'type',
type: 'select',
options: [
{ display: 'Login Intercept', value: 'login' },
{ display: 'E-Mail', value: 'email' },
{ display: 'System Banner', value: 'banner' },
],
},
],
handlers: {
insert: {
action: 'redirect',
next: '/dash/c/listing/system/Announcement',
},
}
}
}
const system_announcement = new AnnouncementResource()
export { system_announcement }

View File

@ -40,6 +40,26 @@ class MessageController extends Controller {
await message.dismiss() await message.dismiss()
return res.api() return res.api()
} }
async create_banner(req, res, next) {
// expires, display_type = info, message, user_id?
const expires = req.body.expires
const display_type = req.body.display_type || 'info'
const message = req.body.message
const user_ids = req.body.user_ids
if ( !expires )
return res.status(400)
.message(`${req.T('api.missing_field')} expires`)
.api()
if ( !message )
return res.status(400)
.message(`${req.T('api.missing_field')} message`)
.api()
}
} }
module.exports = exports = MessageController module.exports = exports = MessageController

View File

@ -0,0 +1,84 @@
const { Controller } = require('libflitter')
class ReflectController extends Controller {
static get services() {
return [...super.services, 'routers', 'models', 'activity']
}
async get_announcements(req, res, next) {
const Announcement = this.models.get('system:Announcement')
const announcements = await Announcement.find()
const data = []
for ( const announcement of announcements ) {
data.push(await announcement.to_api())
}
return res.api(data)
}
async get_announcement(req, res, next) {
const Announcement = this.models.get('system:Announcement')
const announcement = await Announcement.findById(req.params.id)
if ( !announcement )
return res.status(404)
.message(req.T('api.announcement_not_found'))
.api()
return res.api(await announcement.to_api())
}
async create_announcement(req, res, next) {
const Announcement = this.models.get('system:Announcement')
const required_fields = ['title', 'message', 'user_ids', 'group_ids', 'type']
for ( const field of required_fields ) {
if ( !req.body[field] )
return res.status(400)
.message(`${req.T('api.missing_field')} ${field}`)
.api()
}
if ( !Array.isArray(req.body.user_ids) )
return res.status(400)
.message(`${req.T('api.improper_field')} user_ids`)
.api()
if ( !Array.isArray(req.body.group_ids) )
return res.status(400)
.message(`${req.T('api.improper_field')} group_ids`)
.api()
if ( !['email', 'login', 'banner'].includes(req.body.type) )
return res.status(400)
.message(`${req.T('api.improper_field')} type`)
.api()
const announcement = new Announcement({
title: req.body.title,
message: req.body.message,
user_ids: req.body.user_ids,
group_ids: req.body.group_ids,
type: req.body.type,
})
await announcement.save()
return res.api(await announcement.to_api())
}
async delete_announcement(req, res, next) {
const Announcement = this.models.get('system:Announcement')
const announcement = await Announcement.findById(req.params.id)
if ( !announcement )
return res.status(404)
.message(req.T('api.announcement_not_found'))
.api()
await announcement.delete()
return res.api()
}
}
module.exports = exports = ReflectController

View File

@ -24,10 +24,6 @@ class ClientModel extends Model {
} }
} }
can(scope) {
return this.api_scopes.includes()
}
async application() { async application() {
const Application = this.models.get('Application') const Application = this.models.get('Application')
return Application.findOne({ active: true, oauth_client_ids: this.id }) return Application.findOne({ active: true, oauth_client_ids: this.id })

View File

@ -0,0 +1,26 @@
const { Model } = require('flitter-orm')
class AnnouncementModel extends Model {
static get schema() {
return {
title: String,
message: String,
user_ids: [String],
group_ids: [String],
type: String, // login | email | banner
}
}
async to_api() {
return {
id: this.id,
title: this.title,
message: this.message,
user_ids: this.user_ids,
group_ids: this.group_ids,
type: this.type,
}
}
}
module.exports = exports = AnnouncementModel

View File

@ -17,6 +17,10 @@ const message_routes = {
['middleware::api:Permission', { check: 'v1:message:banners:update' }], ['middleware::api:Permission', { check: 'v1:message:banners:update' }],
'controller::api:v1:Message.read_banner', 'controller::api:v1:Message.read_banner',
], ],
'/banners': [
['middleware::api:Permission', { check: 'v1:message:banners:create' }],
'controller::api:v1:Message.create_banner',
],
}, },
} }

View File

@ -0,0 +1,34 @@
const system_routes = {
prefix: '/api/v1/system',
middleware: [
'auth:APIRoute'
],
get: {
'/announcements': [
['middleware::api:Permission', { check: 'v1:system:announcements:list' }],
'controller::api:v1:System.get_announcements',
],
'/announcements/:id': [
['middleware::api:Permission', { check: 'v1:system:announcements:get' }],
'controller::api:v1:System.get_announcement',
],
},
post: {
'/announcements': [
['middleware::api:Permission', { check: 'v1:system:announcements:create'}],
'controller::api:v1:System.create_announcement',
],
},
delete: {
'/announcements/:id': [
['middleware::api:Permission', { check: 'v1:system:announcements:delete' }],
'controller::api:v1:System.delete_announcement',
],
},
}
module.exports = exports = system_routes

View File

@ -21,6 +21,8 @@ module.exports = exports = {
app_pw_not_found: 'App password not found with that UUID.', app_pw_not_found: 'App password not found with that UUID.',
announcement_not_found: 'Announcement not found with that ID.',
invalid_ldap_client_id: 'Invalid ldap_client_id:', invalid_ldap_client_id: 'Invalid ldap_client_id:',
invalid_oauth_client_id: 'Invalid oauth_client_id:', invalid_oauth_client_id: 'Invalid oauth_client_id:',
invalid_saml_service_provider_id: 'Invalid saml_service_provider_id:', invalid_saml_service_provider_id: 'Invalid saml_service_provider_id:',