1
0
Fork 0

wip(backend): Cloning of people

This commit is contained in:
joostdecock 2022-11-14 18:26:20 +01:00
parent e37548fcf7
commit d0b8572f46
6 changed files with 127 additions and 30 deletions

View file

@ -8,7 +8,7 @@ export function PersonController() {}
*/ */
PersonController.prototype.create = async (req, res, tools) => { PersonController.prototype.create = async (req, res, tools) => {
const Person = new PersonModel(tools) const Person = new PersonModel(tools)
await Person.create(req) await Person.guardedCreate(req)
return Person.sendResponse(res) return Person.sendResponse(res)
} }
@ -19,7 +19,7 @@ PersonController.prototype.create = async (req, res, tools) => {
*/ */
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.readForReturn({ id: parseInt(req.params.id) }, req.user) await Person.guardedRead(req)
return Person.sendResponse(res) return Person.sendResponse(res)
} }
@ -50,9 +50,9 @@ PersonController.prototype.delete = async (req, res, tools) => {
* Clone a person * Clone a person
* See: https://freesewing.dev/reference/backend/api * See: https://freesewing.dev/reference/backend/api
*/ */
//PersonController.prototype.clone = async (req, res, tools) => { PersonController.prototype.clone = async (req, res, tools) => {
// const Person = new PersonModel(tools) const Person = new PersonModel(tools)
// await Person.unsafeUpdate(req) await Person.guardedClone(req)
//
// return Person.sendResponse(res) return Person.sendResponse(res)
//} }

View file

@ -10,7 +10,7 @@ export function UserController() {}
*/ */
UserController.prototype.signup = async (req, res, tools) => { UserController.prototype.signup = async (req, res, tools) => {
const User = new UserModel(tools) const User = new UserModel(tools)
await User.create(req) await User.guardedCreate(req)
return User.sendResponse(res) return User.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.readForReturn({ id: req.user.uid }) await User.guardedRead({ id: req.user.uid })
return User.sendResponse(res) return User.sendResponse(res)
} }

View file

@ -12,7 +12,7 @@ export function PersonModel(tools) {
return this return this
} }
PersonModel.prototype.create = async function ({ body, user }) { PersonModel.prototype.guardedCreate = async function ({ body, user }) {
if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 3) return this.setResponse(403, 'insufficientAccessLevel')
if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing') if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing')
if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing') if (!body.name || typeof body.name !== 'string') return this.setResponse(400, 'nameMissing')
@ -28,12 +28,7 @@ PersonModel.prototype.create = async function ({ body, user }) {
data.img = this.config.avatars.person data.img = this.config.avatars.person
// Create record // Create record
try { await this.unguardedCreate(data)
this.record = await this.prisma.person.create({ data: this.cloak(data) })
} catch (err) {
log.warn(err, 'Could not create person')
return this.setResponse(500, 'createPersonFailed')
}
// Update img? (now that we have the ID) // Update img? (now that we have the ID)
const img = const img =
@ -49,6 +44,17 @@ PersonModel.prototype.create = async function ({ body, user }) {
return this.setResponse(201, 'created', { person: this.asPerson() }) return this.setResponse(201, 'created', { person: this.asPerson() })
} }
PersonModel.prototype.unguardedCreate = async function (data) {
try {
this.record = await this.prisma.person.create({ data: this.cloak(data) })
} catch (err) {
log.warn(err, 'Could not create person')
return this.setResponse(500, 'createPersonFailed')
}
return this
}
/* /*
* Loads a person from the database based on the where clause you pass it * Loads a person from the database based on the where clause you pass it
* *
@ -72,11 +78,11 @@ PersonModel.prototype.read = async function (where) {
* *
* Stores result in this.record * Stores result in this.record
*/ */
PersonModel.prototype.readForReturn = async function (where, user) { PersonModel.prototype.guardedRead = async function ({ params, user }) {
if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel') if (user.level < 1) return this.setResponse(403, 'insufficientAccessLevel')
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking') if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
await this.read(where) await this.read({ id: parseInt(params.id) })
if (this.record.userId !== user.uid && user.level < 5) { if (this.record.userId !== user.uid && user.level < 5) {
return this.setResponse(403, 'insufficientAccessLevel') return this.setResponse(403, 'insufficientAccessLevel')
} }
@ -87,6 +93,37 @@ PersonModel.prototype.readForReturn = async function (where, user) {
}) })
} }
/*
* Clones a person
* In addition prepares it for returning the person data
*
* Stores result in this.record
*/
PersonModel.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 person
const data = this.asPerson()
delete data.id
data.name += ` (cloned from #${this.record.id})`
data.notes += ` (Note: This person was cloned from person #${this.record.id})`
await this.unguardedCreate(data)
// Update unencrypted data
this.reveal()
return this.setResponse(200, false, {
result: 'success',
person: this.asPerson(),
})
}
/* /*
* Helper method to decrypt at-rest data * Helper method to decrypt at-rest data
*/ */

View file

@ -67,7 +67,7 @@ UserModel.prototype.cloak = function (data) {
* *
* Stores result in this.record * Stores result in this.record
*/ */
UserModel.prototype.readForReturn = async function (where) { UserModel.prototype.guardedRead = async function (where) {
await this.read(where) await this.read(where)
return this.setResponse(200, false, { return this.setResponse(200, false, {
@ -136,7 +136,7 @@ UserModel.prototype.setExists = function () {
/* /*
* Creates a user+confirmation and sends out signup email * Creates a user+confirmation and sends out signup email
*/ */
UserModel.prototype.create = async function ({ body }) { UserModel.prototype.guardedCreate = async function ({ body }) {
if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing') if (Object.keys(body) < 1) return this.setResponse(400, 'postBodyMissing')
if (!body.email) return this.setResponse(400, 'emailMissing') if (!body.email) return this.setResponse(400, 'emailMissing')
if (!body.language) return this.setResponse(400, 'languageMissing') if (!body.language) return this.setResponse(400, 'languageMissing')

View file

@ -15,6 +15,14 @@ export function personRoutes(tools) {
Person.create(req, res, tools) Person.create(req, res, tools)
) )
// Clone person
app.post('/people/:id/clone/jwt', passport.authenticate(...jwt), (req, res) =>
Person.clone(req, res, tools)
)
app.post('/people/:id/clone/key', passport.authenticate(...bsc), (req, res) =>
Person.clone(req, res, tools)
)
// Read person // Read person
app.get('/people/:id/jwt', passport.authenticate(...jwt), (req, res) => app.get('/people/:id/jwt', passport.authenticate(...jwt), (req, res) =>
Person.read(req, res, tools) Person.read(req, res, tools)
@ -31,14 +39,6 @@ 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)

View file

@ -95,7 +95,7 @@ export const personTests = async (chai, config, expect, store) => {
for (const field of ['imperial', 'public']) { for (const field of ['imperial', 'public']) {
it(`${store.icon('person', auth)} Should update the ${field} field (${auth})`, (done) => { it(`${store.icon('person', auth)} Should update the ${field} field (${auth})`, (done) => {
const data = {} const data = {}
const val = false const val = !store.person[auth][field]
data[field] = val data[field] = val
chai chai
.request(config.api) .request(config.api)
@ -115,6 +115,7 @@ export const personTests = async (chai, config, expect, store) => {
expect(res.status).to.equal(200) expect(res.status).to.equal(200)
expect(res.body.result).to.equal(`success`) expect(res.body.result).to.equal(`success`)
expect(res.body.person[field]).to.equal(val) expect(res.body.person[field]).to.equal(val)
store.person[auth][field] = val
done() done()
}) })
}) })
@ -310,6 +311,65 @@ export const personTests = async (chai, config, expect, store) => {
}) })
}) })
//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: // TODO:
// - Clone person // - Clone person
// - Clone person accross accounts of they are public // - Clone person accross accounts of they are public