wip(backend): More work on backend/tests
This commit is contained in:
parent
2297b61d20
commit
b9bb96d837
22 changed files with 905 additions and 101 deletions
|
@ -7,7 +7,6 @@
|
|||
"author": "Joost De Cock <joost@joost.at> (https://github.com/joostdecock)",
|
||||
"homepage": "https://freesewing.org/",
|
||||
"repository": "github:freesewing/freesewing",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/freesewing/freesewing/issues"
|
||||
},
|
||||
|
@ -30,10 +29,13 @@
|
|||
"crypto": "^1.0.1",
|
||||
"express": "4.18.2",
|
||||
"mustache": "^4.2.0",
|
||||
"passport": "^0.6.0",
|
||||
"passport-http": "^0.3.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"pino": "^8.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai-http": "^4.3.0",
|
||||
"mocha": "^10.1.0",
|
||||
"mocha-steps": "^1.3.0",
|
||||
"prisma": "4.5.0"
|
||||
|
|
|
@ -73,9 +73,12 @@ model Pattern {
|
|||
data String
|
||||
design String
|
||||
img String?
|
||||
name String @default("")
|
||||
notes String
|
||||
person Person? @relation(fields: [personId], references: [id])
|
||||
personId Int?
|
||||
notes String
|
||||
public Boolean @default(false)
|
||||
settings String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
updatedAt DateTime @updatedAt
|
||||
|
|
Binary file not shown.
|
@ -4,7 +4,7 @@ import { log } from '../utils/log.mjs'
|
|||
import { ApikeyModel } from '../models/apikey.mjs'
|
||||
import { UserModel } from '../models/user.mjs'
|
||||
|
||||
export function ApikeyController() {}
|
||||
export function ApikeysController() {}
|
||||
|
||||
/*
|
||||
* Create API key
|
||||
|
@ -12,7 +12,7 @@ export function ApikeyController() {}
|
|||
* This is the endpoint that handles creation of API keys/tokens
|
||||
* See: https://freesewing.dev/reference/backend/api/apikey
|
||||
*/
|
||||
ApikeyController.prototype.create = async (req, res, tools) => {
|
||||
ApikeysController.prototype.create = async (req, res, tools) => {
|
||||
const Apikey = new ApikeyModel(tools)
|
||||
await Apikey.create(req)
|
||||
|
||||
|
@ -25,7 +25,7 @@ ApikeyController.prototype.create = async (req, res, tools) => {
|
|||
* This is the endpoint that handles creation of API keys/tokens
|
||||
* See: https://freesewing.dev/reference/backend/api/apikey
|
||||
*/
|
||||
ApikeyController.prototype.read = async (req, res, tools) => {
|
||||
ApikeysController.prototype.read = async (req, res, tools) => {
|
||||
const Apikey = new ApikeyModel(tools)
|
||||
await Apikey.guardedRead(req)
|
||||
|
||||
|
@ -39,7 +39,7 @@ ApikeyController.prototype.read = async (req, res, tools) => {
|
|||
* request
|
||||
* See: https://freesewing.dev/reference/backend/api/apikey
|
||||
*/
|
||||
ApikeyController.prototype.whoami = async (req, res, tools) => {
|
||||
ApikeysController.prototype.whoami = async (req, res, tools) => {
|
||||
const User = new UserModel(tools)
|
||||
const Apikey = new ApikeyModel(tools)
|
||||
|
||||
|
@ -69,7 +69,7 @@ ApikeyController.prototype.whoami = async (req, res, tools) => {
|
|||
* This is the endpoint that handles removal of API keys/tokens
|
||||
* See: https://freesewing.dev/reference/backend/api/apikey
|
||||
*/
|
||||
ApikeyController.prototype.delete = async (req, res, tools) => {
|
||||
ApikeysController.prototype.delete = async (req, res, tools) => {
|
||||
const Apikey = new ApikeyModel(tools)
|
||||
await Apikey.guardedDelete(req)
|
||||
|
58
sites/backend/src/controllers/patterns.mjs
Normal file
58
sites/backend/src/controllers/patterns.mjs
Normal file
|
@ -0,0 +1,58 @@
|
|||
import { PatternModel } from '../models/pattern.mjs'
|
||||
|
||||
export function PatternsController() {}
|
||||
|
||||
/*
|
||||
* Create a pattern
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PatternsController.prototype.create = async (req, res, tools) => {
|
||||
const Pattern = new PatternModel(tools)
|
||||
await Pattern.guardedCreate(req)
|
||||
|
||||
return Pattern.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Read a pattern
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PatternsController.prototype.read = async (req, res, tools) => {
|
||||
const Pattern = new PatternModel(tools)
|
||||
await Pattern.guardedRead(req)
|
||||
|
||||
return Pattern.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Update a pattern
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PatternsController.prototype.update = async (req, res, tools) => {
|
||||
const Pattern = new PatternModel(tools)
|
||||
await Pattern.guardedUpdate(req)
|
||||
|
||||
return Pattern.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove a pattern
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PatternsController.prototype.delete = async (req, res, tools) => {
|
||||
const Pattern = new PatternModel(tools)
|
||||
await Pattern.guardedDelete(req)
|
||||
|
||||
return Pattern.sendResponse(res)
|
||||
}
|
||||
|
||||
/*
|
||||
* Clone a pattern
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PatternsController.prototype.clone = async (req, res, tools) => {
|
||||
const Pattern = new PatternModel(tools)
|
||||
await Pattern.guardedClone(req)
|
||||
|
||||
return Pattern.sendResponse(res)
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
import { PersonModel } from '../models/person.mjs'
|
||||
|
||||
export function PersonController() {}
|
||||
export function PeopleController() {}
|
||||
|
||||
/*
|
||||
* Create a person for the authenticated user
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PersonController.prototype.create = async (req, res, tools) => {
|
||||
PeopleController.prototype.create = async (req, res, tools) => {
|
||||
const Person = new PersonModel(tools)
|
||||
await Person.guardedCreate(req)
|
||||
|
||||
|
@ -17,7 +17,7 @@ PersonController.prototype.create = async (req, res, tools) => {
|
|||
* Read a person
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PersonController.prototype.read = async (req, res, tools) => {
|
||||
PeopleController.prototype.read = async (req, res, tools) => {
|
||||
const Person = new PersonModel(tools)
|
||||
await Person.guardedRead(req)
|
||||
|
||||
|
@ -28,7 +28,7 @@ PersonController.prototype.read = async (req, res, tools) => {
|
|||
* Update a person
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PersonController.prototype.update = async (req, res, tools) => {
|
||||
PeopleController.prototype.update = async (req, res, tools) => {
|
||||
const Person = new PersonModel(tools)
|
||||
await Person.guardedUpdate(req)
|
||||
|
||||
|
@ -39,7 +39,7 @@ PersonController.prototype.update = async (req, res, tools) => {
|
|||
* Remove a person
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PersonController.prototype.delete = async (req, res, tools) => {
|
||||
PeopleController.prototype.delete = async (req, res, tools) => {
|
||||
const Person = new PersonModel(tools)
|
||||
await Person.guardedDelete(req)
|
||||
|
||||
|
@ -50,7 +50,7 @@ PersonController.prototype.delete = async (req, res, tools) => {
|
|||
* Clone a person
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
PersonController.prototype.clone = async (req, res, tools) => {
|
||||
PeopleController.prototype.clone = async (req, res, tools) => {
|
||||
const Person = new PersonModel(tools)
|
||||
await Person.guardedClone(req)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { UserModel } from '../models/user.mjs'
|
||||
|
||||
export function UserController() {}
|
||||
export function UsersController() {}
|
||||
|
||||
/*
|
||||
* Signup
|
||||
|
@ -8,7 +8,7 @@ export function UserController() {}
|
|||
* This is the endpoint that handles account signups
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
UserController.prototype.signup = async (req, res, tools) => {
|
||||
UsersController.prototype.signup = async (req, res, tools) => {
|
||||
const User = new UserModel(tools)
|
||||
await User.guardedCreate(req)
|
||||
|
||||
|
@ -21,7 +21,7 @@ UserController.prototype.signup = async (req, res, tools) => {
|
|||
* This is the endpoint that fully unlocks the account if the user gives their consent
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
UserController.prototype.confirm = async (req, res, tools) => {
|
||||
UsersController.prototype.confirm = async (req, res, tools) => {
|
||||
const User = new UserModel(tools)
|
||||
await User.confirm(req)
|
||||
|
||||
|
@ -34,7 +34,7 @@ UserController.prototype.confirm = async (req, res, tools) => {
|
|||
* This is the endpoint that provides traditional username/password login
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
UserController.prototype.login = async function (req, res, tools) {
|
||||
UsersController.prototype.login = async function (req, res, tools) {
|
||||
const User = new UserModel(tools)
|
||||
await User.passwordLogin(req)
|
||||
|
||||
|
@ -46,7 +46,7 @@ UserController.prototype.login = async function (req, res, tools) {
|
|||
*
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
UserController.prototype.whoami = async (req, res, tools) => {
|
||||
UsersController.prototype.whoami = async (req, res, tools) => {
|
||||
const User = new UserModel(tools)
|
||||
await User.guardedRead({ id: req.user.uid }, req)
|
||||
|
||||
|
@ -58,7 +58,7 @@ UserController.prototype.whoami = async (req, res, tools) => {
|
|||
*
|
||||
* See: https://freesewing.dev/reference/backend/api
|
||||
*/
|
||||
UserController.prototype.update = async (req, res, tools) => {
|
||||
UsersController.prototype.update = async (req, res, tools) => {
|
||||
const User = new UserModel(tools)
|
||||
await User.guardedRead({ id: req.user.uid }, req)
|
||||
await User.guardedUpdate(req)
|
317
sites/backend/src/models/pattern.mjs
Normal file
317
sites/backend/src/models/pattern.mjs
Normal file
|
@ -0,0 +1,317 @@
|
|||
import { log } from '../utils/log.mjs'
|
||||
import { setPatternAvatar } from '../utils/sanity.mjs'
|
||||
|
||||
export function PatternModel(tools) {
|
||||
this.config = tools.config
|
||||
this.prisma = tools.prisma
|
||||
this.decrypt = tools.decrypt
|
||||
this.encrypt = tools.encrypt
|
||||
this.encryptedFields = ['data', 'img', 'name', 'notes', 'settings']
|
||||
this.clear = {} // For holding decrypted data
|
||||
|
||||
return this
|
||||
}
|
||||
/*
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now())
|
||||
data String
|
||||
design String
|
||||
img String?
|
||||
person Person? @relation(fields: [personId], references: [id])
|
||||
personId Int?
|
||||
name String @default("")
|
||||
notes String
|
||||
public
|
||||
settings String
|
||||
user User @relation(fields: [userId], references: [id])
|
||||
userId Int
|
||||
updatedAt DateTime @updatedAt
|
||||
*/
|
||||
|
||||
PatternModel.prototype.guardedCreate = async function ({ body, user }) {
|
||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (Object.keys(body) < 2) return this.setResponse(400, 'postBodyMissing')
|
||||
if (!body.person) return this.setResponse(400, 'personMissing')
|
||||
if (typeof body.person !== 'number') return this.setResponse(400, 'personNotNumeric')
|
||||
if (typeof body.settings !== 'object') return this.setResponse(400, 'settingsNotAnObject')
|
||||
if (body.data && typeof body.data !== 'object') return this.setResponse(400, 'dataNotAnObject')
|
||||
if (!body.design && !body.data?.design) return this.setResponse(400, 'designMissing')
|
||||
if (typeof body.design !== 'string') return this.setResponse(400, 'designNotStringy')
|
||||
|
||||
// Prepare data
|
||||
const data = {
|
||||
design: body.design,
|
||||
personId: body.person,
|
||||
settings: body.settings,
|
||||
}
|
||||
// Data (will be encrypted, so always set _some_ value)
|
||||
if (typeof body.data === 'object') data.data = body.data
|
||||
else data.data = {}
|
||||
// Name (will be encrypted, so always set _some_ value)
|
||||
if (typeof body.name === 'string' && body.name.length > 0) data.name = body.name
|
||||
else data.name = '--'
|
||||
// Notes (will be encrypted, so always set _some_ value)
|
||||
if (typeof body.notes === 'string' && body.notes.length > 0) data.notes = body.notes
|
||||
else data.notes = '--'
|
||||
// Public
|
||||
if (body.public === true) data.public = true
|
||||
data.userId = user.uid
|
||||
// Set this one initially as we need the ID to create a custom img via Sanity
|
||||
data.img = this.config.avatars.pattern
|
||||
|
||||
// Create record
|
||||
await this.unguardedCreate(data)
|
||||
|
||||
// Update img? (now that we have the ID)
|
||||
const img =
|
||||
this.config.use.sanity &&
|
||||
typeof body.img === 'string' &&
|
||||
(!body.unittest || (body.unittest && this.config.use.tests?.sanity))
|
||||
? await setPatternAvatar(this.record.id, body.img)
|
||||
: false
|
||||
|
||||
if (img) await this.unguardedUpdate(this.cloak({ img: img.url }))
|
||||
else await this.read({ id: this.record.id })
|
||||
|
||||
return this.setResponse(201, 'created', { pattern: this.asPattern() })
|
||||
}
|
||||
|
||||
PatternModel.prototype.unguardedCreate = async function (data) {
|
||||
try {
|
||||
this.record = await this.prisma.pattern.create({ data: this.cloak(data) })
|
||||
} catch (err) {
|
||||
log.warn(err, 'Could not create pattern')
|
||||
return this.setResponse(500, 'createPatternFailed')
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a pattern from the database based on the where clause you pass it
|
||||
*
|
||||
* Stores result in this.record
|
||||
*/
|
||||
PatternModel.prototype.read = async function (where) {
|
||||
try {
|
||||
this.record = await this.prisma.pattern.findUnique({ where })
|
||||
} catch (err) {
|
||||
log.warn({ err, where }, 'Could not read pattern')
|
||||
}
|
||||
|
||||
this.reveal()
|
||||
|
||||
return this.setExists()
|
||||
}
|
||||
|
||||
/*
|
||||
* Loads a pattern from the database based on the where clause you pass it
|
||||
* In addition prepares it for returning the pattern data
|
||||
*
|
||||
* Stores result in this.record
|
||||
*/
|
||||
PatternModel.prototype.guardedRead = async function ({ params, user }) {
|
||||
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
if (this.record.userId !== user.uid && user.level < 5) {
|
||||
return this.setResponse(403, 'insufficientAccessLevel')
|
||||
}
|
||||
|
||||
return this.setResponse(200, false, {
|
||||
result: 'success',
|
||||
pattern: this.asPattern(),
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Clones a pattern
|
||||
* In addition prepares it for returning the pattern data
|
||||
*
|
||||
* Stores result in this.record
|
||||
*/
|
||||
PatternModel.prototype.guardedClone = async function ({ params, user }) {
|
||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
if (this.record.userId !== user.uid && !this.record.public && user.level < 5) {
|
||||
return this.setResponse(403, 'insufficientAccessLevel')
|
||||
}
|
||||
|
||||
// Clone pattern
|
||||
const data = this.asPattern()
|
||||
delete data.id
|
||||
data.name += ` (cloned from #${this.record.id})`
|
||||
data.notes += ` (Note: This pattern was cloned from pattern #${this.record.id})`
|
||||
await this.unguardedCreate(data)
|
||||
|
||||
// Update unencrypted data
|
||||
this.reveal()
|
||||
|
||||
return this.setResponse(200, false, {
|
||||
result: 'success',
|
||||
pattern: this.asPattern(),
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to decrypt at-rest data
|
||||
*/
|
||||
PatternModel.prototype.reveal = async function () {
|
||||
this.clear = {}
|
||||
if (this.record) {
|
||||
for (const field of this.encryptedFields) {
|
||||
this.clear[field] = this.decrypt(this.record[field])
|
||||
}
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to encrypt at-rest data
|
||||
*/
|
||||
PatternModel.prototype.cloak = function (data) {
|
||||
for (const field of this.encryptedFields) {
|
||||
if (typeof data[field] !== 'undefined') {
|
||||
data[field] = this.encrypt(data[field])
|
||||
}
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks this.record and sets a boolean to indicate whether
|
||||
* the pattern exists or not
|
||||
*
|
||||
* Stores result in this.exists
|
||||
*/
|
||||
PatternModel.prototype.setExists = function () {
|
||||
this.exists = this.record ? true : false
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the pattern data - Used when we create the data ourselves
|
||||
* so we know it's safe
|
||||
*/
|
||||
PatternModel.prototype.unguardedUpdate = async function (data) {
|
||||
try {
|
||||
this.record = await this.prisma.pattern.update({
|
||||
where: { id: this.record.id },
|
||||
data,
|
||||
})
|
||||
} catch (err) {
|
||||
log.warn(err, 'Could not update pattern record')
|
||||
process.exit()
|
||||
return this.setResponse(500, 'updatePatternFailed')
|
||||
}
|
||||
await this.reveal()
|
||||
|
||||
return this.setResponse(200)
|
||||
}
|
||||
|
||||
/*
|
||||
* Updates the pattern data - Used when we pass through user-provided data
|
||||
* so we can't be certain it's safe
|
||||
*/
|
||||
PatternModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
if (this.record.userId !== user.uid && user.level < 8) {
|
||||
return this.setResponse(403, 'insufficientAccessLevel')
|
||||
}
|
||||
const data = {}
|
||||
// Name
|
||||
if (typeof body.name === 'string') data.name = body.name
|
||||
// Notes
|
||||
if (typeof body.notes === 'string') data.notes = body.notes
|
||||
// Public
|
||||
if (body.public === true || body.public === false) data.public = body.public
|
||||
// Data
|
||||
if (typeof body.data === 'object') data.data = body.data
|
||||
// Settings
|
||||
if (typeof body.settings === 'object') data.settings = body.settings
|
||||
// Image (img)
|
||||
if (typeof body.img === 'string') {
|
||||
const img = await setPatternAvatar(params.id, body.img)
|
||||
data.img = img.url
|
||||
}
|
||||
|
||||
// Now update the record
|
||||
await this.unguardedUpdate(this.cloak(data))
|
||||
|
||||
return this.setResponse(200, false, { pattern: this.asPattern() })
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the pattern - No questions asked
|
||||
*/
|
||||
PatternModel.prototype.unguardedDelete = async function () {
|
||||
await this.prisma.pattern.delete({ here: { id: this.record.id } })
|
||||
this.record = null
|
||||
this.clear = null
|
||||
|
||||
return this.setExists()
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the pattern - Checks permissions
|
||||
*/
|
||||
PatternModel.prototype.guardedDelete = async function ({ params, body, user }) {
|
||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
|
||||
await this.read({ id: parseInt(params.id) })
|
||||
if (this.record.userId !== user.uid && user.level < 8) {
|
||||
return this.setResponse(403, 'insufficientAccessLevel')
|
||||
}
|
||||
|
||||
await this.unguardedDelete()
|
||||
|
||||
return this.setResponse(204, false)
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns record data
|
||||
*/
|
||||
PatternModel.prototype.asPattern = function () {
|
||||
return {
|
||||
...this.record,
|
||||
...this.clear,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to set the response code, result, and body
|
||||
*
|
||||
* Will be used by this.sendResponse()
|
||||
*/
|
||||
PatternModel.prototype.setResponse = function (status = 200, error = false, data = {}) {
|
||||
this.response = {
|
||||
status,
|
||||
body: {
|
||||
result: 'success',
|
||||
...data,
|
||||
},
|
||||
}
|
||||
if (status > 201) {
|
||||
this.response.body.error = error
|
||||
this.response.body.result = 'error'
|
||||
this.error = true
|
||||
} else this.error = false
|
||||
|
||||
return this.setExists()
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper method to send response
|
||||
*/
|
||||
PatternModel.prototype.sendResponse = async function (res) {
|
||||
return res.status(this.response.status).send(this.response.body)
|
||||
}
|
|
@ -19,7 +19,12 @@ PersonModel.prototype.guardedCreate = async function ({ body, user }) {
|
|||
|
||||
// Prepare data
|
||||
const data = { name: body.name }
|
||||
// Name (will be encrypted, so always set _some_ value)
|
||||
if (typeof body.name === 'string') data.name = body.name
|
||||
else data.name = '--'
|
||||
// Notes (will be encrypted, so always set _some_ value)
|
||||
if (body.notes || typeof body.notes === 'string') data.notes = body.notes
|
||||
else data.notes = '--'
|
||||
if (body.public === true) data.public = true
|
||||
if (body.measies) data.measies = this.sanitizeMeasurements(body.measies)
|
||||
data.imperial = body.imperial === true ? true : false
|
||||
|
|
|
@ -223,9 +223,9 @@ UserModel.prototype.guardedCreate = async function ({ body }) {
|
|||
* Login based on username + password
|
||||
*/
|
||||
UserModel.prototype.passwordLogin = async function (req) {
|
||||
if (Object.keys(req.body) < 1) return this.setReponse(400, 'postBodyMissing')
|
||||
if (!req.body.username) return this.setReponse(400, 'usernameMissing')
|
||||
if (!req.body.password) return this.setReponse(400, 'passwordMissing')
|
||||
if (Object.keys(req.body) < 1) return this.setResponse(400, 'postBodyMissing')
|
||||
if (!req.body.username) return this.setResponse(400, 'usernameMissing')
|
||||
if (!req.body.password) return this.setResponse(400, 'passwordMissing')
|
||||
|
||||
await this.find(req.body)
|
||||
if (!this.exists) {
|
||||
|
@ -255,7 +255,7 @@ UserModel.prototype.passwordLogin = async function (req) {
|
|||
* Confirms a user account
|
||||
*/
|
||||
UserModel.prototype.confirm = async function ({ body, params }) {
|
||||
if (!params.id) return this.setReponse(404, 'missingConfirmationId')
|
||||
if (!params.id) return this.setResponse(404, 'missingConfirmationId')
|
||||
if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing')
|
||||
if (!body.consent || typeof body.consent !== 'number' || body.consent < 1)
|
||||
return this.setResponse(400, 'consentRequired')
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
import { ApikeyController } from '../controllers/apikey.mjs'
|
||||
|
||||
const Apikey = new ApikeyController()
|
||||
const jwt = ['jwt', { session: false }]
|
||||
const bsc = ['basic', { session: false }]
|
||||
|
||||
export function apikeyRoutes(tools) {
|
||||
const { app, passport } = tools
|
||||
|
||||
// Create Apikey
|
||||
app.post('/apikey/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Apikey.create(req, res, tools)
|
||||
)
|
||||
app.post('/apikey/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikey.create(req, res, tools)
|
||||
)
|
||||
|
||||
// Read Apikey
|
||||
app.get('/apikey/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Apikey.read(req, res, tools)
|
||||
)
|
||||
app.get('/apikey/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikey.read(req, res, tools)
|
||||
)
|
||||
|
||||
// Read current Apikey
|
||||
app.get('/whoami/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikey.whoami(req, res, tools)
|
||||
)
|
||||
|
||||
// Remove Apikey
|
||||
app.delete('/apikey/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Apikey.delete(req, res, tools)
|
||||
)
|
||||
app.delete('/apikey/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikey.delete(req, res, tools)
|
||||
)
|
||||
}
|
38
sites/backend/src/routes/apikeys.mjs
Normal file
38
sites/backend/src/routes/apikeys.mjs
Normal file
|
@ -0,0 +1,38 @@
|
|||
import { ApikeysController } from '../controllers/apikeys.mjs'
|
||||
|
||||
const Apikeys = new ApikeysController()
|
||||
const jwt = ['jwt', { session: false }]
|
||||
const bsc = ['basic', { session: false }]
|
||||
|
||||
export function apikeysRoutes(tools) {
|
||||
const { app, passport } = tools
|
||||
|
||||
// Create Apikey
|
||||
app.post('/apikeys/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Apikeys.create(req, res, tools)
|
||||
)
|
||||
app.post('/apikeys/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikeys.create(req, res, tools)
|
||||
)
|
||||
|
||||
// Read Apikey
|
||||
app.get('/apikeys/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Apikeys.read(req, res, tools)
|
||||
)
|
||||
app.get('/apikeys/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikeys.read(req, res, tools)
|
||||
)
|
||||
|
||||
// Read current Apikey
|
||||
app.get('/whoami/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikeys.whoami(req, res, tools)
|
||||
)
|
||||
|
||||
// Remove Apikey
|
||||
app.delete('/apikeys/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Apikeys.delete(req, res, tools)
|
||||
)
|
||||
app.delete('/apikeys/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Apikeys.delete(req, res, tools)
|
||||
)
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
import { apikeyRoutes } from './apikey.mjs'
|
||||
import { userRoutes } from './user.mjs'
|
||||
import { personRoutes } from './person.mjs'
|
||||
import { apikeysRoutes } from './apikeys.mjs'
|
||||
import { usersRoutes } from './users.mjs'
|
||||
import { peopleRoutes } from './people.mjs'
|
||||
import { patternsRoutes } from './patterns.mjs'
|
||||
|
||||
export const routes = {
|
||||
apikeyRoutes,
|
||||
userRoutes,
|
||||
personRoutes,
|
||||
apikeysRoutes,
|
||||
usersRoutes,
|
||||
peopleRoutes,
|
||||
patternsRoutes,
|
||||
}
|
||||
|
|
49
sites/backend/src/routes/patterns.mjs
Normal file
49
sites/backend/src/routes/patterns.mjs
Normal file
|
@ -0,0 +1,49 @@
|
|||
import { PatternsController } from '../controllers/patterns.mjs'
|
||||
|
||||
const Patterns = new PatternsController()
|
||||
const jwt = ['jwt', { session: false }]
|
||||
const bsc = ['basic', { session: false }]
|
||||
|
||||
export function patternsRoutes(tools) {
|
||||
const { app, passport } = tools
|
||||
|
||||
// Create pattern
|
||||
app.post('/patterns/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Patterns.create(req, res, tools)
|
||||
)
|
||||
app.post('/patterns/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Patterns.create(req, res, tools)
|
||||
)
|
||||
|
||||
// Clone pattern
|
||||
app.post('/patterns/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Patterns.clone(req, res, tools)
|
||||
)
|
||||
app.post('/patterns/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Patterns.clone(req, res, tools)
|
||||
)
|
||||
|
||||
// Read pattern
|
||||
app.get('/patterns/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Patterns.read(req, res, tools)
|
||||
)
|
||||
app.get('/patterns/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Patterns.read(req, res, tools)
|
||||
)
|
||||
|
||||
// Update pattern
|
||||
app.put('/patterns/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Patterns.update(req, res, tools)
|
||||
)
|
||||
app.put('/patterns/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Patterns.update(req, res, tools)
|
||||
)
|
||||
|
||||
// Delete pattern
|
||||
app.delete('/patterns/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Patterns.delete(req, res, tools)
|
||||
)
|
||||
app.delete('/patterns/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Patterns.delete(req, res, tools)
|
||||
)
|
||||
}
|
|
@ -1,49 +1,49 @@
|
|||
import { PersonController } from '../controllers/person.mjs'
|
||||
import { PeopleController } from '../controllers/people.mjs'
|
||||
|
||||
const Person = new PersonController()
|
||||
const People = new PeopleController()
|
||||
const jwt = ['jwt', { session: false }]
|
||||
const bsc = ['basic', { session: false }]
|
||||
|
||||
export function personRoutes(tools) {
|
||||
export function peopleRoutes(tools) {
|
||||
const { app, passport } = tools
|
||||
|
||||
// Create person
|
||||
app.post('/people/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Person.create(req, res, tools)
|
||||
People.create(req, res, tools)
|
||||
)
|
||||
app.post('/people/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Person.create(req, res, tools)
|
||||
People.create(req, res, tools)
|
||||
)
|
||||
|
||||
// Clone person
|
||||
app.post('/people/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Person.clone(req, res, tools)
|
||||
People.clone(req, res, tools)
|
||||
)
|
||||
app.post('/people/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Person.clone(req, res, tools)
|
||||
People.clone(req, res, tools)
|
||||
)
|
||||
|
||||
// Read person
|
||||
app.get('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Person.read(req, res, tools)
|
||||
People.read(req, res, tools)
|
||||
)
|
||||
app.get('/people/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Person.read(req, res, tools)
|
||||
People.read(req, res, tools)
|
||||
)
|
||||
|
||||
// Update person
|
||||
app.put('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Person.update(req, res, tools)
|
||||
People.update(req, res, tools)
|
||||
)
|
||||
app.put('/people/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Person.update(req, res, tools)
|
||||
People.update(req, res, tools)
|
||||
)
|
||||
|
||||
// Delete person
|
||||
app.delete('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Person.delete(req, res, tools)
|
||||
People.delete(req, res, tools)
|
||||
)
|
||||
app.delete('/people/:id/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Person.delete(req, res, tools)
|
||||
People.delete(req, res, tools)
|
||||
)
|
||||
}
|
|
@ -1,30 +1,38 @@
|
|||
import { UserController } from '../controllers/user.mjs'
|
||||
import { UsersController } from '../controllers/users.mjs'
|
||||
|
||||
const User = new UserController()
|
||||
const Users = new UsersController()
|
||||
const jwt = ['jwt', { session: false }]
|
||||
const bsc = ['basic', { session: false }]
|
||||
|
||||
export function userRoutes(tools) {
|
||||
export function usersRoutes(tools) {
|
||||
const { app, passport } = tools
|
||||
|
||||
// Sign up
|
||||
app.post('/signup', (req, res) => User.signup(req, res, tools))
|
||||
app.post('/signup', (req, res) => Users.signup(req, res, tools))
|
||||
|
||||
// Confirm account
|
||||
app.post('/confirm/signup/:id', (req, res) => User.confirm(req, res, tools))
|
||||
app.post('/confirm/signup/:id', (req, res) => Users.confirm(req, res, tools))
|
||||
|
||||
// Login
|
||||
app.post('/login', (req, res) => User.login(req, res, tools))
|
||||
app.post('/login', (req, res) => Users.login(req, res, tools))
|
||||
|
||||
// Read current jwt
|
||||
|
||||
app.get('/whoami/jwt', passport.authenticate(...jwt), (req, res) => User.whoami(req, res, tools))
|
||||
app.get('/account/jwt', passport.authenticate(...jwt), (req, res) => User.whoami(req, res, tools))
|
||||
app.get('/account/key', passport.authenticate(...bsc), (req, res) => User.whoami(req, res, tools))
|
||||
app.get('/whoami/jwt', passport.authenticate(...jwt), (req, res) => Users.whoami(req, res, tools))
|
||||
app.get('/account/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Users.whoami(req, res, tools)
|
||||
)
|
||||
app.get('/account/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Users.whoami(req, res, tools)
|
||||
)
|
||||
|
||||
// Update account
|
||||
app.put('/account/jwt', passport.authenticate(...jwt), (req, res) => User.update(req, res, tools))
|
||||
app.put('/account/key', passport.authenticate(...bsc), (req, res) => User.update(req, res, tools))
|
||||
app.put('/account/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||
Users.update(req, res, tools)
|
||||
)
|
||||
app.put('/account/key', passport.authenticate(...bsc), (req, res) =>
|
||||
Users.update(req, res, tools)
|
||||
)
|
||||
|
||||
/*
|
||||
|
|
@ -33,6 +33,7 @@ async function getAvatar(type, id) {
|
|||
*/
|
||||
export const setUserAvatar = async (id, data) => setAvatar('user', id, data)
|
||||
export const setPersonAvatar = async (id, data) => setAvatar('person', id, data)
|
||||
export const setPatternAvatar = async (id, data) => setAvatar('pattern', id, data)
|
||||
export async function setAvatar(type, id, data) {
|
||||
// Step 1: Upload the image as asset
|
||||
const [contentType, binary] = b64ToBinaryWithType(data)
|
||||
|
|
|
@ -3,7 +3,7 @@ export const apikeyTests = async (chai, config, expect, store) => {
|
|||
step(`${store.icon('key', 'jwt')} Create API Key (jwt)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/apikey/jwt')
|
||||
.post('/apikeys/jwt')
|
||||
.set('Authorization', 'Bearer ' + store.account.token)
|
||||
.send({
|
||||
name: 'Test API key',
|
||||
|
@ -27,7 +27,7 @@ export const apikeyTests = async (chai, config, expect, store) => {
|
|||
step(`${store.icon('key', 'key')} Create API Key (key)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/apikey/key')
|
||||
.post('/apikeys/key')
|
||||
.auth(store.apikey1.key, store.apikey1.secret)
|
||||
.send({
|
||||
name: 'Test API key with key',
|
||||
|
@ -67,7 +67,7 @@ export const apikeyTests = async (chai, config, expect, store) => {
|
|||
step(`${store.icon('key', 'key')} Read API key (key)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/apikey/${store.apikey1.key}/key`)
|
||||
.get(`/apikeys/${store.apikey1.key}/key`)
|
||||
.auth(store.apikey2.key, store.apikey2.secret)
|
||||
.end((err, res) => {
|
||||
expect(res.status).to.equal(200)
|
||||
|
@ -83,7 +83,7 @@ export const apikeyTests = async (chai, config, expect, store) => {
|
|||
step(`${store.icon('key', 'jwt')} Read API key (jwt)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/apikey/${store.apikey2.key}/jwt`)
|
||||
.get(`/apikeys/${store.apikey2.key}/jwt`)
|
||||
.set('Authorization', 'Bearer ' + store.account.token)
|
||||
.end((err, res) => {
|
||||
expect(res.status).to.equal(200)
|
||||
|
@ -99,7 +99,7 @@ export const apikeyTests = async (chai, config, expect, store) => {
|
|||
step(`${store.icon('key', 'key')} Remove API key (key)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.delete(`/apikey/${store.apikey2.key}/key`)
|
||||
.delete(`/apikeys/${store.apikey2.key}/key`)
|
||||
.auth(store.apikey2.key, store.apikey2.secret)
|
||||
.end((err, res) => {
|
||||
expect(res.status).to.equal(204)
|
||||
|
@ -110,7 +110,7 @@ export const apikeyTests = async (chai, config, expect, store) => {
|
|||
step(`${store.icon('key', 'jwt')} Remove API key (jwt)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.delete(`/apikey/${store.apikey1.key}/jwt`)
|
||||
.delete(`/apikeys/${store.apikey1.key}/jwt`)
|
||||
.set('Authorization', 'Bearer ' + store.account.token)
|
||||
.end((err, res) => {
|
||||
expect(res.status).to.equal(204)
|
||||
|
|
|
@ -2,6 +2,7 @@ import { userTests } from './user.mjs'
|
|||
import { accountTests } from './account.mjs'
|
||||
import { apikeyTests } from './apikey.mjs'
|
||||
import { personTests } from './person.mjs'
|
||||
import { patternTests } from './pattern.mjs'
|
||||
import { setup } from './shared.mjs'
|
||||
|
||||
const runTests = async (...params) => {
|
||||
|
@ -9,6 +10,7 @@ const runTests = async (...params) => {
|
|||
await apikeyTests(...params)
|
||||
await accountTests(...params)
|
||||
await personTests(...params)
|
||||
await patternTests(...params)
|
||||
}
|
||||
|
||||
// Load initial data required for tests
|
||||
|
|
326
sites/backend/tests/pattern.mjs
Normal file
326
sites/backend/tests/pattern.mjs
Normal file
|
@ -0,0 +1,326 @@
|
|||
import { cat } from './cat.mjs'
|
||||
|
||||
export const patternTests = async (chai, config, expect, store) => {
|
||||
store.account.patterns = {}
|
||||
for (const auth of ['jwt', 'key']) {
|
||||
describe(`${store.icon('pattern', auth)} Pattern tests (${auth})`, () => {
|
||||
it(`${store.icon('pattern', auth)} Should create a new pattern (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post(`/patterns/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({
|
||||
design: 'aaron',
|
||||
settings: {},
|
||||
person: store.account.people.her.id,
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(201)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(typeof res.body.pattern?.id).to.equal('number')
|
||||
expect(res.body.pattern.userId).to.equal(store.account.id)
|
||||
expect(res.body.pattern.personId).to.equal(store.account.people.her.id)
|
||||
expect(res.body.pattern.design).to.equal('aaron')
|
||||
expect(res.body.pattern.public).to.equal(false)
|
||||
store.account.patterns[auth] = res.body.pattern
|
||||
done()
|
||||
})
|
||||
}).timeout(5000)
|
||||
|
||||
for (const field of ['name', 'notes']) {
|
||||
it(`${store.icon('pattern', auth)} Should update the ${field} field (${auth})`, (done) => {
|
||||
const data = {}
|
||||
const val = store.account.patterns[auth][field] + '_updated'
|
||||
data[field] = val
|
||||
chai
|
||||
.request(config.api)
|
||||
.put(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.account.apikey.key}:${store.account.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.send(data)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.pattern[field]).to.equal('--_updated')
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it(`${store.icon('person', auth)} Should update the public field (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.put(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({ public: true })
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.pattern.public).to.equal(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('person', auth)} Should not update the design field (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.put(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({ design: 'updated' })
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.pattern.design).to.equal('aaron')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('person', auth)} Should not update the person field (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.put(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({ person: 1 })
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.pattern.personId).to.equal(store.account.people.her.id)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
for (const field of ['data', 'settings']) {
|
||||
it(`${store.icon('person', auth)} Should update the ${field} field (${auth})`, (done) => {
|
||||
const data = {}
|
||||
data[field] = { test: { value: 'hello' } }
|
||||
chai
|
||||
.request(config.api)
|
||||
.put(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.account.apikey.key}:${store.account.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.send(data)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.pattern[field].test.value).to.equal('hello')
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it(`${store.icon('pattern', auth)} Should read a pattern (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(res.body.pattern.data.test.value).to.equal('hello')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon(
|
||||
'person',
|
||||
auth
|
||||
)} Should not allow reading another user's pattern (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.get(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.altaccount.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.altaccount.apikey.key}:${store.altaccount.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(403)
|
||||
expect(res.body.result).to.equal(`error`)
|
||||
expect(res.body.error).to.equal(`insufficientAccessLevel`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon(
|
||||
'person',
|
||||
auth
|
||||
)} Should not allow updating another user's pattern (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.put(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.altaccount.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.altaccount.apikey.key}:${store.altaccount.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.send({
|
||||
name: 'I have been taken over',
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(403)
|
||||
expect(res.body.result).to.equal(`error`)
|
||||
expect(res.body.error).to.equal(`insufficientAccessLevel`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon(
|
||||
'person',
|
||||
auth
|
||||
)} Should not allow removing another user's pattern (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.delete(`/patterns/${store.account.patterns[auth].id}/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.altaccount.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.altaccount.apikey.key}:${store.altaccount.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(403)
|
||||
expect(res.body.result).to.equal(`error`)
|
||||
expect(res.body.error).to.equal(`insufficientAccessLevel`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
/*
|
||||
it(`${store.icon('person', auth)} Should clone a person (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post(`/people/${store.person[auth].id}/clone/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.account.token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${store.account.apikey.key}:${store.account.apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(typeof res.body.error).to.equal(`undefined`)
|
||||
expect(typeof res.body.person.id).to.equal(`number`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon(
|
||||
'person',
|
||||
auth
|
||||
)} Should (not) clone a public person across accounts (${auth})`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post(`/people/${store.person[auth].id}/clone/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + store.altaccount.token
|
||||
: 'Basic ' +
|
||||
new Buffer(
|
||||
`${store.altaccount.apikey.key}:${store.altaccount.apikey.secret}`
|
||||
).toString('base64')
|
||||
)
|
||||
.end((err, res) => {
|
||||
if (store.person[auth].public) {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
expect(typeof res.body.error).to.equal(`undefined`)
|
||||
expect(typeof res.body.person.id).to.equal(`number`)
|
||||
} else {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(403)
|
||||
expect(res.body.result).to.equal(`error`)
|
||||
expect(res.body.error).to.equal(`insufficientAccessLevel`)
|
||||
}
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
// TODO:
|
||||
// - Clone person
|
||||
// - Clone person accross accounts of they are public
|
||||
*/
|
||||
})
|
||||
}
|
||||
}
|
|
@ -4,12 +4,17 @@ import chai from 'chai'
|
|||
import http from 'chai-http'
|
||||
import { verifyConfig } from '../src/config.mjs'
|
||||
import { randomString } from '../src/utils/crypto.mjs'
|
||||
import {
|
||||
cisFemaleAdult34 as her,
|
||||
cisMaleAdult42 as him,
|
||||
} from '../../../packages/models/src/index.mjs'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
const config = verifyConfig(true)
|
||||
const expect = chai.expect
|
||||
chai.use(http)
|
||||
const people = { her, him }
|
||||
|
||||
export const setup = async () => {
|
||||
// Initial store contents
|
||||
|
@ -21,17 +26,20 @@ export const setup = async () => {
|
|||
email: `test_${randomString()}@${config.tests.domain}`,
|
||||
language: 'en',
|
||||
password: randomString(),
|
||||
people: {},
|
||||
},
|
||||
altaccount: {
|
||||
email: `test_${randomString()}@${config.tests.domain}`,
|
||||
language: 'en',
|
||||
password: randomString(),
|
||||
people: {},
|
||||
},
|
||||
icons: {
|
||||
user: '🧑 ',
|
||||
jwt: '🎫 ',
|
||||
key: '🎟️ ',
|
||||
person: '🧕 ',
|
||||
pattern: '👕 ',
|
||||
},
|
||||
randomString,
|
||||
}
|
||||
|
@ -63,12 +71,12 @@ export const setup = async () => {
|
|||
}
|
||||
store[acc].token = result.data.token
|
||||
store[acc].username = result.data.account.username
|
||||
store[acc].userid = result.data.account.id
|
||||
store[acc].id = result.data.account.id
|
||||
|
||||
// Create API key
|
||||
try {
|
||||
result = await axios.post(
|
||||
`${store.config.api}/apikey/jwt`,
|
||||
`${store.config.api}/apikeys/jwt`,
|
||||
{
|
||||
name: 'Test API key',
|
||||
level: 4,
|
||||
|
@ -85,6 +93,29 @@ export const setup = async () => {
|
|||
process.exit()
|
||||
}
|
||||
store[acc].apikey = result.data.apikey
|
||||
|
||||
// Create people key
|
||||
for (const name in people) {
|
||||
try {
|
||||
result = await axios.post(
|
||||
`${store.config.api}/people/jwt`,
|
||||
{
|
||||
name: `This is ${name} name`,
|
||||
name: `These are ${name} notes`,
|
||||
measies: people[name],
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
authorization: `Bearer ${store[acc].token}`,
|
||||
},
|
||||
}
|
||||
)
|
||||
} catch (err) {
|
||||
console.log('Failed at API key creation request', err)
|
||||
process.exit()
|
||||
}
|
||||
store[acc].people[name] = result.data.person
|
||||
}
|
||||
}
|
||||
|
||||
return { chai, config, expect, store }
|
||||
|
|
|
@ -184,12 +184,12 @@ export const userTests = async (chai, config, expect, store) => {
|
|||
})
|
||||
})
|
||||
|
||||
step(`${store.icon('user')} Should login with userid and password`, (done) => {
|
||||
step(`${store.icon('user')} Should login with id and password`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/login')
|
||||
.send({
|
||||
username: store.account.userid,
|
||||
username: store.account.id,
|
||||
password: store.account.password,
|
||||
})
|
||||
.end((err, res) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue