mirror of https://github.com/01-edu/public.git
Michele Sessa
2 years ago
committed by
Michele
2 changed files with 203 additions and 0 deletions
@ -0,0 +1,139 @@
|
||||
import { once } from 'node:events' |
||||
import * as cp from 'node:child_process' |
||||
import { mkdir, writeFile, chmod } from 'fs/promises' |
||||
|
||||
export const tests = [] |
||||
const fetch = _fetch // to redefine the real fetch
|
||||
|
||||
const port = 5000 |
||||
|
||||
export const setup = async ({}) => { |
||||
const dir = '.' |
||||
|
||||
await mkdir(`${dir}/guests`, { recursive: true }) |
||||
|
||||
const createFilesIn = ({ files, dirPath }) => |
||||
Promise.all( |
||||
files.map(([fileName, content]) => |
||||
writeFile(`${dirPath}/${fileName}`, JSON.stringify(content)), |
||||
), |
||||
) |
||||
|
||||
const sendRequest = async (path, options) => { |
||||
const response = await fetch(`http://localhost:${port}${path}`, options) |
||||
const { status, statusText, ok } = response |
||||
const headers = Object.fromEntries(response.headers) |
||||
let body = '' |
||||
try { |
||||
body = await response.json() |
||||
} catch (err) { |
||||
body = err |
||||
} |
||||
return { status, body, headers } |
||||
} |
||||
|
||||
return { tmpPath: dir, createFilesIn, sendRequest } |
||||
} |
||||
|
||||
// Test the server is running and writes the port in stdout
|
||||
tests.push(async ({ path, ctx }) => { |
||||
ctx.server = cp.spawn('node', [`${path}`]) |
||||
const message = await Promise.race([ |
||||
once(ctx.server.stdout, 'data'), |
||||
Promise.race([ |
||||
once(ctx.server.stderr, 'data').then(String).then(Error), |
||||
once(ctx.server, 'error'), |
||||
]).then(x => Promise.reject(x)), |
||||
]) |
||||
return message[0].toString().includes(port) |
||||
}) |
||||
|
||||
tests.push(async ({ eq, ctx }) => { |
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest(`/Rahima_Young`, { |
||||
method: 'POST', |
||||
headers: { |
||||
authorization: |
||||
'Basic ' + |
||||
Buffer.from('Caleb_Squires:abracadabra').toString('base64'), |
||||
}, |
||||
}) |
||||
if (status != 200 || headers['content-type'] != 'application/json') { |
||||
return false |
||||
} |
||||
} |
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest(`/Rahima_Young`, { |
||||
method: 'POST', |
||||
headers: { |
||||
authorization: |
||||
'Basic ' + |
||||
Buffer.from('Tyrique_Dalton:abracadabra').toString('base64'), |
||||
}, |
||||
}) |
||||
if (status != 200 || headers['content-type'] != 'application/json') { |
||||
return false |
||||
} |
||||
} |
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest(`/Rahima_Young`, { |
||||
method: 'POST', |
||||
headers: { |
||||
authorization: |
||||
'Basic ' + Buffer.from('Rahima_Young:abracadabra').toString('base64'), |
||||
}, |
||||
}) |
||||
if (status != 200 || headers['content-type'] != 'application/json') { |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
|
||||
// Unauthorized requests
|
||||
tests.push(async ({ eq, ctx }) => { |
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest(`/Rahima_Young`, { |
||||
method: 'POST', |
||||
}) |
||||
if (status != 401) { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest(``, { |
||||
method: 'POST', |
||||
}) |
||||
if (status != 401) { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest( |
||||
`/Rahima_Young:wrongpass`, |
||||
{ |
||||
method: 'POST', |
||||
}, |
||||
) |
||||
if (status != 401) { |
||||
return false |
||||
} |
||||
} |
||||
|
||||
{ |
||||
const { status, body, headers } = await ctx.sendRequest( |
||||
`/Anonymus:abracadabra`, |
||||
{ |
||||
method: 'POST', |
||||
}, |
||||
) |
||||
if (status != 401) { |
||||
return false |
||||
} |
||||
} |
||||
return true |
||||
}) |
||||
|
||||
Object.freeze(tests) |
@ -0,0 +1,64 @@
|
||||
## Gatecrashers |
||||
|
||||
### Instructions |
||||
|
||||
Unfortunately many of your guests started to invite plus ones, that started to invite plus ones, that started... to be short, everybody is inviting everybody and the situation is rapidly going out of control. |
||||
|
||||
To fix this issue you will introduce a [Basic Access Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) on your server. |
||||
|
||||
To modify the guest list now your friends will need to make authenticated requests. You decided that only your 3 best friends (`Caleb_Squires`, `Tyrique_Dalton` and `Rahima_Young`) will be able to modify the list. You also told them the secret password `abracadabra` in order to do that. |
||||
|
||||
The request query will look like this `curl -u Caleb_Squires:abracadabra ...`. |
||||
|
||||
The server should properly handle unauthorized requests using the error code `401`. |
||||
|
||||
Apart for the authentication part your server's behavior should remain unchanged: |
||||
- You will listen to port `5000` and print a message containing the port you are using. |
||||
- For each **authorized** http `POST` request, your program should create the corresponding JSON file and store the contents of the body, and then provide the content as JSON in the HTTP response, if possible. |
||||
|
||||
### Example |
||||
|
||||
To test your program, you should be able to expect the following behavior once your program is up and running. |
||||
|
||||
Unauthorized attempt: |
||||
|
||||
```shell |
||||
curl -i -X POST localhost:5000/Ricky_Banni -H "Content-Type: application/json" -d '{"answer": "yes", "drink": "alcohol", "food": "bats"}' |
||||
HTTP/1.1 401 Unauthorized |
||||
Content-Type: text/html |
||||
Date: [date] |
||||
Connection: keep-alive |
||||
Keep-Alive: timeout=5 |
||||
Transfer-Encoding: chunked |
||||
|
||||
Authorization Required% |
||||
``` |
||||
|
||||
Authorized attempt: |
||||
|
||||
```shell |
||||
curl -i -u Rahima_Young:abracadabra -X POST localhost:5000/Ricky_Banni -H "Content-Type: application/json" -d '{"answer": "yes", "drink": "alcohol", "food": "bats"}' |
||||
HTTP/1.1 200 OK |
||||
Content-Type: text/html |
||||
Date: [date] |
||||
Connection: keep-alive |
||||
Keep-Alive: timeout=5 |
||||
Transfer-Encoding: chunked |
||||
|
||||
{ |
||||
"answer": "yes", |
||||
"drink": "alcohol", |
||||
"food": "bats" |
||||
} |
||||
``` |
||||
|
||||
### Notions |
||||
|
||||
- [HTTP protocol](https://developer.mozilla.org/en-US/docs/Web/HTTP) |
||||
- [HTTP Status Codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status) |
||||
- [Node http package: createServer](https://nodejs.org/en/knowledge/HTTP/servers/how-to-create-a-HTTP-server/) |
||||
- [http.setHeader()](https://nodejs.org/api/http.html#requestsetheadername-value) |
||||
|
||||
### Provided files |
||||
|
||||
Download [`guests.zip`](https://assets.01-edu.org/tell-me-how-many/guests.zip) to have at your disposal the `guests` directory containing the files with the guest information. You must save it in your `gatecrashers` exercise directory to test your program on it. |
Loading…
Reference in new issue