diff --git a/debian/changelog b/debian/changelog index 322639d6..1641245c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,102 @@ +ngcp-rest-api (10.5.9.1+0~mr10.5.9.1) unstable; urgency=medium + + * New release. + + -- Sipwise Jenkins Builder Tue, 29 Jul 2025 11:32:40 +0200 + +ngcp-rest-api (10.5.9.0+0~mr10.5.9.0) unstable; urgency=medium + + * New release. + + -- Sipwise Jenkins Builder Wed, 16 Oct 2024 11:06:28 +0200 + +ngcp-rest-api (10.5.8.0+0~mr10.5.8.0) unstable; urgency=medium + + * New release. + + -- Sipwise Jenkins Builder Mon, 06 May 2024 14:05:15 +0200 + +ngcp-rest-api (10.5.7.0+0~mr10.5.7.0) unstable; urgency=medium + + [ Kirill Solomko ] + * [9682300] MT#57927 use Debian node-gyp package for bindings + + -- Sipwise Jenkins Builder Mon, 20 Nov 2023 16:38:25 +0100 + +ngcp-rest-api (10.5.6.0+0~mr10.5.6.0) unstable; urgency=medium + + * New release. + + -- Sipwise Jenkins Builder Mon, 24 Jul 2023 12:20:59 +0200 + +ngcp-rest-api (10.5.5.0+0~mr10.5.5.0) unstable; urgency=medium + + * New release. + + -- Sipwise Jenkins Builder Wed, 15 Mar 2023 16:09:33 +0100 + +ngcp-rest-api (10.5.4.0+0~mr10.5.4.0) unstable; urgency=medium + + * New release. + + -- Sipwise Jenkins Builder Thu, 22 Dec 2022 16:20:48 +0100 + +ngcp-rest-api (10.5.3.0+0~mr10.5.3.0) unstable; urgency=medium + + [ Kirill Solomko ] + * [e30dafe] TT#187200 adjust /api/pbxgroups repo and response + * [b7ffaae] TT#180554 /api/admins allow ccare roles GET + + -- Sipwise Jenkins Builder Mon, 19 Sep 2022 14:37:04 +0200 + +ngcp-rest-api (10.5.2.0+0~mr10.5.2.0) unstable; urgency=medium + + [ Kirill Solomko ] + * [6173340] TT#181250 /api/journals return role name instead of role_id + * [e812fec] TT#181251 refactor journaling.interceptor to service + + -- Sipwise Jenkins Builder Wed, 29 Jun 2022 17:27:58 +0200 + +ngcp-rest-api (10.5.1.0+0~mr10.5.1.0) unstable; urgency=medium + + [ Michael Berger ] + * [c8dad2f] TT#168700 rename db entities and repositories + * [7ed027b] TT#168400 rework /api/domains to repository pattern + * [a15e667] TT#168400 add internal domain tests + * [9ed6c2f] TT#169150 implement endpoint /api/pbxgroups + * [d0661f3] TT#168700 fix error admin repo readById method + * [d95e64e] TT#170150 correctly update admin role in PATCH + * [2e69527] TT#170200 implement wildcard search + * [fe7684f] TT#170201 implement response ordering + * [a484696] TT#170454 improve search logic + * [d26c870] TT#170652 add pagination for OpenAPI + * [16bcd58] TT#125651 improve formatting and imports + * [940e83f] TT#170901 conditionally filter out reseller_id from admin response + * [3881238] TT#170902 improve json+hal return representation + * [aad467a] TT#170950 add debug logging in controller methods + * [393314c] TT#170955 fix circular dependency in PaginatedDto + * [11be078] TT#167151 add support for 'prefer' header + * [5f408de] TT#125651 rename enums to be singular + * [b876246] TT#169756 refactor admins service + * [1ad17a4] TT#171100 improve admins password validation + * [41b4e57] TT#171350 create resource with PUT in admins + * [103f1ca] TT#171350 simplify legacy put config + * [b21c018] TT#172850 fix error in SearchLogic + * [dc79f87] TT#174404 rework journals to use repository pattern + * [2f8f4a5] TT#176250 improve pbxgroups endpoint + * [2d8d769] TT#178250 improve password validation + * [d52c0fd] TT#178900 add legacy config option for errors + + [ Kirill Solomko ] + * [711745a] TT#169150 fix pbxgroups-response swagger circular dependency + * [eb50562] TT#169768 improve total_count support + * [434fb90] TT#125651 journals decode JSON content + + [ Michael Prokop ] + * [4a3f259] TT#76552 Harden ngcp-rest-api service + + -- Sipwise Jenkins Builder Tue, 07 Jun 2022 11:44:31 +0200 + ngcp-rest-api (10.5.0.0+0~mr10.5.0.0) unstable; urgency=medium [ Kirill Solomko ] diff --git a/debian/control b/debian/control index 124494bc..5374dd93 100644 --- a/debian/control +++ b/debian/control @@ -6,6 +6,7 @@ Build-Depends: debhelper-compat (= 13), gyp, libsystemd-dev, + node-gyp, yarnpkg | nodejs (>= 12.19.0~), yarnpkg | yarn, Standards-Version: 4.5.1 diff --git a/debian/rules b/debian/rules index 1384dc11..79928706 100755 --- a/debian/rules +++ b/debian/rules @@ -10,6 +10,7 @@ YARN_BIN := $(shell which yarnpkg || echo yarn) dh $@ override_dh_auto_install: + $(YARN_BIN) cache clean $(YARN_BIN) install $(YARN_BIN) build:prod diff --git a/src/api/admins/admins.controller.ts b/src/api/admins/admins.controller.ts index 84608a3e..80577a39 100644 --- a/src/api/admins/admins.controller.ts +++ b/src/api/admins/admins.controller.ts @@ -34,7 +34,6 @@ import {RbacRole} from '../../config/constants.config' import {number} from 'yargs' import {PatchDto} from '../patch.dto' import {Request} from 'express' -import {JournalingInterceptor} from '../../interceptors/journaling.interceptor' import {CrudController} from '../../controllers/crud.controller' import {AdminSearchDto} from './dto/admin-search.dto' import {ExpandHelper} from '../../helpers/expand.helper' @@ -47,7 +46,6 @@ const resourceName = 'admins' @ApiTags('Admins') @ApiExtraModels(PaginatedDto) @Controller(resourceName) -@UseInterceptors(JournalingInterceptor) @Auth(RbacRole.admin, RbacRole.system, RbacRole.reseller) export class AdminsController extends CrudController { private readonly log = new Logger(AdminsController.name) @@ -76,10 +74,13 @@ export class AdminsController extends CrudController { @@ -102,6 +103,7 @@ export class AdminsController extends CrudController { + const sr = this.newServiceRequest(req) this.log.debug({message: 'delete admin by id', func: this.delete.name, url: req.url, method: req.method}) - return await this.adminsService.delete(id, this.newServiceRequest(req)) + const response = await this.adminsService.delete(id, sr) + await this.journalsService.writeJournal(sr, id, {}) + return response } @Get(':id/journal') diff --git a/src/api/admins/admins.service.spec.ts b/src/api/admins/admins.service.spec.ts index 97ac9704..bdbdf70e 100644 --- a/src/api/admins/admins.service.spec.ts +++ b/src/api/admins/admins.service.spec.ts @@ -44,7 +44,7 @@ describe('AdminsService', () => { .compile() service = module.get(AdminsService) - sr = {headers: [undefined], params: [undefined], query: undefined, user: user} + sr = {headers: [undefined], params: [undefined], query: undefined, user: user, init: undefined} }) it('should be defined', () => { diff --git a/src/api/contracts/contracts.controller.ts b/src/api/contracts/contracts.controller.ts index 4da4c4a4..d350b821 100644 --- a/src/api/contracts/contracts.controller.ts +++ b/src/api/contracts/contracts.controller.ts @@ -50,7 +50,10 @@ export class ContractsController extends CrudController { this.log.debug({message: 'create contract', func: this.create.name, url: req.url, method: req.method}) - return this.contractsService.create(entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contractsService.create(entity, sr) + await this.journalsService.writeJournal(sr, response.id, response) + return response } @Get() @@ -88,7 +91,10 @@ export class ContractsController extends CrudController { this.log.debug({message: 'update contract by id', func: this.update.name, url: req.url, method: req.method}) - return this.contractsService.update(id, dto, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contractsService.update(id, dto, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Patch(':id') @@ -101,7 +107,10 @@ export class ContractsController extends CrudController { this.log.debug({message: 'patch contract by id', func: this.adjust.name, url: req.url, method: req.method}) - return this.contractsService.adjust(id, patch, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contractsService.adjust(id, patch, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } // DELETE is not allowed for Contracts diff --git a/src/api/customercontacts/customercontacts.controller.ts b/src/api/customercontacts/customercontacts.controller.ts index d9c5627f..eee98317 100644 --- a/src/api/customercontacts/customercontacts.controller.ts +++ b/src/api/customercontacts/customercontacts.controller.ts @@ -63,7 +63,10 @@ export class CustomercontactsController extends CrudController { this.log.debug({message: 'create customer contact', func: this.create.name, url: req.url, method: req.method}) - return super.create(entity, req) + const sr = this.newServiceRequest(req) + const response = await super.create(entity, req) + await this.journalsService.writeJournal(sr, response.id, response) + return response } @Get() @@ -110,7 +113,10 @@ export class CustomercontactsController extends CrudController { this.log.debug({message: 'patch customer contact by id', func: this.adjust.name, url: req.url, method: req.method}) - return this.contactsService.adjust(id, patch, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.adjust(id, patch, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Put(':id') @@ -119,7 +125,10 @@ export class CustomercontactsController extends CrudController { this.log.debug({message: 'update customer contact by id', func: this.update.name, url: req.url, method: req.method}) - return this.contactsService.update(id, entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.update(id, entity, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Delete(':id') @@ -128,7 +137,10 @@ export class CustomercontactsController extends CrudController { this.log.debug({message: 'delete customer contact by id', func: this.delete.name, url: req.url, method: req.method}) - return this.contactsService.delete(id, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.delete(id, sr) + await this.journalsService.writeJournal(sr, id, {}) + return response } @Get(':id/journal') diff --git a/src/api/customers/customers.controller.ts b/src/api/customers/customers.controller.ts index 759a14af..85724bd6 100644 --- a/src/api/customers/customers.controller.ts +++ b/src/api/customers/customers.controller.ts @@ -54,7 +54,10 @@ export class CustomersController extends CrudController { this.log.debug({message: 'create customer', func: this.create.name, url: req.url, method: req.method}) - return this.customerService.create(entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.customerService.create(entity, sr) + await this.journalService.writeJournal(sr, response.id, response) + return response } @Get() @@ -93,7 +96,10 @@ export class CustomersController extends CrudController { this.log.debug({message: 'update customer by id', func: this.update.name, url: req.url, method: req.method}) - return this.customerService.update(id, dto, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.customerService.update(id, dto, sr) + await this.journalService.writeJournal(sr, id, response) + return response } @Patch(':id') @@ -106,7 +112,10 @@ export class CustomersController extends CrudController { this.log.debug({message: 'patch customer by id', func: this.adjust.name, url: req.url, method: req.method}) - return this.customerService.adjust(id, patch, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.customerService.adjust(id, patch, sr) + await this.journalService.writeJournal(sr, id, response) + return response } @Delete(':id') @@ -115,7 +124,10 @@ export class CustomersController extends CrudController { this.log.debug({message: 'delete customer by id', func: this.delete.name, url: req.url, method: req.method}) - return this.customerService.delete(id, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.customerService.delete(id, sr) + await this.journalService.writeJournal(sr, id, {}) + return response } @Get(':id/journal') diff --git a/src/api/domains/domains.controller.ts b/src/api/domains/domains.controller.ts index 130bc462..a67251b5 100644 --- a/src/api/domains/domains.controller.ts +++ b/src/api/domains/domains.controller.ts @@ -42,8 +42,11 @@ export class DomainsController extends CrudController { this.log.debug({message: 'create domain', func: this.readAll.name, url: req.url, method: req.method}) - const domain = await this.domainsService.create(entity.toInternal(), this.newServiceRequest(req)) - return new DomainResponseDto(domain) + const sr = this.newServiceRequest(req) + const domain = await this.domainsService.create(entity.toInternal(), sr) + const response = new DomainResponseDto(domain) + await this.journalsService.writeJournal(sr, response.id, response) + return response } @Get() @@ -83,7 +86,10 @@ export class DomainsController extends CrudController { this.log.debug({message: 'delete domain by id', func: this.delete.name, url: req.url, method: req.method}) - return this.domainsService.delete(id, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.domainsService.delete(id, sr) + await this.journalsService.writeJournal(sr, id, {}) + return response } @Get(':id/journal') diff --git a/src/api/domains/domains.service.spec.ts b/src/api/domains/domains.service.spec.ts index 0cac3a1d..50c583c3 100644 --- a/src/api/domains/domains.service.spec.ts +++ b/src/api/domains/domains.service.spec.ts @@ -35,7 +35,7 @@ describe('DomainsService', () => { .compile() service = module.get(DomainsService) - sr = {headers: [undefined], params: [undefined], query: undefined, user: user} + sr = {headers: [undefined], params: [undefined], query: undefined, user: user, init: undefined} }) it('should be defined', () => { diff --git a/src/api/fileshare/fileshare.controller.ts b/src/api/fileshare/fileshare.controller.ts index e5791d34..c51a79ab 100644 --- a/src/api/fileshare/fileshare.controller.ts +++ b/src/api/fileshare/fileshare.controller.ts @@ -53,7 +53,10 @@ export class FileshareController extends CrudController { this.log.debug({message: 'create fileshare', func: this.create.name, url: req.url, method: req.method}) - return await this.fileshareService.create(createDto, req, file) + const sr = this.newServiceRequest(req) + const response = await this.fileshareService.create(createDto, sr, file) + await this.journalsService.writeJournal(sr, 0, response) + return response } @Get() @@ -100,7 +103,10 @@ export class FileshareController extends CrudController { return } + + public async writeJournal(req: ServiceRequest, id: number, data: any) { + // Set content format and default to json + let cf = contentFormat[req.headers['Content-Type']] + if (cf === undefined) { + cf = 'json' + } + + // skip journaling if request method is not POST, PUT or DELETE + const op = operation[req.init.method] + if (op === undefined) { + return false + } + + const resourceId = id + const resourceName = extractResourceName(req.init.path, this.app.config.common.api_prefix) + + const ctx = Context.get(req.init) + + // create new Journal entry + const entry = internal.Journal.create({ + reseller_id: req.user.reseller_id, + role_id: req.user.role_data ? req.user.role_data.id : null, + role: req.user.role, + user_id: req.user.id, + tx_id: ctx.txid, + content: Object.keys(data).length > 0 + ? isObject(data) || Array.isArray(data) + ? JSON.stringify(data, obfuscatePasswordJSON) + : Buffer.from(data) + : '', + content_format: cf, + operation: op, + resource_id: resourceId, + resource_name: resourceName, + timestamp: ctx.startTime / 1000, + username: req['user'] !== undefined ? req.user.username : '', + }) + + this.log.debug("write journal entry") + + // write Journal entry to database + await this.create(entry) + + return true + } } diff --git a/src/api/journals/repositories/journals.mariadb.repository.ts b/src/api/journals/repositories/journals.mariadb.repository.ts index 3f008014..c688d38c 100644 --- a/src/api/journals/repositories/journals.mariadb.repository.ts +++ b/src/api/journals/repositories/journals.mariadb.repository.ts @@ -25,6 +25,7 @@ export class JournalsMariadbRepository { const user: AuthResponseDto = req.user const qb = db.billing.Journal.createQueryBuilder('journal') qb.where('journal.id = :id', {id: id}) + qb.leftJoinAndSelect('journal.role', 'role') if (user.reseller_id_required) { qb.andWhere('journal.reseller_id = :resellerId', {resellerId: user.reseller_id}) @@ -39,6 +40,7 @@ export class JournalsMariadbRepository { async readAll(req: ServiceRequest, resourceName?: string, resourceId?: number | string): Promise<[internal.Journal[], number]> { const user: AuthResponseDto = req.user const qb = db.billing.Journal.createQueryBuilder('journal') + qb.leftJoinAndSelect('journal.role', 'role') await configureQueryBuilder(qb, req.query, new SearchLogic(req, Object.keys(new JournalSearchDto()))) if (resourceName !== undefined) { diff --git a/src/api/pbxgroups/dto/pbxgroups-response.dto.ts b/src/api/pbxgroups/dto/pbxgroups-response.dto.ts index dea1ff98..e927e9c5 100644 --- a/src/api/pbxgroups/dto/pbxgroups-response.dto.ts +++ b/src/api/pbxgroups/dto/pbxgroups-response.dto.ts @@ -24,10 +24,12 @@ export class PbxgroupsResponseDto { this.hunt_policy = pbxGroup.huntPolicy this.hunt_timeout = pbxGroup.huntTimeout this.id = pbxGroup.id - this.members = pbxGroup.members.map(member => ({ - extension: member.extension, - subscriber_id: member.subscriberId, - })) + this.members = pbxGroup.members + ? pbxGroup.members.map(member => ({ + extension: member.extension, + subscriber_id: member.subscriberId, + })) + : [] this.name = pbxGroup.name } } diff --git a/src/api/pbxgroups/repositories/pbxgroups.mariadb.repository.ts b/src/api/pbxgroups/repositories/pbxgroups.mariadb.repository.ts index f8b8ca78..1a7e5fb6 100644 --- a/src/api/pbxgroups/repositories/pbxgroups.mariadb.repository.ts +++ b/src/api/pbxgroups/repositories/pbxgroups.mariadb.repository.ts @@ -44,13 +44,10 @@ export class PbxgroupsMariadbRepository implements PbxgroupsRepository { } private generateBaseQuery(req: ServiceRequest, searchLogic?: SearchLogic): SelectQueryBuilder { - const distinctGroupQuery = db.provisioning.VoipPbxGroup.createQueryBuilder('pg') - .select('group_id') - .distinct(true) const memberSubQuery = db.provisioning.VoipSubscriber.createQueryBuilder('sm') .select('concat("[", group_concat(json_object("subscriberId", bm.id, "extension", sm.pbx_extension)), "]")') - .innerJoin('voip_pbx_groups', 'pm', 'pm.subscriber_id = sm.id and pm.group_id = pg.group_id') + .innerJoin('voip_pbx_groups', 'pm', 'pm.subscriber_id = sm.id and pm.group_id = sg.id') .innerJoin('sm.billing_voip_subscriber', 'bm') const query = db.provisioning.VoipSubscriber.createQueryBuilder('sg') @@ -59,11 +56,11 @@ export class PbxgroupsMariadbRepository implements PbxgroupsRepository { .addSelect('sg.pbx_hunt_timeout') .addSelect('sg.pbx_extension') .addSelect('bg.id', 'group_bsub_id') - .addSelect('pg.group_id', 'group_psub_id') + .addSelect('sg.id', 'group_psub_id') .addSelect('bg.contract_id', 'customer_id') .addSelect('(' + memberSubQuery.getQuery() + ')', 'members') - .innerJoin('(' + distinctGroupQuery.getQuery() + ')', 'pg', 'pg.group_id = sg.id') .innerJoin('sg.billing_voip_subscriber', 'bg') + .where('sg.is_pbx_group = 1') this.addPermissionFilterToQueryBuilder(query, req) diff --git a/src/api/resellers/resellers.controller.ts b/src/api/resellers/resellers.controller.ts index 00cd8a06..56229489 100644 --- a/src/api/resellers/resellers.controller.ts +++ b/src/api/resellers/resellers.controller.ts @@ -49,7 +49,10 @@ export class ResellersController extends CrudController { this.log.debug({message: 'create reseller', func: this.create.name, url: req.url, method: req.method}) - return this.resellersService.create(entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.resellersService.create(entity, sr) + await this.journalsService.writeJournal(sr, response.id, response) + return response } @Get() @@ -87,7 +90,10 @@ export class ResellersController extends CrudController { this.log.debug({message: 'update reseller by id', func: this.update.name, url: req.url, method: req.method}) - return this.resellersService.update(id, entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.resellersService.update(id, entity, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Patch(':id') @@ -100,7 +106,10 @@ export class ResellersController extends CrudController { this.log.debug({message: 'patch reseller by id', func: this.adjust.name, url: req.url, method: req.method}) - return this.resellersService.adjust(id, patch, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.resellersService.adjust(id, patch, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Get(':id/journal') diff --git a/src/api/systemcontacts/systemcontacts.controller.ts b/src/api/systemcontacts/systemcontacts.controller.ts index f283fb48..a48c16df 100644 --- a/src/api/systemcontacts/systemcontacts.controller.ts +++ b/src/api/systemcontacts/systemcontacts.controller.ts @@ -48,7 +48,10 @@ export class SystemcontactsController extends CrudController { this.log.debug({message: 'create system contact', func: this.create.name, url: req.url, method: req.method}) - return this.contactsService.create(entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.create(entity, sr) + await this.journalsService.writeJournal(sr, response.id, response) + return response } @Get() @@ -91,7 +94,10 @@ export class SystemcontactsController extends CrudController { this.log.debug({message: 'update system contact by id', func: this.update.name, url: req.url, method: req.method}) - return this.contactsService.update(id, entity, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.update(id, entity, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Patch(':id') @@ -101,7 +107,10 @@ export class SystemcontactsController extends CrudController { this.log.debug({message: 'patch system contact by id', func: this.adjust.name, url: req.url, method: req.method}) - return this.contactsService.adjust(id, patch, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.adjust(id, patch, sr) + await this.journalsService.writeJournal(sr, id, response) + return response } @Delete(':id') @@ -110,7 +119,10 @@ export class SystemcontactsController extends CrudController { this.log.debug({message: 'delete system contact by id', func: this.delete.name, url: req.url, method: req.method}) - return this.contactsService.delete(id, this.newServiceRequest(req)) + const sr = this.newServiceRequest(req) + const response = await this.contactsService.delete(id, sr) + await this.journalsService.writeJournal(sr, id, {}) + return response } @Get(':id/journal') diff --git a/src/api/voicemails/voicemails.controller.ts b/src/api/voicemails/voicemails.controller.ts index 23b9b2af..ec0a4526 100644 --- a/src/api/voicemails/voicemails.controller.ts +++ b/src/api/voicemails/voicemails.controller.ts @@ -61,7 +61,10 @@ export class VoicemailsController extends CrudController { this.log.debug({message: 'create voicemail', func: this.create.name, url: req.url, method: req.method}) - return await this.voicemailsService.create(voicemail, req) + const sr = this.newServiceRequest(req) + const response = await this.voicemailsService.create(voicemail, sr) + await this.journalService.writeJournal(sr, response.id, response) + return response } @Get() @@ -98,7 +101,10 @@ export class VoicemailsController extends CrudController { this.log.debug({message: 'delete voicemail by id', func: this.delete.name, url: req.url, method: req.method}) - return await this.voicemailsService.delete(id, req) + const sr = this.newServiceRequest(req) + const response = await this.voicemailsService.delete(id, sr) + await this.journalService.writeJournal(sr, id, {}) + return response } @Patch(':id') @@ -111,12 +117,15 @@ export class VoicemailsController extends CrudController { this.log.debug({message: 'patch voicemail by id', func: this.adjust.name, url: req.url, method: req.method}) + const sr = this.newServiceRequest(req) const err = validate(patch) if (err) { const message = err.message.replace(/[\n\s]+/g, ' ').replace(/"/g, '\'') throw new BadRequestException(message) } - return await this.voicemailsService.adjust(id, patch, req) + const response = await this.voicemailsService.adjust(id, patch, sr) + await this.journalService.writeJournal(sr, id, response) + return response } @Put(':id') @@ -125,6 +134,9 @@ export class VoicemailsController extends CrudController { this.log.debug({message: 'update voicemail by id', func: this.update.name, url: req.url, method: req.method}) - return await this.voicemailsService.update(id, voicemail, req) + const sr = this.newServiceRequest(req) + const response = await this.voicemailsService.update(id, voicemail, sr) + await this.journalService.writeJournal(sr, id, response) + return response } } diff --git a/src/config/constants.config.ts b/src/config/constants.config.ts index 9ba3c00b..4c42c609 100644 --- a/src/config/constants.config.ts +++ b/src/config/constants.config.ts @@ -24,7 +24,7 @@ export enum RbacRole { ccare = 'ccare', ccareadmin = 'ccareadmin', lintercept = 'lintercept', - subscriber = 'subscriber' + subscriber = 'subscriber', } export interface RbacFlag { diff --git a/src/config/database.config.ts b/src/config/database.config.ts index f632ef95..819666cb 100644 --- a/src/config/database.config.ts +++ b/src/config/database.config.ts @@ -27,7 +27,7 @@ export const databaseConfig: ConnectionOptions = { host: db_host, type: 'mariadb', entities: entities, - connectTimeout: 2000, + connectTimeout: 10000, trace: false, debug: false, supportBigNumbers: true, diff --git a/src/controllers/crud.controller.ts b/src/controllers/crud.controller.ts index 0c4942e4..cbc8d568 100644 --- a/src/controllers/crud.controller.ts +++ b/src/controllers/crud.controller.ts @@ -74,6 +74,7 @@ export class CrudController { params: [req.params], user: req.user, query: req.query, + init: req, } } } diff --git a/src/entities/db/billing/journal.mariadb.entity.ts b/src/entities/db/billing/journal.mariadb.entity.ts index cf896080..108e3c30 100644 --- a/src/entities/db/billing/journal.mariadb.entity.ts +++ b/src/entities/db/billing/journal.mariadb.entity.ts @@ -91,7 +91,7 @@ export class Journal extends BaseEntity { width: 11, }) user_id: number - + fromInternal(journal: internal.Journal) { this.id = journal.id this.reseller_id = journal.reseller_id @@ -112,6 +112,7 @@ export class Journal extends BaseEntity { journal.id = this.id journal.reseller_id = this.reseller_id + journal.role = this.role ? this.role.role : null journal.role_id = this.role_id journal.tx_id = this.tx_id journal.user_id = this.user_id diff --git a/src/entities/internal/journal.internal.entity.ts b/src/entities/internal/journal.internal.entity.ts index 6b0e7101..0fb0a20d 100644 --- a/src/entities/internal/journal.internal.entity.ts +++ b/src/entities/internal/journal.internal.entity.ts @@ -6,6 +6,7 @@ interface JournalInterface { reseller_id: number resource_id: number resource_name: string + role: string | null role_id: number | null timestamp: number tx_id: string @@ -21,6 +22,7 @@ export class Journal implements JournalInterface { reseller_id: number resource_id: number resource_name: string + role: string | null role_id: number | null timestamp: number tx_id: string diff --git a/src/interceptors/interceptor.module.ts b/src/interceptors/interceptor.module.ts index 13f530d1..065940af 100644 --- a/src/interceptors/interceptor.module.ts +++ b/src/interceptors/interceptor.module.ts @@ -2,7 +2,6 @@ import {forwardRef, Module} from '@nestjs/common' import {JournalsModule} from '../api/journals/journals.module' import {LoggerModule} from '../logger/logger.module' import {LoggerService} from '../logger/logger.service' -import {JournalingInterceptor} from './journaling.interceptor' import {LoggingInterceptor} from './logging.interceptor' import {JournalsService} from '../api/journals/journals.service' @@ -11,7 +10,7 @@ import {JournalsService} from '../api/journals/journals.service' forwardRef(() => JournalsModule), LoggerModule ], - providers: [JournalingInterceptor, LoggingInterceptor, LoggerService, JournalsService], + providers: [LoggingInterceptor, LoggerService, JournalsService], exports: [JournalsModule, LoggerModule], }) export class InterceptorModule { diff --git a/src/interceptors/journaling.interceptor.ts b/src/interceptors/journaling.interceptor.ts deleted file mode 100644 index 22812841..00000000 --- a/src/interceptors/journaling.interceptor.ts +++ /dev/null @@ -1,116 +0,0 @@ -import {CallHandler, ExecutionContext, forwardRef, Inject, NestInterceptor} from '@nestjs/common' -import {Observable} from 'rxjs' -import {map} from 'rxjs/operators' -import {JournalsService} from '../api/journals/journals.service' -import {extractResourceName} from '../helpers/uri.helper' -import {AppService} from '../app.service' -import Context from '../helpers/context.helper' -import {isObject} from 'class-validator' -import {internal} from '../entities' -import {obfuscatePasswordJSON} from '../helpers/password-obfuscator.helper' - -/** - * Lookup-table for HTTP operations - */ -const operation = { - 'PATCH': 'update', - 'POST': 'create', - 'PUT': 'update', - 'DELETE': 'delete', -} - -/** - * Lookup-table for Content-Type - */ -const contentFormat = { - 'application/json': 'json', -} - -/** - * JournalInterceptor writes journal entries for POST, PUT and DELETE requests to the database. - */ -export class JournalingInterceptor implements NestInterceptor { - - /** - * Creates a new `JournalingInterceptor` - * @param journalsService Injected JournalService to access database - */ - constructor( - @Inject(forwardRef(() => JournalsService)) - private readonly journalsService: JournalsService, - ) { - } - - /** - * Creates a journal entry containing the received Content-Type, HTTP method, username, timestamp, resource name - * resource ID and encoded received data to database - * - * Journal entries are only created for HTTP methods POST, PUT and DELETE. - * On other methods the function simply returns - * - * It does not modify received data and always returns it as is. - * - * Currently the only supported Content-Type is `application/json`. - * - * @param context ExecutionContext to access HTTP request - * @param next Next CallHandler - * - * @returns data unmodified as received - */ - intercept(context: ExecutionContext, next: CallHandler): Observable | Promise> { - return next.handle().pipe( - map(data => { - const httpCtx = context.switchToHttp() - const req = httpCtx.getRequest() - - // Set content format and default to json - let cf = contentFormat[req.get('Content-Type')] - if (cf === undefined) { - cf = 'json' - } - - // skip journaling if request method is not POST, PUT or DELETE - const op = operation[req.method] - if (op === undefined) { - return data - } - - const resourceName = extractResourceName(req.path, AppService.config.common.api_prefix) - - // Get resourceID from data values if method is POST else from request params 'id' - let resourceID = 0 - if (req.method == 'POST') { - if (data && 'id' in data && data.id && Number.isInteger(data.id)) - resourceID = data.id - } else { - resourceID = req.params.id - } - - const ctx = Context.get(req) - - // create new Journal entry - const entry = internal.Journal.create({ - reseller_id: req.user.reseller_id, - role_id: req.user.role_data ? req.user.role_data.id : null, - user_id: req.user.id, - tx_id: ctx.txid, - content: Object.keys(req.body).length > 0 - ? isObject(req.body) || Array.isArray(req.body) - ? JSON.stringify(req.body, obfuscatePasswordJSON) - : Buffer.from(req.body) - : '', - content_format: cf, - operation: op, - resource_id: resourceID, - resource_name: resourceName, - timestamp: ctx.startTime / 1000, - username: req['user'] !== undefined ? req.user.username : '', - }) - - // write Journal entry to database - this.journalsService.create(entry) - return data - }), - ) - } -} diff --git a/src/interfaces/service-request.interface.ts b/src/interfaces/service-request.interface.ts index 09d7cd97..a1e7a50e 100644 --- a/src/interfaces/service-request.interface.ts +++ b/src/interfaces/service-request.interface.ts @@ -1,8 +1,10 @@ import {AuthResponseDto} from '../auth/dto/auth-response.dto' +import {Request} from 'express' export interface ServiceRequest { params: [any], user: AuthResponseDto | any, // TODO: fix typing headers: [any], query?: any + init: Request, }