Skip to content

Commit

Permalink
Merge pull request #175 from juicyllama/fix/websockets-auth
Browse files Browse the repository at this point in the history
Fix/websockets auth
  • Loading branch information
andyslack authored Jan 2, 2025
2 parents 2d32a86 + 995867b commit f9b44f9
Show file tree
Hide file tree
Showing 17 changed files with 310 additions and 165 deletions.
15 changes: 1 addition & 14 deletions demo/databases/mysql.sql
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,6 @@ CREATE TABLE IF NOT EXISTS `UserApiKey` (

INSERT IGNORE INTO `UserApiKey` (`id`, `userId`, `apiKey`, `createdAt`, `updatedAt`, `deletedAt`) VALUES (1, 1, 'Ex@mp1eS$Cu7eAp!K3y', '2000-01-01 00:00:00', '2000-01-01 00:00:00', NULL);

CREATE TABLE IF NOT EXISTS Category (
categoryId INT AUTO_INCREMENT NOT NULL
,categoryName VARCHAR(15) NOT NULL
,description TEXT NULL
,picture BLOB NULL
,createdAt datetime DEFAULT CURRENT_TIMESTAMP
,updatedAt datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
,deletedAt datetime DEFAULT NULL
,PRIMARY KEY (categoryId)
) ENGINE=INNODB;


CREATE TABLE IF NOT EXISTS Customer (
custId INT AUTO_INCREMENT NOT NULL
,userId int NOT NULL
Expand Down Expand Up @@ -85,7 +73,7 @@ CREATE TABLE IF NOT EXISTS Employee (
,mobile VARCHAR(24) NULL
,email VARCHAR(225) NULL
,photo BLOB NULL
,notes BLOB NULL
,notes TEXT NULL
,photoPath VARCHAR(255) NULL
,mgrid INT NULL
,createdAt datetime DEFAULT CURRENT_TIMESTAMP
Expand Down Expand Up @@ -129,7 +117,6 @@ CREATE TABLE IF NOT EXISTS SalesOrder (
REFERENCES Shipper(shipperId)
,FOREIGN KEY (custId)
REFERENCES Customer(custId)

) ENGINE=INNODB;


Expand Down
12 changes: 8 additions & 4 deletions src/app.controller.delete.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Delete, Headers, Param, Req, Res, Query as QueryParams } from '@nestjs/common'
import { Body, Controller, Delete, Headers, Param, Query as QueryParams, Req, Res } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'

import { LLANA_WEBHOOK_TABLE } from './app.constants'
Expand Down Expand Up @@ -42,7 +42,7 @@ export class DeleteController {
@Res() res,
@Headers() headers: HeaderParams,
@Param('id') id: string,
@QueryParams('hard') hard = false
@QueryParams('hard') hard = false,
): Promise<DeleteResponseObject> {
const x_request_id = headers['x-request-id']
let table_name = UrlToTable(req.originalUrl, 1)
Expand Down Expand Up @@ -136,7 +136,11 @@ export class DeleteController {

let softDelete: string = null

if (!hard && databaseConfig.deletes.soft && schema.columns.find(col => col.field === databaseConfig.deletes.soft)) {
if (
!hard &&
databaseConfig.deletes.soft &&
schema.columns.find(col => col.field === databaseConfig.deletes.soft)
) {
softDelete = databaseConfig.deletes.soft
}

Expand Down Expand Up @@ -164,7 +168,7 @@ export class DeleteController {
@Res() res,
@Headers() headers: HeaderParams,
@Body() body: Partial<any> | Partial<any>[],
@QueryParams('hard') hard = false
@QueryParams('hard') hard = false,
): Promise<DeleteManyResponseObject> {
const x_request_id = headers['x-request-id']
let table_name = UrlToTable(req.originalUrl, 1)
Expand Down
28 changes: 14 additions & 14 deletions src/app.controller.get.test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,8 +202,10 @@ describe('App > Controller > Get', () => {

describe('List', () => {
it('All', async function () {
const result = await request(app.getHttpServer()).get(`/SalesOrder/`).set('Authorization', `Bearer ${jwt}`)
.expect(200)
const result = await request(app.getHttpServer())
.get(`/SalesOrder/`)
.set('Authorization', `Bearer ${jwt}`)
.expect(200)

expect(result.body).toBeDefined()
expect(result.body.total).toBeDefined()
Expand Down Expand Up @@ -320,9 +322,6 @@ describe('App > Controller > Get', () => {
})

it('Date', function () {

console.log(result.body)

expect(result.body.orderDate).not.toBeNull()
expect(new Date(result.body.orderDate)).toBeInstanceOf(Date)
expect(result.body.orderDate).toBeTruthy()
Expand Down Expand Up @@ -815,16 +814,15 @@ describe('App > Controller > Get', () => {
})

it('When allowed_fields are passed, only return these fields even with fields passe, with relations', async function () {

const role_salesOrder = await authTestingService.createRole({
custom: true,
table: salesOrderSchema.table,
role: 'ADMIN',
records: RolePermission.WRITE,
own_records: RolePermission.WRITE,
allowed_fields: salesOrderSchema.primary_key+',custId,shipName',
allowed_fields: salesOrderSchema.primary_key + ',custId,shipName',
})

const role_customer = await authTestingService.createRole({
custom: true,
table: customerSchema.table,
Expand All @@ -837,9 +835,7 @@ describe('App > Controller > Get', () => {

try {
const result = await request(app.getHttpServer())
.get(
`/SalesOrder/${orders[0][salesOrderSchema.primary_key]}?relations=Customer`,
)
.get(`/SalesOrder/${orders[0][salesOrderSchema.primary_key]}?relations=Customer`)
.set('Authorization', `Bearer ${jwt}`)
.expect(200)

Expand All @@ -866,12 +862,16 @@ describe('App > Controller > Get', () => {
await authTestingService.deleteRole(role_customer)
}
})

})

afterAll(async () => {
for (let i = 0; i < 10; i++) {
await salesOrderTestingService.deleteOrder(orders[i][salesOrderSchema.primary_key])
console.log(salesOrderSchema.primary_key)

console.log(orders)

for (const order of orders) {
console.log('delete order #' + order[salesOrderSchema.primary_key])
await salesOrderTestingService.deleteOrder(order[salesOrderSchema.primary_key])
}
await customerTestingService.deleteCustomer(customer[customerSchema.primary_key])
await employeeTestingService.deleteEmployee(employee[employeeSchema.primary_key])
Expand Down
55 changes: 28 additions & 27 deletions src/app.controller.get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -545,40 +545,41 @@ export class GetController {
)) as FindManyResponseObject

if (postQueryRelations?.length) {
for (const r in postQueryRelations) {
// Check if the relation has allowed_field restrictions
const relation_public_auth = await this.authentication.public({
table: postQueryRelations[r].table,
access_level: RolePermission.READ,
x_request_id,
})

for(const r in postQueryRelations) {
// Check if the relation has allowed_field restrictions
const relation_public_auth = await this.authentication.public({
if (relation_public_auth.valid && relation_public_auth.allowed_fields?.length) {
postQueryRelations[r].columns = postQueryRelations[r].columns.filter(field =>
relation_public_auth.allowed_fields.includes(field),
)
}

// If not public, check role table permissions
if (auth.user_identifier) {
let permission = await this.roles.tablePermission({
identifier: auth.user_identifier,
table: postQueryRelations[r].table,
access_level: RolePermission.READ,
access: RolePermission.READ,
x_request_id,
})

if (relation_public_auth.valid && relation_public_auth.allowed_fields?.length) {
if (
permission.valid &&
(permission as AuthTablePermissionSuccessResponse).allowed_fields?.length
) {
postQueryRelations[r].columns.push(
...(permission as AuthTablePermissionSuccessResponse).allowed_fields,
)
postQueryRelations[r].columns = postQueryRelations[r].columns.filter(field =>
relation_public_auth.allowed_fields.includes(field),
(permission as AuthTablePermissionSuccessResponse).allowed_fields.includes(field),
)
}

// If not public, check role table permissions
if (auth.user_identifier) {
let permission = await this.roles.tablePermission({
identifier: auth.user_identifier,
table: postQueryRelations[r].table,
access: RolePermission.READ,
x_request_id,
})

if (
permission.valid &&
(permission as AuthTablePermissionSuccessResponse).allowed_fields?.length
) {
postQueryRelations[r].columns.push(...(permission as AuthTablePermissionSuccessResponse).allowed_fields)
postQueryRelations[r].columns = postQueryRelations[r].columns.filter(field =>
(permission as AuthTablePermissionSuccessResponse).allowed_fields.includes(field),
)
}
}
}
}

options.relations = postQueryRelations
Expand All @@ -590,7 +591,7 @@ export class GetController {
)
}
}

return res.status(200).send(result)
} catch (e) {
return res.status(400).send(this.response.text(e.message))
Expand Down
76 changes: 67 additions & 9 deletions src/app.controller.post.test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { AppModule } from './app.module'
import { AuthTestingService } from './testing/auth.testing.service'
import { DataSourceSchema } from './types/datasource.types'
import { UserTestingService } from './testing/user.testing.service'
import { EmployeeTestingService } from './testing/employee.testing.service'
import { Logger } from './helpers/Logger'
import { TIMEOUT } from './testing/testing.const'

Expand All @@ -18,6 +19,7 @@ import hosts from './config/hosts.config'
import jwt from './config/jwt.config'
import roles from './config/roles.config'
import { envValidationSchema } from './config/env.validation'
import exp from 'constants'
import { RolePermission } from './types/roles.types'

// Type the config imports
Expand All @@ -29,6 +31,7 @@ describe('App > Controller > Post', () => {
let authTestingService: AuthTestingService
let customerTestingService: CustomerTestingService
let userTestingService: UserTestingService
let employeeTestingService: EmployeeTestingService

let customerSchema: DataSourceSchema
let userSchema: DataSourceSchema
Expand Down Expand Up @@ -58,8 +61,8 @@ describe('App > Controller > Post', () => {
}),
AppModule,
],
providers: [AuthTestingService, CustomerTestingService, UserTestingService],
exports: [AuthTestingService, CustomerTestingService, UserTestingService],
providers: [AuthTestingService, CustomerTestingService, UserTestingService, EmployeeTestingService],
exports: [AuthTestingService, CustomerTestingService, UserTestingService, EmployeeTestingService],
}).compile()

app = moduleRef.createNestApplication()
Expand All @@ -68,6 +71,7 @@ describe('App > Controller > Post', () => {
authTestingService = app.get<AuthTestingService>(AuthTestingService)
customerTestingService = app.get<CustomerTestingService>(CustomerTestingService)
userTestingService = app.get<UserTestingService>(UserTestingService)
employeeTestingService = app.get<EmployeeTestingService>(EmployeeTestingService)

customerSchema = await customerTestingService.getSchema()
userSchema = await userTestingService.getSchema()
Expand Down Expand Up @@ -110,6 +114,7 @@ describe('App > Controller > Post', () => {
expect(result.body.contactName).toBeDefined()
customers.push(result.body)
})

it('Create Many', async function () {
const result = await request(app.getHttpServer())
.post(`/Customer/`)
Expand All @@ -133,6 +138,60 @@ describe('App > Controller > Post', () => {
})
})

describe('Create with special characters', () => {
it('Create One with special characters !@#$%^&*()_+', async function () {
const mock = customerTestingService.mockCustomer(userId)
mock.companyName = 'Test Company Name - !@#$%^&*()_+'

const result = await request(app.getHttpServer())
.post(`/Customer/`)
.send(mock)
.set('Authorization', `Bearer ${jwt}`)
.expect(201)

expect(result.body).toBeDefined()
expect(result.body[customerSchema.primary_key]).toBeDefined()
expect(result.body.companyName).toBeDefined()
expect(result.body.contactName).toBeDefined()
customers.push(result.body)
})

it('Create One with comma', async function () {
const mock = customerTestingService.mockCustomer(userId)
mock.companyName = 'Test Company Name, with comma'

const result = await request(app.getHttpServer())
.post(`/Customer/`)
.send(mock)
.set('Authorization', `Bearer ${jwt}`)
.expect(201)

expect(result.body).toBeDefined()
expect(result.body[customerSchema.primary_key]).toBeDefined()
expect(result.body.companyName).toBeDefined()
expect(result.body.contactName).toBeDefined()
customers.push(result.body)
})

// it('Create One with comma in TEXT field', async function () {

// const mock = employeeTestingService.mockEmployee()
// mock.notes = 'Test note, with comma'

// const result = await request(app.getHttpServer())
// .post(`/Employee/`)
// .send(mock)
// .set('Authorization', `Bearer ${jwt}`)

// console.log(result.body)
// //.expect(201)

// expect(result.body).toBeDefined()
// expect(result.body.notes).toBeDefined()
// customers.push(result.body)
// })
})

describe('Public Creation', () => {
it('Default public fail to create', async function () {
await request(app.getHttpServer())
Expand Down Expand Up @@ -499,7 +558,7 @@ describe('App > Controller > Post', () => {
role: 'ADMIN',
records: RolePermission.NONE,
own_records: RolePermission.DELETE,
allowed_fields: customerSchema.primary_key+',companyName,contactName',
allowed_fields: customerSchema.primary_key + ',companyName,contactName',
})

try {
Expand All @@ -509,12 +568,11 @@ describe('App > Controller > Post', () => {
.set('Authorization', `Bearer ${jwt}`)
.expect(201)

customers.push(result.body)
expect(result.body).toBeDefined()
expect(result.body[customerSchema.primary_key]).toBeDefined()
expect(result.body.companyName).toBeDefined()
expect(result.body.contactName).toBeDefined()

customers.push(result.body)
expect(result.body).toBeDefined()
expect(result.body[customerSchema.primary_key]).toBeDefined()
expect(result.body.companyName).toBeDefined()
expect(result.body.contactName).toBeDefined()
} catch (e) {
logger.error(e)
throw e
Expand Down
2 changes: 1 addition & 1 deletion src/app.controller.post.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ export class PostController {
}

if (singular) {
if(errors.length) {
if (errors.length) {
return res.status(400).send(errors[0].message)
}

Expand Down
2 changes: 1 addition & 1 deletion src/datasources/mysql.datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ export class MySQL {
data[key] = data[key] === 1
break
case DataSourceColumnType.DATE:
if(data[key] !== null) {
if (data[key] !== null) {
data[key] = new Date(data[key]).toISOString()
}
break
Expand Down
Loading

0 comments on commit f9b44f9

Please sign in to comment.