134 lines
3.7 KiB
JavaScript
134 lines
3.7 KiB
JavaScript
const { getSignedUrl } = require('@aws-sdk/s3-request-presigner');
|
|
const { S3Client, GetObjectCommand } = require('@aws-sdk/client-s3');
|
|
|
|
const KV = {
|
|
'skydusky': SKYDUSKY
|
|
};
|
|
const noauth = ['lidar'];
|
|
|
|
/************************************************************/
|
|
/*
|
|
* Most of this code is based on
|
|
* https://developers.cloudflare.com/workers/examples/basic-auth
|
|
*/
|
|
|
|
/**
|
|
* Parse HTTP Basic Authorization value.
|
|
* @param {Request} request
|
|
* @throws {BadRequestException}
|
|
* @returns {{ user: string, pass: string }}
|
|
*/
|
|
function basicAuthentication(request) {
|
|
const Authorization = request.headers.get('Authorization')
|
|
|
|
const [scheme, encoded] = Authorization.split(' ')
|
|
|
|
// The Authorization header must start with Basic, followed by a space.
|
|
if (!encoded || scheme !== 'Basic') {
|
|
throw new BadRequestException('Malformed authorization header.')
|
|
}
|
|
|
|
// Decodes the base64 value and performs unicode normalization.
|
|
// @see https://datatracker.ietf.org/doc/html/rfc7613#section-3.3.2 (and #section-4.2.2)
|
|
// @see https://dev.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String/normalize
|
|
const buffer = Uint8Array.from(atob(encoded), character => character.charCodeAt(0))
|
|
const decoded = new TextDecoder().decode(buffer).normalize()
|
|
|
|
// The username & password are split by the first colon.
|
|
//=> example: "username:password"
|
|
const index = decoded.indexOf(':')
|
|
|
|
// The user & password are split by the first colon and MUST NOT contain control characters.
|
|
// @see https://tools.ietf.org/html/rfc5234#appendix-B.1 (=> "CTL = %x00-1F / %x7F")
|
|
if (index === -1 || /[\0-\x1F\x7F]/.test(decoded)) {
|
|
throw new BadRequestException('Invalid authorization value.')
|
|
}
|
|
|
|
return {
|
|
user: decoded.substring(0, index),
|
|
pass: decoded.substring(index + 1),
|
|
}
|
|
}
|
|
|
|
function UnauthorizedException(reason) {
|
|
return new Response(reason, {
|
|
status: 401,
|
|
statusTest: 'Unauthorized'
|
|
});
|
|
}
|
|
|
|
function BadRequestException(reason) {
|
|
this.status = 400
|
|
this.statusText = 'Bad Request'
|
|
this.reason = reason
|
|
}
|
|
|
|
/**
|
|
* Throws exception on verification failure.
|
|
* @param {string} user
|
|
* @param {string} pass
|
|
* @throws {UnauthorizedException}
|
|
*/
|
|
async function verifyCredentials(store, user, pass) {
|
|
if (!KV.hasOwnProperty(store)) {
|
|
throw new UnauthorizedException('Invalid password.');
|
|
}
|
|
const KVpass = await KV[store].get(user);
|
|
if (KVpass === 'null' || KVpass !== pass) {
|
|
throw new UnauthorizedException('Invalid password.');
|
|
}
|
|
}
|
|
/************************************************************/
|
|
|
|
/**
|
|
* Main functions to handle request
|
|
*/
|
|
async function handleRequest(request) {
|
|
const requestURL = new URL(request.url);
|
|
const path = requestURL.pathname.substring(1);
|
|
const area = path.split('/')[0];
|
|
|
|
if (!noauth.includes(area)) {
|
|
// Prompt login
|
|
if (!request.headers.has('Authorization')) {
|
|
return new Response('Please login.', {
|
|
status: 401,
|
|
headers: { 'WWW-Authenticate': `Basic realm="${area.toUpperCase()}", charset="UTF-8"` }
|
|
});
|
|
}
|
|
|
|
// Verify login
|
|
const { user, pass } = basicAuthentication(request);
|
|
try {
|
|
await verifyCredentials(area, user, pass);
|
|
} catch (e) {
|
|
return e;
|
|
}
|
|
}
|
|
|
|
// S3 Client
|
|
const client = new S3Client({
|
|
credentials: {
|
|
accessKeyId: WASABI_ACCESS_KEY,
|
|
secretAccessKey: WASABI_SECRET_KEY,
|
|
},
|
|
endpoint: 'https://s3.us-east-2.wasabisys.com',
|
|
region: 'us-east-2',
|
|
});
|
|
|
|
// S3 Request
|
|
const command = new GetObjectCommand({
|
|
Bucket: 'bigcavemaps.com',
|
|
Key: path,
|
|
});
|
|
|
|
// S3 Url
|
|
const url = await getSignedUrl(client, command, { expiresIn: 3600 });
|
|
const r = await fetch(url);
|
|
return r;
|
|
}
|
|
|
|
addEventListener('fetch', event => {
|
|
event.respondWith(handleRequest(event.request))
|
|
})
|