Skip to content

Commit

Permalink
feat: pinning api allowlist (#705)
Browse files Browse the repository at this point in the history
* feat(pinning-api): add skeleton and validation for pinning apis

* feat: add pinning endpoints

* feat: add tests

* feat: add validation for endpoints and refactor error messages

* fix: validation bug

* feat: add tests for get and delete endpoints

* fix: test assertions

* feat: add replace pin api endpoint

* chore: rollback type changes for now

Co-authored-by: Paolo <paolo@potatolondon.com>

* feat(pinning-apis): work on db to support new pinning apis

* chore: rename PinsUpsertInput to PinUpsertInput

* chore: fix PinUpsertInput type

* chore: use PinUpsertInput where required

* chore(refactor types): refactor pinItem

* chore(refactor types): Use location with other types

* chore(refactor types): fix some type errors in db client

* chore(refactor types): rename PinItemOutput to PinItemNormalized

* chore(refactor types): Pin item should have an _id field

* Rename PinItemOutput back

* feat(pinning-api-db): create pinning table and update reset.sql

* feat(pinning-api-db): create initial types

* feat(pinning-api-db): create db client signatures

* feat(pinning-api-db): updat types

* feat(pinning-api-db): write first intial add pin spec

* feat(pinning-api-db): first implementation of create and get pinRequest

* feat(pinning-api-db): get pin data for request

* feat(pinning-api-db): housekeeping

* feat(pinning-api-db): get pin request tests

* feat(pinning-api-db): housekeeping

* feat: add types to db data types

* feat(pinning-api-db): create content function

* feat(pinning-api-db): remove duplicated type

* feat(pinning-api-db): housekeeping

* feat(pinning-api-db): update types

* feat(pinning-api-db): update documentation

* chore: db change for allowlist

* Add endpoint and logic to our api service (#702)

* feat(pinning-apis): get request endpoint work

* chore: remove logging

* chore: improve validation get /pin/requestId

* chore: better integer validation

* fix: requestId conditional

* fix: remove extra bracket

Co-authored-by: Alexandra Stoica <alexandra.stoica@potatolondon.com>

* feat(pinning-apis): POST /pin endpoint

* wip: create pin request

* feat: add async task

* chore: merge feature branch

* feat: code improvements

* chore: add todo

* feat: add comments and some small fixes

* fix: pass token id

* fix: normalized cid vs source cid

* fix: minor fix and tests updates

* fix: update return of get user mock

Co-authored-by: Paolo <paolo@potatolondon.com>

* chore:  make standard happier

* feat: wip add pinning allowlist checking to API

* fix: fix token referemce

* chore: fix test mocking

* feat: add tests for pinning allowlist

* fix: merge conflict issue

* chore: fix tests after merge

* chore: review amends

* feat : delete and replace pin requests endpoints

* wip: delete pin request

* feat: add db definitions

* feat: wip replace pin endpoint

* chore: update mocks

* fix: do not list deleted requests

* feat: add delete db tests

* fix: tests

* fix: improve validation

* feat: tests

* chore: update comments

* fix: replace pin bugs and tests

* fix: coerce to number using parseInt

* chore: disable tests for now

Co-authored-by: Paolo <paolo@potatolondon.com>

* chore: simplified allowlist middleware

* feat: GET /pins endpoint

* feat: wip list pin requests

* feat: list pinning requests

* chore: renaming test descriptions

* chore: filter for list pins

* chore: fixing tests

* chore: list pins tests

* feat: db and api tests for list pins

* chore: reorder list test

* fix: correct count in list response

* fix: avoid double query on list

* feat: PR feedback

* feat: add todo for future improv

* fix: typos, use new URL in test, improve Date test

* feat: don't skip test

* fix: update tests and more fixes

* fix: take back the todo

Co-authored-by: Gary Homewood <gary@potatolondon.com>

* feat: use separate pinning auth table

* fix: db user test

* fix: correct db type

* Refactor Pin APIs and PR feedback (#810)

chore: pinning apis refactoring and feedback

* feat: update sql and db client logic

* feat: update tests and minor fixes

* feat: more updates/fixes to db package

* feat: refactor pin creation flow and refactor car upload as well

* feat: pinning, fix tests and add more

* fix: rename table, functions and types

* chore: PR feedback

* fix: rename types

* chore: rename requestedCid to sourceCid

* fix: delete should not return a body and status should be 202

* chore: add some documentation to waitOkPins

* chore: delete stale mock

* fix: allowlist on user id instead of token

* fix: allow pinning user for post

* fix: do not passa meta to cluster

* chore: remove comment

* chore: refactor createPin

* chore: linting

* chore: remove stale mock

* Get request id by user token (#863)

* fix: get pin req ids by user token

* fix: update tests

* fix: rest api types

* fix: update function params

* fix: type name

* feat: add test

* fix: tests

* fix: merge conflict

* fix: merge

* fix: rename authToken id

* fix: db test dead code

* chore: merge from pinning-apis, de-dupe tests

* fix: skip status db test

* Add metadata to API (#881)

* feat: add metadata to psa pin req

* feat: add metadata to api

* feat: add metadata tests

* chore: feedback

* feat: added pinning service request issue mailto link/github button (#879)

* feat: added pinning service request issue mailto link/github button

* chore: new line

* Fix test

* fix: update comment to reflect changes

* fix: code review

* chore: spelling consistency

* chore: review changes

Co-authored-by: Alexandra Stoica <ralexandrastoica@gmail.com>
Co-authored-by: Paolo <paolo@potatolondon.com>
Co-authored-by: Paolo Chillari <flea89@users.noreply.github.com>
Co-authored-by: Alexandra Stoica <alexandra.stoica@potatolondon.com>
Co-authored-by: Leslie Owusu-Appiah <leslie@localhost.international>
  • Loading branch information
6 people authored Jan 26, 2022
1 parent 57eaf6c commit ed3a08d
Show file tree
Hide file tree
Showing 10 changed files with 232 additions and 48 deletions.
22 changes: 20 additions & 2 deletions packages/api/src/auth.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as JWT from './utils/jwt.js'
import {
UserNotFoundError,
PinningUnauthorizedError,
TokenNotFoundError,
UnrecognisedTokenError,
NoTokenError,
Expand Down Expand Up @@ -68,11 +69,28 @@ export function withApiOrMagicToken (handler) {
}
}

/**
* Middleware: verify that the authenticated request is for a user who is
* authorized to pin.
*
* @param {import('itty-router').RouteHandler} handler
* @returns {import('itty-router').RouteHandler}
*/
export function withPinningAuthorized (handler) {
return async (request, env, ctx) => {
const authorized = await env.db.isPinningAuthorized(request.auth.user._id)
if (authorized) {
return handler(request, env, ctx)
}
throw new PinningUnauthorizedError()
}
}

/**
* @param {string} token
* @param {import('./env').Env}
* @param {import('./env').Env} env
* @throws UserNotFoundError
* @returns {import(./user).User | null }
* @returns {Promise<import('@web3-storage/db/db-client-types').UserOutput> | null }
*/
async function tryMagicToken (token, env) {
let issuer = null
Expand Down
9 changes: 9 additions & 0 deletions packages/api/src/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ export class UserNotFoundError extends HTTPError {
}
UserNotFoundError.CODE = 'ERROR_USER_NOT_FOUND'

export class PinningUnauthorizedError extends HTTPError {
constructor (msg = 'Pinning not authorized for this user') {
super(msg, 403)
this.name = 'PinningUnauthorizedError'
this.code = PinningUnauthorizedError.CODE
}
}
PinningUnauthorizedError.CODE = 'ERROR_PINNING_UNAUTHORIZED'

export class TokenNotFoundError extends HTTPError {
constructor (msg = 'API token no longer valid') {
super(msg, 401)
Expand Down
15 changes: 8 additions & 7 deletions packages/api/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import { Router } from 'itty-router'
import { errorHandler } from './error-handler.js'
import { addCorsHeaders, withCorsHeaders, corsOptions } from './cors.js'
import { withApiOrMagicToken, withMagicToken } from './auth.js'
import { withApiOrMagicToken, withMagicToken, withPinningAuthorized } from './auth.js'
import { envAll } from './env.js'
import { statusGet } from './status.js'
import { carHead, carGet, carPut, carPost } from './car.js'
Expand All @@ -26,7 +26,8 @@ router.all('*', envAll)
const auth = {
'🤲': handler => withCorsHeaders(handler),
'🔒': handler => withCorsHeaders(withApiOrMagicToken(handler)),
'👮': handler => withCorsHeaders(withMagicToken(handler))
'👮': handler => withCorsHeaders(withMagicToken(handler)),
'📌': handler => auth['🔒'](withPinningAuthorized(handler))
}

const mode = {
Expand All @@ -45,11 +46,11 @@ router.put('/car/:cid', mode['📝'](auth['🔒'](carPut)))
router.post('/upload', mode['📝'](auth['🔒'](uploadPost)))
router.get('/user/uploads', mode['👀'](auth['🔒'](userUploadsGet)))

router.post('/pins', mode['📝'](auth['🔒'](pinPost)))
router.post('/pins/:requestId', mode['📝'](auth['🔒'](pinPost)))
router.get('/pins/:requestId', mode['👀'](auth['🔒'](pinGet)))
router.get('/pins', mode['👀'](auth['🔒'](pinsGet)))
router.delete('/pins/:requestId', mode['📝'](auth['🔒'](pinDelete)))
router.post('/pins', mode['📝'](auth['📌'](pinPost)))
router.post('/pins/:requestId', mode['📝'](auth['📌'](pinPost)))
router.get('/pins/:requestId', mode['👀'](auth['📌'](pinGet)))
router.get('/pins', mode['👀'](auth['📌'](pinsGet)))
router.delete('/pins/:requestId', mode['📝'](auth['📌'](pinDelete)))

router.get('/name/:key', mode['👀'](auth['🤲'](nameGet)))
router.post('/name/:key', mode['📝'](auth['🔒'](namePost)))
Expand Down
31 changes: 24 additions & 7 deletions packages/api/test/fixtures/init-data.sql
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ VALUES (2, 'test-upload-user', 'test-upload@user.com', 'test-upload', 'test-uplo
INSERT INTO public.user (id, name, email, issuer, public_address)
VALUES (3, 'test-status-user', 'test-status@user.com', 'test-status', 'test-status');

INSERT INTO public.user (id, name, email, issuer, public_address)
VALUES (4, 'test-pinning-user', 'test-pinning@user.com', 'test-pinning', 'test-pinning');

INSERT INTO public.user (id, name, email, issuer, public_address)
VALUES (5, 'test-pinning-2-user', 'test-pinning2@user.com', 'test-pinning-2', 'test-pinning-2');

INSERT INTO auth_key (name, secret, user_id)
VALUES ('test-key', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LW1hZ2ljLWlzc3VlciIsImlzcyI6IndlYjMtc3RvcmFnZSIsImlhdCI6MTYzMzk1NzM4OTg3MiwibmFtZSI6InRlc3QtbWFnaWMtaXNzdWVyIn0.p2nD1Q4X4Z6DtJ0vxk35hhZOqSPVymhN5uyXrXth1zs', 1);

Expand All @@ -18,6 +24,12 @@ VALUES ('test-magic-key', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0
INSERT INTO auth_key (name, secret, user_id)
VALUES ('test-upload-key', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXVwbG9hZCIsImlzcyI6IndlYjMtc3RvcmFnZSIsImlhdCI6MTYzMzk1NzM4OTg3MiwibmFtZSI6InRlc3QtdXBsb2FkIn0.K30NZxNOTUdJ0u-2dRGbZYXu2A2-TqcNdRV-G1HkKnI', 2);

-- Used to test pinning
INSERT INTO auth_key (name, secret, user_id)
VALUES ('test-pinning', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXBpbm5pbmciLCJpc3MiOiJ3ZWIzLXN0b3JhZ2UiLCJpYXQiOjE2MzM5NTczODk4NzIsIm5hbWUiOiJ0ZXN0LXBpbm5pbmcifQ.li8kWohG90P8TdsKL_dUStJb2f6-43G98uZsLrVEaho', 4);
INSERT INTO auth_key (name, secret, user_id)
VALUES ('test-pinning-2', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ0ZXN0LXBpbm5pbmctMiIsImlzcyI6IndlYjMtc3RvcmFnZSIsImlhdCI6MTYzMzk1NzM4OTg3MiwibmFtZSI6InRlc3QtcGlubmluZy0yIn0.B0lwP5T2KLP0D1XGvz_f7AJcJ_j65NPN3BsxZ4Io2-g', 5);

-- /user route data testing
INSERT INTO content (cid)
VALUES ('bafkreigpimx5kl6thyfysh2witvbo5nexvu3q3uc3y65rj5sr5czcc7wae'),
Expand Down Expand Up @@ -69,7 +81,12 @@ VALUES (
1669394359626000000
);

-- /pins route testing
-- user 'test-pinning' is authorized
INSERT INTO pinning_authorization (user_id)
VALUES (4);
INSERT INTO pinning_authorization (user_id)
VALUES (5);

INSERT INTO content (cid)
VALUES ('bafybeid46f7zggioxjm5p2ze2l6s6wbqvoo4gzbdzfjtdosthmfyxdign4'),
('bafybeig7yvw6a4uhio4pmg5gahyd2xumowkfljdukad7pmdsv5uk5zcseu'),
Expand All @@ -90,11 +107,11 @@ VALUES
('Pinning', 'bafybeifsrhq2qtkcgjt4gzi7rkafrv2gaai24ptt6rohe2ebqzydkz47sm', 1, '2021-07-14T19:27:14.934572+00:00', '2021-07-14T19:27:14.934572+00:00');

INSERT INTO psa_pin_request (id, auth_key_id, content_cid, source_cid, name, origins, meta, inserted_at, updated_at)
VALUES ('ab62cf3c-c98d-494b-a756-b3a3fb6ddcab', 3, 'bafybeid46f7zggioxjm5p2ze2l6s6wbqvoo4gzbdzfjtdosthmfyxdign4', 'bafybeid46f7zggioxjm5p2ze2l6s6wbqvoo4gzbdzfjtdosthmfyxdign4', 'ReportDoc.pdf', '["/ip6/2606:4700:60::6/tcp/4009/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N", "/ip4/172.65.0.13/tcp/4009/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx4N"]', null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('bebd5f62-1381-4124-93a1-1e4eeed52635', 3, 'bafybeig7yvw6a4uhio4pmg5gahyd2xumowkfljdukad7pmdsv5uk5zcseu', 'bafybeig7yvw6a4uhio4pmg5gahyd2xumowkfljdukad7pmdsv5uk5zcseu', 'reportdoc.pdf', null, '{"app_id": "99986338-1113-4706-8302-4420da6158aa"}', '2021-01-01T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('d9670c3b-179c-4799-b4fa-66e2bb82c84f', 3, 'bafybeia45bscvzxngto555xsel4gwoclb5fxd7zpxige7rl3maoleznswu', 'bafybeia45bscvzxngto555xsel4gwoclb5fxd7zpxige7rl3maoleznswu', 'FailedPinning.doc', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('63992f6e-5bbf-4d01-8a69-9e0561c38b04', 3, 'bafybeidw7pc6nvm7u4rfhpctac4qgtpmwxapw4duugvsl3ppivvzibdlgy', 'bafybeidw7pc6nvm7u4rfhpctac4qgtpmwxapw4duugvsl3ppivvzibdlgy', 'Image.jpeg', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('3fa630f2-f22c-486f-ad1c-b36d4d740e31', 3, 'bafybeidrzt6t4k25qjeasydgi3fyh6ejos5x4d6tk2pdzxkb66bkomezy4', 'bafybeidrzt6t4k25qjeasydgi3fyh6ejos5x4d6tk2pdzxkb66bkomezy4', 'Image.png', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('5c7e7885-7f68-462d-bdfb-3f0abfb367b5', 3, 'bafybeifsrhq2qtkcgjt4gzi7rkafrv2gaai24ptt6rohe2ebqzydkz47sm', 'bafybeifsrhq2qtkcgjt4gzi7rkafrv2gaai24ptt6rohe2ebqzydkz47sm', 'Image.jpg', null, null, '2021-07-20T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
VALUES ('ab62cf3c-c98d-494b-a756-b3a3fb6ddcab', 4, 'bafybeid46f7zggioxjm5p2ze2l6s6wbqvoo4gzbdzfjtdosthmfyxdign4', 'bafybeid46f7zggioxjm5p2ze2l6s6wbqvoo4gzbdzfjtdosthmfyxdign4', 'ReportDoc.pdf', '["/ip6/2606:4700:60::6/tcp/4009/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N", "/ip4/172.65.0.13/tcp/4009/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx4N"]', null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('bebd5f62-1381-4124-93a1-1e4eeed52635', 4, 'bafybeig7yvw6a4uhio4pmg5gahyd2xumowkfljdukad7pmdsv5uk5zcseu', 'bafybeig7yvw6a4uhio4pmg5gahyd2xumowkfljdukad7pmdsv5uk5zcseu', 'reportdoc.pdf', null, '{"app_id": "99986338-1113-4706-8302-4420da6158aa"}', '2021-01-01T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('d9670c3b-179c-4799-b4fa-66e2bb82c84f', 4, 'bafybeia45bscvzxngto555xsel4gwoclb5fxd7zpxige7rl3maoleznswu', 'bafybeia45bscvzxngto555xsel4gwoclb5fxd7zpxige7rl3maoleznswu', 'FailedPinning.doc', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('63992f6e-5bbf-4d01-8a69-9e0561c38b04', 4, 'bafybeidw7pc6nvm7u4rfhpctac4qgtpmwxapw4duugvsl3ppivvzibdlgy', 'bafybeidw7pc6nvm7u4rfhpctac4qgtpmwxapw4duugvsl3ppivvzibdlgy', 'Image.jpeg', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('3fa630f2-f22c-486f-ad1c-b36d4d740e31', 4, 'bafybeidrzt6t4k25qjeasydgi3fyh6ejos5x4d6tk2pdzxkb66bkomezy4', 'bafybeidrzt6t4k25qjeasydgi3fyh6ejos5x4d6tk2pdzxkb66bkomezy4', 'Image.png', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('5c7e7885-7f68-462d-bdfb-3f0abfb367b5', 4, 'bafybeifsrhq2qtkcgjt4gzi7rkafrv2gaai24ptt6rohe2ebqzydkz47sm', 'bafybeifsrhq2qtkcgjt4gzi7rkafrv2gaai24ptt6rohe2ebqzydkz47sm', 'Image.jpg', null, null, '2021-07-20T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('3a19e48d-d6db-4f36-b686-fb8bc37c9d48', 2, 'bafybeiaqu6ijhfhwzjipwesbqf4myz6uczyigahib5joqbo5jw2xmjczfa', 'bafybeiaqu6ijhfhwzjipwesbqf4myz6uczyigahib5joqbo5jw2xmjczfa', 'Image.jpg', null, null, '2021-07-20T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z'),
('9be23b92-918e-44b8-98f4-6043c346fb4e', 2, 'bafybeidqts3rbwkprggjojbvcxy4jzpgzgcvs4a73y3gx2jjxphjeerbcy', 'bafybeidqts3rbwkprggjojbvcxy4jzpgzgcvs4a73y3gx2jjxphjeerbcy', 'Image.jpg', null, null, '2021-07-14T19:27:14.934572Z', '2021-07-14T19:27:14.934572Z');
131 changes: 116 additions & 15 deletions packages/api/test/pin.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import {
INVALID_LIMIT,
INVALID_REPLACE
} from '../src/pins.js'
import { PinningUnauthorizedError } from '../src/errors'

/**
*
* @param {string} status
* @param {import('../../db/postgres/pg-rest-api-types').definitions['pin']['status']} status
* @returns {import('../../db/db-client-types.js').PinItemOutput}
*/
const createPinWithStatus = (status) => {
Expand Down Expand Up @@ -82,17 +83,26 @@ const createPinRequest = async (cid, token) => {
}

describe('Pinning APIs endpoints', () => {
let token = null

before(async () => {
token = await getTestJWT('test-upload', 'test-upload')
})

describe('GET /pins', () => {
let baseUrl
let token

before(async () => {
baseUrl = new URL('pins', endpoint).toString()
token = await getTestJWT('test-pinning', 'test-pinning')
})

it('returns unauthorized if no token', async () => {
const res = await fetch(new URL('pins/1', endpoint).toString(), {
method: 'GET',
headers: {
'Content-Type': 'application/json'
}
})

assert(res, 'Server responded')
assert(!res.ok)
assert.deepEqual(res.status, 401)
})

it('validates filter values', async () => {
Expand Down Expand Up @@ -321,9 +331,28 @@ describe('Pinning APIs endpoints', () => {
assert.strictEqual(data.count, 6)
assert.strictEqual(data.results.length, 3)
})

it('error if user not authorized to pin', async () => {
const notAuthorizedToken = await getTestJWT('test-upload', 'test-upload')
const res = await fetch(new URL('pins', endpoint).toString(), {
method: 'GET',
headers: {
Authorization: `Bearer ${notAuthorizedToken}`,
'Content-Type': 'application/json'
}
})
assert(!res.ok)
const data = await res.json()
assert.strictEqual(data.code, PinningUnauthorizedError.CODE)
})
})

describe('POST /pins', () => {
let token = null
before(async () => {
token = await getTestJWT('test-pinning', 'test-pinning')
})

it('should receive pin data containing cid', async () => {
const cid = 'bafybeibqmrg5e5bwhx2ny4kfcjx2mm3ohh2cd4i54wlygquwx7zbgwqs4e'
const res = await fetch(new URL('pins', endpoint).toString(), {
Expand Down Expand Up @@ -495,13 +524,31 @@ describe('Pinning APIs endpoints', () => {
assert.strictEqual(error.reason, ERROR_STATUS)
assert.strictEqual(error.details, INVALID_META)
})

it('error if user not authorized to pin', async () => {
const notAuthorizedToken = await getTestJWT()
const res = await fetch(new URL('pins', endpoint).toString(), {
method: 'POST',
headers: {
Authorization: `Bearer ${notAuthorizedToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
cid: 'bafybeibqmrg5e5bwhx2ny4kfcjx2mm3ohh2cd4i54wlygquwx7zbgwqs4e'
})
})

assert(!res.ok)
const data = await res.json()
assert.strictEqual(data.code, PinningUnauthorizedError.CODE)
})
})

describe('GET /pins/:requestId', () => {
before(async () => {
// Create token
token = await getTestJWT('test-upload', 'test-upload')
let token = null

before(async () => {
token = await getTestJWT('test-pinning', 'test-pinning')
const cid = 'bafybeihy6bymmfcdjdrkhaha2srphnhrewimtkdxdmcama2dpgvpyx4efu'
await (await fetch(new URL('pins', endpoint).toString(), {
method: 'POST',
Expand Down Expand Up @@ -540,7 +587,7 @@ describe('Pinning APIs endpoints', () => {
})

it('returns not found if the request does not belong to the user token', async () => {
const wrongToken = await getTestJWT()
const wrongToken = await getTestJWT('test-pinning-2', 'test-pinning-2')
const res = await fetch(new URL('pins/ab62cf3c-c98d-494b-a756-b3a3fb6ddcab', endpoint).toString(), {
method: 'GET',
headers: { Authorization: `Bearer ${wrongToken}` }
Expand Down Expand Up @@ -571,7 +618,6 @@ describe('Pinning APIs endpoints', () => {

it('returns the pin request with specified name', async () => {
const requestId = 'bebd5f62-1381-4124-93a1-1e4eeed52635'

const res = await fetch(new URL(`pins/${requestId}`, endpoint).toString(), {
method: 'GET',
headers: {
Expand All @@ -591,7 +637,6 @@ describe('Pinning APIs endpoints', () => {
it('returns the pin request with specified metadata', async () => {
const requestId = 'bebd5f62-1381-4124-93a1-1e4eeed52635'
const meta = { app_id: '99986338-1113-4706-8302-4420da6158aa' }

const res = await fetch(new URL(`pins/${requestId}`, endpoint).toString(), {
method: 'GET',
headers: {
Expand All @@ -608,6 +653,24 @@ describe('Pinning APIs endpoints', () => {
assert.deepStrictEqual(data.pin.meta, meta)
})

it('error if user not authorized to pin', async () => {
const notAuthorizedToken = await getTestJWT()
const res = await fetch(new URL('pins', endpoint).toString(), {
method: 'POST',
headers: {
Authorization: `Bearer ${notAuthorizedToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
cid: 'bafybeibqmrg5e5bwhx2ny4kfcjx2mm3ohh2cd4i54wlygquwx7zbgwqs4e'
})
})

assert(!res.ok)
const data = await res.json()
assert.strictEqual(data.code, PinningUnauthorizedError.CODE)
})

it('returns the pin request with specified origins', async () => {
const requestId = 'ab62cf3c-c98d-494b-a756-b3a3fb6ddcab'
const origins = [
Expand Down Expand Up @@ -672,14 +735,18 @@ describe('Pinning APIs endpoints', () => {
createPinWithStatus('UnpinQueued'),
createPinWithStatus('PinError')
]

assert.strictEqual(getEffectivePinStatus(pins), 'failed')
})
})

describe('DELETE /pins/:requestId', () => {
let token = null
before(async () => {
token = await getTestJWT('test-pinning', 'test-pinning')
})

it('fails to delete if there is no user token', async () => {
const res = await fetch(new URL('pins/ab62cf3c-c98d-494b-a756-b3a3fb6ddcab', endpoint).toString(), {
const res = await fetch(new URL('pins/1', endpoint).toString(), {
method: 'DELETE'
})

Expand Down Expand Up @@ -745,9 +812,28 @@ describe('Pinning APIs endpoints', () => {
assert(res.ok, 'Server responded')
assert.equal(res.status, 202)
})

it('error if user not authorized to pin', async () => {
const notAuthorizedToken = await getTestJWT()
const res = await fetch(new URL('pins/1', endpoint).toString(), {
method: 'DELETE',
headers: {
Authorization: `Bearer ${notAuthorizedToken}`,
'Content-Type': 'application/json'
}
})
assert(!res.ok)
const data = await res.json()
assert.strictEqual(data.code, PinningUnauthorizedError.CODE)
})
})

describe('POST /pins/:requestId', () => {
let token = null
before(async () => {
token = await getTestJWT('test-pinning', 'test-pinning')
})

it('should not replace a pin request that doesn\'t exist', async () => {
const res = await fetch(new URL('pins/100', endpoint).toString(), {
method: 'POST',
Expand Down Expand Up @@ -816,5 +902,20 @@ describe('Pinning APIs endpoints', () => {
const { error } = await res.json()
assert.equal(error.details, INVALID_REPLACE)
})

it('error if user not authorized to pin', async () => {
const notAuthorizedToken = await getTestJWT()
const res = await fetch(new URL('pins/UniqueIdOfPinRequest', endpoint).toString(), {
method: 'POST',
headers: {
Authorization: `Bearer ${notAuthorizedToken}`,
'Content-Type': 'application/json'
}
})

assert(!res.ok)
const data = await res.json()
assert.strictEqual(data.code, PinningUnauthorizedError.CODE)
})
})
})
Loading

0 comments on commit ed3a08d

Please sign in to comment.