wip(backend): More access control guarding
This commit is contained in:
parent
bc5a605c9b
commit
e37548fcf7
9 changed files with 325 additions and 130 deletions
|
@ -4,7 +4,6 @@ export function PersonController() {}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a person for the authenticated user
|
* Create a person for the authenticated user
|
||||||
*
|
|
||||||
* See: https://freesewing.dev/reference/backend/api
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
*/
|
*/
|
||||||
PersonController.prototype.create = async (req, res, tools) => {
|
PersonController.prototype.create = async (req, res, tools) => {
|
||||||
|
@ -16,34 +15,44 @@ PersonController.prototype.create = async (req, res, tools) => {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Read a person
|
* Read a person
|
||||||
*
|
|
||||||
* See: https://freesewing.dev/reference/backend/api
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
*/
|
*/
|
||||||
PersonController.prototype.read = async (req, res, tools) => {
|
PersonController.prototype.read = async (req, res, tools) => {
|
||||||
//const Person = new PersonModel(tools)
|
const Person = new PersonModel(tools)
|
||||||
//await Person.read({ id: req.params.id })
|
await Person.readForReturn({ id: parseInt(req.params.id) }, req.user)
|
||||||
//return Person.sendResponse(res)
|
|
||||||
|
return Person.sendResponse(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update a person
|
* Update a person
|
||||||
*
|
|
||||||
* See: https://freesewing.dev/reference/backend/api
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
*/
|
*/
|
||||||
PersonController.prototype.update = async (req, res, tools) => {
|
PersonController.prototype.update = async (req, res, tools) => {
|
||||||
const Person = new PersonModel(tools)
|
const Person = new PersonModel(tools)
|
||||||
await Person.unsafeUpdate(req)
|
await Person.guardedUpdate(req)
|
||||||
|
|
||||||
return Person.sendResponse(res)
|
return Person.sendResponse(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove a person
|
* Remove a person
|
||||||
*
|
|
||||||
* See: https://freesewing.dev/reference/backend/api
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
*/
|
*/
|
||||||
PersonController.prototype.delete = async (req, res, tools) => {
|
PersonController.prototype.delete = async (req, res, tools) => {
|
||||||
//const Person = new PersonModel(tools)
|
const Person = new PersonModel(tools)
|
||||||
//await Person.remove(req)
|
await Person.guardedDelete(req)
|
||||||
//return Person.sendResponse(res)
|
|
||||||
|
return Person.sendResponse(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Clone a person
|
||||||
|
* See: https://freesewing.dev/reference/backend/api
|
||||||
|
*/
|
||||||
|
//PersonController.prototype.clone = async (req, res, tools) => {
|
||||||
|
// const Person = new PersonModel(tools)
|
||||||
|
// await Person.unsafeUpdate(req)
|
||||||
|
//
|
||||||
|
// return Person.sendResponse(res)
|
||||||
|
//}
|
||||||
|
|
|
@ -48,7 +48,7 @@ UserController.prototype.login = async function (req, res, tools) {
|
||||||
*/
|
*/
|
||||||
UserController.prototype.whoami = async (req, res, tools) => {
|
UserController.prototype.whoami = async (req, res, tools) => {
|
||||||
const User = new UserModel(tools)
|
const User = new UserModel(tools)
|
||||||
await User.readAsAccount({ id: req.user.uid })
|
await User.readForReturn({ id: req.user.uid })
|
||||||
|
|
||||||
return User.sendResponse(res)
|
return User.sendResponse(res)
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ UserController.prototype.whoami = async (req, res, tools) => {
|
||||||
UserController.prototype.update = async (req, res, tools) => {
|
UserController.prototype.update = async (req, res, tools) => {
|
||||||
const User = new UserModel(tools)
|
const User = new UserModel(tools)
|
||||||
await User.read({ id: req.user.uid })
|
await User.read({ id: req.user.uid })
|
||||||
await User.unsafeUpdate(req.body)
|
await User.guardedUpdate(req.body, req.user)
|
||||||
|
|
||||||
return User.sendResponse(res)
|
return User.sendResponse(res)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ PersonModel.prototype.create = async function ({ body, user }) {
|
||||||
data.imperial = body.imperial === true ? true : false
|
data.imperial = body.imperial === true ? true : false
|
||||||
data.userId = user.uid
|
data.userId = user.uid
|
||||||
// Set this one initially as we need the ID to create a custom img via Sanity
|
// Set this one initially as we need the ID to create a custom img via Sanity
|
||||||
data.img = this.encrypt(this.config.avatars.person)
|
data.img = this.config.avatars.person
|
||||||
|
|
||||||
// Create record
|
// Create record
|
||||||
try {
|
try {
|
||||||
|
@ -43,7 +43,7 @@ PersonModel.prototype.create = async function ({ body, user }) {
|
||||||
? await setPersonAvatar(this.record.id, body.img)
|
? await setPersonAvatar(this.record.id, body.img)
|
||||||
: false
|
: false
|
||||||
|
|
||||||
if (img) await this.safeUpdate(this.cloak({ img: img.url }))
|
if (img) await this.unguardedUpdate(this.cloak({ img: img.url }))
|
||||||
else await this.read({ id: this.record.id })
|
else await this.read({ id: this.record.id })
|
||||||
|
|
||||||
return this.setResponse(201, 'created', { person: this.asPerson() })
|
return this.setResponse(201, 'created', { person: this.asPerson() })
|
||||||
|
@ -66,6 +66,27 @@ PersonModel.prototype.read = async function (where) {
|
||||||
return this.setExists()
|
return this.setExists()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loads a person from the database based on the where clause you pass it
|
||||||
|
* In addition prepares it for returning the person data
|
||||||
|
*
|
||||||
|
* Stores result in this.record
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.readForReturn = async function (where, user) {
|
||||||
|
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
|
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||||
|
|
||||||
|
await this.read(where)
|
||||||
|
if (this.record.userId !== user.uid && user.level < 5) {
|
||||||
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.setResponse(200, false, {
|
||||||
|
result: 'success',
|
||||||
|
person: this.asPerson(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper method to decrypt at-rest data
|
* Helper method to decrypt at-rest data
|
||||||
*/
|
*/
|
||||||
|
@ -85,55 +106,14 @@ PersonModel.prototype.reveal = async function () {
|
||||||
*/
|
*/
|
||||||
PersonModel.prototype.cloak = function (data) {
|
PersonModel.prototype.cloak = function (data) {
|
||||||
for (const field of this.encryptedFields) {
|
for (const field of this.encryptedFields) {
|
||||||
if (typeof data[field] !== 'undefined') data[field] = this.encrypt(data[field])
|
if (typeof data[field] !== 'undefined') {
|
||||||
|
data[field] = this.encrypt(data[field])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Loads a user from the database based on the where clause you pass it
|
|
||||||
* In addition prepares it for returning the account data
|
|
||||||
*
|
|
||||||
* Stores result in this.record
|
|
||||||
*/
|
|
||||||
//UserModel.prototype.readAsAccount = async function (where) {
|
|
||||||
// await this.read(where)
|
|
||||||
//
|
|
||||||
// return this.setResponse(200, false, {
|
|
||||||
// result: 'success',
|
|
||||||
// account: this.asAccount(),
|
|
||||||
// })
|
|
||||||
//}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Finds a user based on one of the accepted unique fields which are:
|
|
||||||
* - lusername (lowercase username)
|
|
||||||
* - ehash
|
|
||||||
* - id
|
|
||||||
*
|
|
||||||
* Stores result in this.record
|
|
||||||
*/
|
|
||||||
//UserModel.prototype.find = async function (body) {
|
|
||||||
// try {
|
|
||||||
// this.record = await this.prisma.user.findFirst({
|
|
||||||
// where: {
|
|
||||||
// OR: [
|
|
||||||
// { lusername: { equals: clean(body.username) } },
|
|
||||||
// { ehash: { equals: hash(clean(body.username)) } },
|
|
||||||
// { id: { equals: parseInt(body.username) || -1 } },
|
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
// })
|
|
||||||
// } catch (err) {
|
|
||||||
// log.warn({ err, body }, `Error while trying to find user: ${body.username}`)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// this.reveal()
|
|
||||||
//
|
|
||||||
// return this.setExists()
|
|
||||||
//}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Checks this.record and sets a boolean to indicate whether
|
* Checks this.record and sets a boolean to indicate whether
|
||||||
* the user exists or not
|
* the user exists or not
|
||||||
|
@ -150,7 +130,7 @@ PersonModel.prototype.setExists = function () {
|
||||||
* Updates the person data - Used when we create the data ourselves
|
* Updates the person data - Used when we create the data ourselves
|
||||||
* so we know it's safe
|
* so we know it's safe
|
||||||
*/
|
*/
|
||||||
PersonModel.prototype.safeUpdate = async function (data) {
|
PersonModel.prototype.unguardedUpdate = async function (data) {
|
||||||
try {
|
try {
|
||||||
this.record = await this.prisma.person.update({
|
this.record = await this.prisma.person.update({
|
||||||
where: { id: this.record.id },
|
where: { id: this.record.id },
|
||||||
|
@ -170,14 +150,14 @@ PersonModel.prototype.safeUpdate = async function (data) {
|
||||||
* Updates the person data - Used when we pass through user-provided data
|
* Updates the person data - Used when we pass through user-provided data
|
||||||
* so we can't be certain it's safe
|
* so we can't be certain it's safe
|
||||||
*/
|
*/
|
||||||
PersonModel.prototype.unsafeUpdate = async function ({ params, body, user }) {
|
PersonModel.prototype.guardedUpdate = async function ({ params, body, user }) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
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) })
|
await this.read({ id: parseInt(params.id) })
|
||||||
if (user.uid !== this.record.userId) return this.setResponse(403, 'accessDenied')
|
if (this.record.userId !== user.uid && user.level < 8) {
|
||||||
|
return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
|
}
|
||||||
const data = {}
|
const data = {}
|
||||||
/*
|
|
||||||
img String?
|
|
||||||
*/
|
|
||||||
// Imperial
|
// Imperial
|
||||||
if (body.imperial === true || body.imperial === false) data.imperial = body.imperial
|
if (body.imperial === true || body.imperial === false) data.imperial = body.imperial
|
||||||
// Name
|
// Name
|
||||||
|
@ -189,11 +169,15 @@ PersonModel.prototype.unsafeUpdate = async function ({ params, body, user }) {
|
||||||
// Measurements
|
// Measurements
|
||||||
const measies = {}
|
const measies = {}
|
||||||
if (typeof body.measies === 'object') {
|
if (typeof body.measies === 'object') {
|
||||||
|
const remove = []
|
||||||
for (const [key, val] of Object.entries(body.measies)) {
|
for (const [key, val] of Object.entries(body.measies)) {
|
||||||
if (this.config.measies.includes(key) && typeof val === 'number' && val > 0)
|
if (this.config.measies.includes(key)) {
|
||||||
measies[key] = val
|
if (val === null) remove.push(key)
|
||||||
|
else if (typeof val == 'number' && val > 0) measies[key] = val
|
||||||
|
}
|
||||||
}
|
}
|
||||||
data.measies = { ...this.clear.measies, ...measies }
|
data.measies = { ...this.clear.measies, ...measies }
|
||||||
|
for (const key of remove) delete data.measies[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image (img)
|
// Image (img)
|
||||||
|
@ -203,11 +187,39 @@ PersonModel.prototype.unsafeUpdate = async function ({ params, body, user }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now update the record
|
// Now update the record
|
||||||
await this.safeUpdate(this.cloak(data))
|
await this.unguardedUpdate(this.cloak(data))
|
||||||
|
|
||||||
return this.setResponse(200, false, { person: this.asPerson() })
|
return this.setResponse(200, false, { person: this.asPerson() })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Removes the person - No questions asked
|
||||||
|
*/
|
||||||
|
PersonModel.prototype.unguardedDelete = async function () {
|
||||||
|
await this.prisma.person.delete({ here: { id: this.record.id } })
|
||||||
|
this.record = null
|
||||||
|
this.clear = null
|
||||||
|
|
||||||
|
return this.setExists()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Removes the person - Checks permissions
|
||||||
|
*/
|
||||||
|
PersonModel.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
|
* Returns record data
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -67,7 +67,7 @@ UserModel.prototype.cloak = function (data) {
|
||||||
*
|
*
|
||||||
* Stores result in this.record
|
* Stores result in this.record
|
||||||
*/
|
*/
|
||||||
UserModel.prototype.readAsAccount = async function (where) {
|
UserModel.prototype.readForReturn = async function (where) {
|
||||||
await this.read(where)
|
await this.read(where)
|
||||||
|
|
||||||
return this.setResponse(200, false, {
|
return this.setResponse(200, false, {
|
||||||
|
@ -175,7 +175,7 @@ UserModel.prototype.create = async function ({ body }) {
|
||||||
|
|
||||||
// Update username
|
// Update username
|
||||||
try {
|
try {
|
||||||
await this.safeUpdate({
|
await this.unguardedUpdate({
|
||||||
username: `user-${this.record.id}`,
|
username: `user-${this.record.id}`,
|
||||||
lusername: `user-${this.record.id}`,
|
lusername: `user-${this.record.id}`,
|
||||||
})
|
})
|
||||||
|
@ -243,7 +243,7 @@ UserModel.prototype.passwordLogin = async function (req) {
|
||||||
// Login success
|
// Login success
|
||||||
if (updatedPasswordField) {
|
if (updatedPasswordField) {
|
||||||
// Update the password field with a v3 hash
|
// Update the password field with a v3 hash
|
||||||
await this.safeUpdate({ password: updatedPasswordField })
|
await this.unguardedUpdate({ password: updatedPasswordField })
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.isOk() ? this.loginOk() : this.setResponse(401, 'loginFailed')
|
return this.isOk() ? this.loginOk() : this.setResponse(401, 'loginFailed')
|
||||||
|
@ -283,7 +283,7 @@ UserModel.prototype.confirm = async function ({ body, params }) {
|
||||||
if (this.error) return this
|
if (this.error) return this
|
||||||
|
|
||||||
// Update user status, consent, and last login
|
// Update user status, consent, and last login
|
||||||
await this.safeUpdate({
|
await this.unguardedUpdate({
|
||||||
status: 1,
|
status: 1,
|
||||||
consent: body.consent,
|
consent: body.consent,
|
||||||
lastLogin: new Date(),
|
lastLogin: new Date(),
|
||||||
|
@ -298,7 +298,7 @@ UserModel.prototype.confirm = async function ({ body, params }) {
|
||||||
* Updates the user data - Used when we create the data ourselves
|
* Updates the user data - Used when we create the data ourselves
|
||||||
* so we know it's safe
|
* so we know it's safe
|
||||||
*/
|
*/
|
||||||
UserModel.prototype.safeUpdate = async function (data) {
|
UserModel.prototype.unguardedUpdate = async function (data) {
|
||||||
try {
|
try {
|
||||||
this.record = await this.prisma.user.update({
|
this.record = await this.prisma.user.update({
|
||||||
where: { id: this.record.id },
|
where: { id: this.record.id },
|
||||||
|
@ -318,7 +318,7 @@ UserModel.prototype.safeUpdate = async function (data) {
|
||||||
* Updates the user data - Used when we pass through user-provided data
|
* Updates the user data - Used when we pass through user-provided data
|
||||||
* so we can't be certain it's safe
|
* so we can't be certain it's safe
|
||||||
*/
|
*/
|
||||||
UserModel.prototype.unsafeUpdate = async function (body) {
|
UserModel.prototype.guardedUpdate = async function (body, user) {
|
||||||
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
|
||||||
const data = {}
|
const data = {}
|
||||||
// Bio
|
// Bio
|
||||||
|
@ -353,7 +353,7 @@ UserModel.prototype.unsafeUpdate = async function (body) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now update the record
|
// Now update the record
|
||||||
await this.safeUpdate(this.cloak(data))
|
await this.unguardedUpdate(this.cloak(data))
|
||||||
|
|
||||||
const isUnitTest = this.isUnitTest(body)
|
const isUnitTest = this.isUnitTest(body)
|
||||||
if (typeof body.email === 'string' && this.clear.email !== clean(body.email)) {
|
if (typeof body.email === 'string' && this.clear.email !== clean(body.email)) {
|
||||||
|
@ -399,7 +399,7 @@ UserModel.prototype.unsafeUpdate = async function (body) {
|
||||||
|
|
||||||
const data = this.Confirmation.clear.data
|
const data = this.Confirmation.clear.data
|
||||||
if (data.email.current === this.clear.email && typeof data.email.new === 'string') {
|
if (data.email.current === this.clear.email && typeof data.email.new === 'string') {
|
||||||
await this.safeUpdate({
|
await this.unguardedUpdate({
|
||||||
email: this.encrypt(data.email.new),
|
email: this.encrypt(data.email.new),
|
||||||
ehash: hash(clean(data.email.new)),
|
ehash: hash(clean(data.email.new)),
|
||||||
})
|
})
|
||||||
|
|
|
@ -31,6 +31,14 @@ export function personRoutes(tools) {
|
||||||
Person.update(req, res, tools)
|
Person.update(req, res, tools)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Clone person
|
||||||
|
app.put('/people/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||||
|
Person.clone(req, res, tools)
|
||||||
|
)
|
||||||
|
app.put('/people/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
|
||||||
|
Person.clone(req, res, tools)
|
||||||
|
)
|
||||||
|
|
||||||
// Delete person
|
// Delete person
|
||||||
app.delete('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
app.delete('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
|
||||||
Person.delete(req, res, tools)
|
Person.delete(req, res, tools)
|
||||||
|
|
|
@ -85,7 +85,6 @@ export const encryption = (stringKey, salt = 'FreeSewing') => {
|
||||||
if (!data.iv || typeof data.ct === 'undefined') {
|
if (!data.iv || typeof data.ct === 'undefined') {
|
||||||
throw 'Encrypted data passed to decrypt() was malformed'
|
throw 'Encrypted data passed to decrypt() was malformed'
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The thing that does the decrypting
|
* The thing that does the decrypting
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -5,9 +5,9 @@ import { personTests } from './person.mjs'
|
||||||
import { setup } from './shared.mjs'
|
import { setup } from './shared.mjs'
|
||||||
|
|
||||||
const runTests = async (...params) => {
|
const runTests = async (...params) => {
|
||||||
//await userTests(...params)
|
await userTests(...params)
|
||||||
//await apikeyTests(...params)
|
await apikeyTests(...params)
|
||||||
//await accountTests(...params)
|
await accountTests(...params)
|
||||||
await personTests(...params)
|
await personTests(...params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,10 @@ export const personTests = async (chai, config, expect, store) => {
|
||||||
jwt: {},
|
jwt: {},
|
||||||
key: {},
|
key: {},
|
||||||
}
|
}
|
||||||
|
store.altperson = {
|
||||||
|
jwt: {},
|
||||||
|
key: {},
|
||||||
|
}
|
||||||
|
|
||||||
for (const auth of ['jwt', 'key']) {
|
for (const auth of ['jwt', 'key']) {
|
||||||
describe(`${store.icon('person', auth)} Person tests (${auth})`, () => {
|
describe(`${store.icon('person', auth)} Person tests (${auth})`, () => {
|
||||||
|
@ -146,13 +150,169 @@ export const personTests = async (chai, config, expect, store) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
it(`${store.icon(
|
||||||
|
'person',
|
||||||
|
auth
|
||||||
|
)} Should not set an non-existing measurement (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.put(`/people/${store.person[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({
|
||||||
|
measies: {
|
||||||
|
ankle: 320,
|
||||||
|
potatoe: 12,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.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.person.measies.ankle).to.equal(320)
|
||||||
|
expect(typeof res.body.person.measies.potatoe).to.equal('undefined')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`${store.icon('person', auth)} Should clear a measurement (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.put(`/people/${store.person[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({
|
||||||
|
measies: {
|
||||||
|
chest: null,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.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.person.measies.chest).to.equal('undefined')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`${store.icon('person', auth)} Should read a person (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.get(`/people/${store.person[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(typeof res.body.person.measies).to.equal('object')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`${store.icon(
|
||||||
|
'person',
|
||||||
|
auth
|
||||||
|
)} Should not allow reading another user's person (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.get(`/people/${store.person[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 person (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.put(`/people/${store.person[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({
|
||||||
|
bio: '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 person (${auth})`, (done) => {
|
||||||
|
chai
|
||||||
|
.request(config.api)
|
||||||
|
.delete(`/people/${store.person[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()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
// - Add non-existing measurement
|
|
||||||
// - Clear measurement
|
|
||||||
// - List/get person
|
|
||||||
// - Clone person
|
// - Clone person
|
||||||
// - Clone person accross accounts of they are public
|
// - Clone person accross accounts of they are public
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,11 @@ export const setup = async () => {
|
||||||
language: 'en',
|
language: 'en',
|
||||||
password: randomString(),
|
password: randomString(),
|
||||||
},
|
},
|
||||||
|
altaccount: {
|
||||||
|
email: `test_${randomString()}@${config.tests.domain}`,
|
||||||
|
language: 'en',
|
||||||
|
password: randomString(),
|
||||||
|
},
|
||||||
icons: {
|
icons: {
|
||||||
user: '🧑 ',
|
user: '🧑 ',
|
||||||
jwt: '🎫 ',
|
jwt: '🎫 ',
|
||||||
|
@ -32,32 +37,33 @@ export const setup = async () => {
|
||||||
}
|
}
|
||||||
store.icon = (icon1, icon2 = false) => store.icons[icon1] + (icon2 ? store.icons[icon2] : '')
|
store.icon = (icon1, icon2 = false) => store.icons[icon1] + (icon2 ? store.icons[icon2] : '')
|
||||||
|
|
||||||
|
for (const acc of ['account', 'altaccount']) {
|
||||||
// Get confirmation ID
|
// Get confirmation ID
|
||||||
let result
|
let result
|
||||||
try {
|
try {
|
||||||
result = await axios.post(`${store.config.api}/signup`, {
|
result = await axios.post(`${store.config.api}/signup`, {
|
||||||
email: store.account.email,
|
email: store[acc].email,
|
||||||
language: store.account.language,
|
language: store[acc].language,
|
||||||
unittest: true,
|
unittest: true,
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed at first setup request', err)
|
console.log('Failed at first setup request', err)
|
||||||
process.exit()
|
process.exit()
|
||||||
}
|
}
|
||||||
store.account.confirmation = result.data.confirmation
|
store[acc].confirmation = result.data.confirmation
|
||||||
|
|
||||||
// Confirm account
|
// Confirm account
|
||||||
try {
|
try {
|
||||||
result = await axios.post(`${store.config.api}/confirm/signup/${store.account.confirmation}`, {
|
result = await axios.post(`${store.config.api}/confirm/signup/${store[acc].confirmation}`, {
|
||||||
consent: 1,
|
consent: 1,
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Failed at account confirmation request', err)
|
console.log('Failed at account confirmation request', err)
|
||||||
process.exit()
|
process.exit()
|
||||||
}
|
}
|
||||||
store.account.token = result.data.token
|
store[acc].token = result.data.token
|
||||||
store.account.username = result.data.account.username
|
store[acc].username = result.data.account.username
|
||||||
store.account.userid = result.data.account.id
|
store[acc].userid = result.data.account.id
|
||||||
|
|
||||||
// Create API key
|
// Create API key
|
||||||
try {
|
try {
|
||||||
|
@ -70,7 +76,7 @@ export const setup = async () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
authorization: `Bearer ${store.account.token}`,
|
authorization: `Bearer ${store[acc].token}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -78,7 +84,8 @@ export const setup = async () => {
|
||||||
console.log('Failed at API key creation request', err)
|
console.log('Failed at API key creation request', err)
|
||||||
process.exit()
|
process.exit()
|
||||||
}
|
}
|
||||||
store.account.apikey = result.data.apikey
|
store[acc].apikey = result.data.apikey
|
||||||
|
}
|
||||||
|
|
||||||
return { chai, config, expect, store }
|
return { chai, config, expect, store }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue