diff --git a/src/controllers/notifications/controller.ts b/src/controllers/notifications/controller.ts index 8e03ef07e316758eaee7d83f717cc619017f3802..87e1f2845473d3dab07a376dc523f66883b16326 100644 --- a/src/controllers/notifications/controller.ts +++ b/src/controllers/notifications/controller.ts @@ -16,6 +16,7 @@ import { GetNotificationResponse, SendNotificationRequest } from './dto'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { API_KEY_ACCESS_ROLE } from '../../middleware/authorizationChecker'; import { StatusCodeDescriptions, StatusCodes } from '../../utils/status-codes'; +import { JSONSchema } from 'class-validator-jsonschema'; @JsonController('/notifications') export class NotificationsController { @@ -40,6 +41,26 @@ export class NotificationsController { return this.notificationsService.getById(notificationId, req.authorizationBag); } + @Authorized([process.env.INTERNAL_ROLE, process.env.SUPPORTER_ROLE]) + @OpenAPI({ + summary: 'Returns the audit of the Notification with the provided notification id.', + description: + 'This endpoint returns the audit of the notification with the provided notification id, if requester has access.', + operationId: 'getNotificationAuditById', + security: [{ oauth2: [] }], + responses: { + [StatusCodes.OK]: StatusCodeDescriptions[StatusCodes.OK], + [StatusCodes.NotFound]: StatusCodeDescriptions[StatusCodes.NotFound], + [StatusCodes.Forbidden]: StatusCodeDescriptions[StatusCodes.Forbidden], + [StatusCodes.Unauthorized]: StatusCodeDescriptions[StatusCodes.Unauthorized], + }, + }) + @ResponseSchema(JSONSchema, { description: 'Audit Notification requested.' }) + @Get('/:id/audit') + getNotificationAuditById(@Req() req, @Param('id') notificationId: string): Promise<JSON> { + return this.notificationsService.getNotificationAuditById(notificationId, req.authorizationBag); + } + @Authorized([process.env.INTERNAL_ROLE, process.env.VIEWER_ROLE, API_KEY_ACCESS_ROLE]) @OpenAPI({ summary: 'Send a Notification.', diff --git a/src/log/auditing.ts b/src/log/auditing.ts index 6ead5baad360263fa319e1defd77ea8bd07e6476..84f9d8b138df232f6109f2ece36c5433bafe3e59 100644 --- a/src/log/auditing.ts +++ b/src/log/auditing.ts @@ -1,6 +1,7 @@ import { Etcd3, SortTarget, SortOrder } from 'etcd3'; import { v4 as uuidv4 } from 'uuid'; import * as moment from 'moment'; +import { NotFoundError } from 'routing-controllers'; class BaseEtcd { private baseClient = new Etcd3({ @@ -70,28 +71,31 @@ class BaseEtcd { return allFValues; } - public async getValuesAsJson(prefix) { + public async getValuesAsJson(prefix): Promise<JSON> { if (!this.namespaceClient) { console.debug('etcd getValues namespaceClient is null'); return; } const allFValues = await this.getValues(prefix); + if (!allFValues) { + throw new NotFoundError('Audit does not exist.'); + } - var ret = {}; + const ret: JSON = {} as JSON; Object.keys(allFValues).forEach(function (key) { const value = allFValues[key]; - const splittedKeys = key.split('/'); + const splitKeys = key.split('/'); let tmpret = ret; - for (var j = 0; j < splittedKeys.length - 1; j++) { - if (splittedKeys[j] === '') continue; - if (!tmpret[splittedKeys[j]]) tmpret[splittedKeys[j]] = {}; - tmpret = tmpret[splittedKeys[j]]; + for (let j = 0; j < splitKeys.length - 1; j++) { + if (splitKeys[j] === '') continue; + if (!tmpret[splitKeys[j]]) tmpret[splitKeys[j]] = {}; + tmpret = tmpret[splitKeys[j]]; } try { - tmpret[splittedKeys[splittedKeys.length - 1]] = JSON.parse(value); + tmpret[splitKeys[splitKeys.length - 1]] = JSON.parse(value); } catch { - tmpret[splittedKeys[splittedKeys.length - 1]] = value; + tmpret[splitKeys[splitKeys.length - 1]] = value; } }); @@ -119,7 +123,19 @@ class AuditChannels extends BaseEtcd { } } -const i1 = AuditNotifications.Instance; -export { i1 as AuditNotifications }; -const i2 = AuditChannels.Instance; -export { i2 as AuditChannels }; +class AuditExternal extends BaseEtcd { + private static _instance: AuditExternal; + constructor() { + super('external/notifications'); + } + public static get Instance() { + return this._instance || (this._instance = new this()); + } +} + +const auditNotifications = AuditNotifications.Instance; +export { auditNotifications as AuditNotifications }; +const auditChannels = AuditChannels.Instance; +export { auditChannels as AuditChannels }; +const auditExternal = AuditExternal.Instance; +export { auditExternal as AuditExternal }; diff --git a/src/services/impl/notifications-service-impl.ts b/src/services/impl/notifications-service-impl.ts index 9fa1c0a6f50b30cf927231c32dd54a816bdb7b5d..743bf9bc613490a06fcdb19c4e853519898ab4d5 100644 --- a/src/services/impl/notifications-service-impl.ts +++ b/src/services/impl/notifications-service-impl.ts @@ -2,6 +2,7 @@ import { NotificationsService } from '../notifications-service'; import { SendNotification } from './notifications/send-notification'; import { FindAllNotifications } from './notifications/find-all-notifications'; import { GetById } from './notifications/get-by-id'; +import { GetNotificationAuditById } from './notifications/get-notification-audit-by-id'; import { AbstractService } from './abstract-service'; import { UserNotification } from '../../models/user-notification'; import { UpdateUserNotification } from './notifications/update-user-notification'; @@ -37,4 +38,8 @@ export class NotificationsServiceImpl extends AbstractService implements Notific updateUserNotification(notification: UserNotification, authorizationBag: AuthorizationBag): Promise<any> { return this.commandExecutor.execute(new UpdateUserNotification(notification, authorizationBag)); } + + getNotificationAuditById(notificationId: string, authorizationBag: AuthorizationBag): Promise<JSON> { + return this.commandExecutor.execute(new GetNotificationAuditById(notificationId, authorizationBag)); + } } diff --git a/src/services/impl/notifications/get-notification-audit-by-id.ts b/src/services/impl/notifications/get-notification-audit-by-id.ts new file mode 100644 index 0000000000000000000000000000000000000000..a53be1cbcb214d7bce3fa6556624b2f2f74f83fc --- /dev/null +++ b/src/services/impl/notifications/get-notification-audit-by-id.ts @@ -0,0 +1,26 @@ +import { Command } from '../command'; +import { AuthorizationBag } from '../../../models/authorization-bag'; +import { AuditExternal } from '../../../log/auditing'; +import { EntityManager } from 'typeorm'; +import { ForbiddenError, NotFoundError } from 'routing-controllers'; +import { Notification } from '../../../models/notification'; + +export class GetNotificationAuditById implements Command { + constructor(private notificationId: string, private authorizationBag: AuthorizationBag) {} + + async execute(transactionManager: EntityManager): Promise<JSON> { + const notification = await transactionManager.findOne(Notification, { + relations: ['target', 'target.owner', 'target.adminGroup'], + where: { + id: this.notificationId, + }, + }); + + if (!notification) throw new NotFoundError('Notification does not exist'); + + if (!(await notification.target.isAdmin(this.authorizationBag))) + throw new ForbiddenError("You don't have the rights to edit this channel."); + + return AuditExternal.getValuesAsJson(this.notificationId); + } +} diff --git a/src/services/notifications-service.ts b/src/services/notifications-service.ts index f1a58880496e970d175d3858365d72e2ba1036ee..d59819e402b878e8bd33de363fa8660ce5fb895a 100644 --- a/src/services/notifications-service.ts +++ b/src/services/notifications-service.ts @@ -20,4 +20,6 @@ export interface NotificationsService { getById(notificationId: string, authorizationBag: AuthorizationBag): Promise<GetNotificationResponse>; updateUserNotification(notification: UserNotification, authorizationBag: AuthorizationBag): Promise<any>; + + getNotificationAuditById(notificationId: string, authorizationBag: AuthorizationBag): Promise<JSON>; }