-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #7 from sombriks/develop
basic spending planning support and other improvements
- Loading branch information
Showing
19 changed files
with
603 additions
and
107 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,36 +1,33 @@ | ||
import {knex} from "../db/index.mjs"; | ||
import {verify} from "./encryption.mjs"; | ||
|
||
export const extractDetails = ctx => { | ||
const extractDetails = ctx => { | ||
const authHeader = ctx.request.header["authorization"]; | ||
if (!authHeader) ctx.throw(401, {message: "Missing auth header"}); | ||
const token = authHeader.replace("Bearer ", ""); | ||
return verify(token); | ||
} | ||
|
||
export const ifAdmin = async (ctx, next) => { | ||
return await next(); | ||
}; | ||
|
||
export const ifOwner = async (ctx, next) => { | ||
return await next(); | ||
const {admin} = extractDetails(ctx) | ||
if (admin) return await next(); | ||
else return ctx.throw(403, "User is not an admin"); | ||
}; | ||
|
||
export const ifAuthenticated = async (ctx, next) => { | ||
const details = extractDetails(ctx) | ||
// console.log(details) | ||
// console.log(new Date(details.exp * 1000)) | ||
const details = extractDetails(ctx) // id, nome, emil, admin, criacao, alteracao, iat, exp | ||
if (!details.iat || !details.exp) | ||
ctx.throw(401, {message: "Something strange with this token"}) | ||
if (new Date().getTime() > new Date(details.exp * 1000)) | ||
ctx.throw(401, {message: "Token expired"}) | ||
// ctx.throw(401, {message:"test"}) | ||
return await next() | ||
}; | ||
|
||
export const contaOwnedBy = async (ctx, next) => { | ||
const {id} = extractDetails(ctx) | ||
const {usuario_id, conta_id} = ctx.request.params; | ||
if (id != usuario_id) return ctx.throw(403, `Usuário logado não é o usuário informado`); | ||
const [count] = await knex("conta").where({usuario_id, id: conta_id}).count(); | ||
if (count[Object.keys(count)[0]]) await next(); | ||
else throw new Error(`Conta ${conta_id} não pertence ao usuário ${usuario_id}`); | ||
else ctx.throw(403, `Conta ${conta_id} não pertence ao usuário ${usuario_id}`); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import chai from "chai" | ||
|
||
import * as middleware from "./middleware.mjs" | ||
import sinon from "sinon"; | ||
import {sign} from "./encryption.mjs"; | ||
import {getAdmin, resetConta} from "../../services/index.mjs"; | ||
|
||
chai.should(); | ||
|
||
describe("Middleware tests", () => { | ||
|
||
it("should check if it's admin", async () => { | ||
const adm = await getAdmin() | ||
const {token} = sign(adm) | ||
const authorization = `Bearer ${token}` | ||
const ctx = {request: {header: {authorization}}, throw: sinon.fake()} | ||
const next = sinon.mock() | ||
next.once() | ||
|
||
await middleware.ifAdmin(ctx, next) | ||
|
||
next.verify() | ||
}) | ||
|
||
it("should check if it's authenticated", async () => { | ||
const adm = await getAdmin() | ||
const {token} = sign(adm) | ||
const authorization = `Bearer ${token}` | ||
const ctx = {request: {header: {authorization}}, throw: sinon.fake()} | ||
const next = sinon.mock() | ||
next.once() | ||
|
||
await middleware.ifAuthenticated(ctx, next) | ||
|
||
next.verify() | ||
|
||
}) | ||
|
||
it("should check if it owns the resource", async () => { | ||
// given | ||
const adm = await getAdmin() | ||
const contasIds = await resetConta({usuario_id: adm.id}) | ||
const {token} = sign(adm) | ||
const authorization = `Bearer ${token}` | ||
const params = {usuario_id: adm.id, conta_id: contasIds[0].id} | ||
const ctx = {request: {header: {authorization}, params}, throw: sinon.fake()} | ||
const next = sinon.mock() | ||
next.once() | ||
|
||
// when | ||
await middleware.contaOwnedBy(ctx, next) | ||
|
||
// then | ||
next.verify() | ||
}) | ||
|
||
it("Should FAIL due missing auth header", async () => { | ||
// given | ||
const authorization = `Bearer` | ||
const ctx = {request: {header: {authorization}}, throw: sinon.mock()} | ||
const next = sinon.mock() | ||
next.never() | ||
ctx.throw.never() | ||
|
||
// when | ||
const spyable = { ifAuthenticated: middleware.ifAuthenticated} | ||
const spy = sinon.spy(spyable, "ifAuthenticated") | ||
try { | ||
await spyable.ifAuthenticated(ctx, next) | ||
} catch(e) { | ||
chai.expect(spy.exceptions).length(1) | ||
} | ||
|
||
// then | ||
chai.expect(spy.called) | ||
chai.expect(spy.threw()) | ||
ctx.throw.verify() | ||
next.verify() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,24 @@ | ||
import { listPlanejamento } from "../services/index.mjs"; | ||
import { delPlanejamento, insertPlanejamento, listPlanejamento, updatePlanejamento } from '../services/index.mjs' | ||
|
||
export const listPlanejamentoRequest = async ctx => { | ||
const { user_id } = ctx.request.params; | ||
const { q, limit = 10, offset = 0 } = ctx.request.query; | ||
return await listPlanejamento({ user_id, q, limit, offset }); | ||
}; | ||
const { usuario_id } = ctx.request.params | ||
const { q, limit = 10, offset = 0 } = ctx.request.query | ||
ctx.body = await listPlanejamento({ usuario_id, q, limit, offset }) | ||
} | ||
|
||
export const insertPlanejamentoRequest = async ctx => { | ||
const { usuario_id } = ctx.request.params | ||
const planejamento = ctx.request.body | ||
ctx.body = await insertPlanejamento({ usuario_id, planejamento }) | ||
} | ||
|
||
export const updatePlanejamentoRequest = async ctx => { | ||
const { id, usuario_id } = ctx.request.params | ||
const planejamento = ctx.request.body | ||
ctx.body = await updatePlanejamento({ id, usuario_id, planejamento }) | ||
} | ||
|
||
export const delPlanejamentoRequest = async ctx => { | ||
const { usuario_id, id } = ctx.request.params | ||
ctx.body = await delPlanejamento({ usuario_id, id }) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,16 @@ | ||
import chai, { expect } from "chai"; | ||
import chai, {expect} from "chai"; | ||
import chaiHttp from "chai-http"; | ||
|
||
import { app } from "../main.mjs"; | ||
import { verify } from "../config/security/index.mjs"; | ||
import {app} from "../main.mjs"; | ||
import {verify} from "../config/security/index.mjs"; | ||
|
||
chai.should(); | ||
chai.use(chaiHttp); | ||
|
||
describe("User API test", async () => { | ||
|
||
it("Should login", async () => { | ||
const testUser = { email: "[email protected]", senha: "e1e2e3e4" }; | ||
const testUser = {email: "[email protected]", senha: "e1e2e3e4"}; | ||
|
||
const res = await chai.request(app.callback()).post("/login").send(testUser); | ||
|
||
|
@@ -21,4 +21,12 @@ describe("User API test", async () => { | |
data.email.should.be.eq(testUser.email); | ||
expect(data.senha).to.be.undefined; | ||
}); | ||
|
||
it("Should NOT login wrong auth info", async () => { | ||
const testUser = {email: "[email protected]", senha: "crap"}; | ||
|
||
const res = await chai.request(app.callback()).post("/login").send(testUser); | ||
|
||
res.should.have.status(404); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import chai from "chai"; | ||
import chaiHttp from "chai-http"; | ||
|
||
import {app} from "./main.mjs"; | ||
|
||
chai.should(); | ||
chai.use(chaiHttp); | ||
|
||
describe("Base API test", () => { | ||
|
||
it("Should return status ONLINE", async () => { | ||
const res = await chai.request(app.callback()).get("/status") | ||
res.should.have.status(200); | ||
res.text.should.be.eql("ONLINE"); | ||
}); | ||
|
||
it("Should answer with 404", async () => { | ||
const res = await chai.request(app.callback()).get("/crap") | ||
res.should.have.status(404); | ||
}) | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,32 @@ | ||
import { knex } from "../config/db/index.mjs"; | ||
|
||
export const listPlanejamento = ({ user_id = -1, q = "", limit = 10, offset = 0 }) => { | ||
return knex("planejamento") | ||
.whereIn("categoria_id", knex("categoria") | ||
.select("id") | ||
.where({ user_id })) | ||
.andWhereLike("descricao", `%${q}%`) | ||
import { knex } from '../config/db/index.mjs' | ||
|
||
export const listPlanejamento = ({ usuario_id = -1, q = '', limit = 10, offset = 0 }) => { | ||
return knex('planejamento') | ||
.whereIn('categoria_id', knex('categoria') | ||
.select('id') | ||
.where({ usuario_id })) | ||
.andWhereLike('descricao', `%${q}%`) | ||
.orderBy('criacao', 'desc') | ||
.offset(offset) | ||
.limit(limit); | ||
}; | ||
.limit(limit) | ||
} | ||
|
||
export const insertPlanejamento = async ({ usuario_id, planejamento }) => { | ||
const count = await knex('categoria').where({ usuario_id, id: planejamento.categoria_id }).count() | ||
if (!count[Object.keys(count)[0]]) throw Error('Categoria não pertence ao usuário') | ||
return knex('planejamento').insert(planejamento, ['id']) | ||
} | ||
|
||
export const updatePlanejamento = async ({ id, usuario_id, planejamento }) => { | ||
const count = await knex('categoria').where({ usuario_id, id: planejamento.categoria_id }).count() | ||
if (!count[Object.keys(count)[0]]) throw Error('Categoria não pertence ao usuário') | ||
planejamento.id = id // sanity | ||
return knex('planejamento').update(planejamento).where({ id }) | ||
} | ||
|
||
export const delPlanejamento = ({ usuario_id = -1, id = -1 }) => | ||
knex('planejamento').del() | ||
.whereIn('categoria_id', knex('categoria') | ||
.select('id') | ||
.where({ usuario_id })) | ||
.andWhere({ id }) |
Oops, something went wrong.