switch authenticated health checks to api keys
This commit is contained in:
parent
8f90385f3f
commit
1faa5c3319
|
@ -1,5 +1,5 @@
|
||||||
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
|
more_set_headers 'Access-Control-Allow-Origin: $http_origin';
|
||||||
more_set_headers 'Access-Control-Allow-Credentials: true';
|
more_set_headers 'Access-Control-Allow-Credentials: true';
|
||||||
more_set_headers 'Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE';
|
more_set_headers 'Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE';
|
||||||
more_set_headers 'Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,If-None-Match,Cache-Control,Content-Type,Range,X-HTTP-Method-Override,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location,Skynet-API-Key';
|
more_set_headers 'Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,If-None-Match,Cache-Control,Content-Type,Range,X-HTTP-Method-Override,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location,Skynet-Api-Key';
|
||||||
more_set_headers 'Access-Control-Expose-Headers: Content-Length,Content-Range,ETag,Skynet-File-Metadata,Skynet-Skylink,Skynet-Proof,Skynet-Portal-Api,Skynet-Server-Api,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location';
|
more_set_headers 'Access-Control-Expose-Headers: Content-Length,Content-Range,ETag,Skynet-File-Metadata,Skynet-Skylink,Skynet-Proof,Skynet-Portal-Api,Skynet-Server-Api,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,upload-concat,location';
|
||||||
|
|
|
@ -6,16 +6,6 @@ require("yargs/yargs")(process.argv.slice(2))
|
||||||
.help()
|
.help()
|
||||||
.demandCommand()
|
.demandCommand()
|
||||||
.strict(true)
|
.strict(true)
|
||||||
.command(
|
|
||||||
"__authenticate", // Internal only function - this function will be removed when API keys are implemented
|
|
||||||
false, // hide this function cli help
|
|
||||||
() => {},
|
|
||||||
async () => {
|
|
||||||
const { getAuthCookie } = require("../src/utils");
|
|
||||||
|
|
||||||
console.log(await getAuthCookie(true));
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.command(
|
.command(
|
||||||
"enable",
|
"enable",
|
||||||
"Mark portal as enabled",
|
"Mark portal as enabled",
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
const got = require("got");
|
const got = require("got");
|
||||||
const FormData = require("form-data");
|
const FormData = require("form-data");
|
||||||
const { isEqual } = require("lodash");
|
const { isEqual } = require("lodash");
|
||||||
const { calculateElapsedTime, getResponseContent, getAuthCookie, isPortalModuleEnabled } = require("../utils");
|
const { calculateElapsedTime, getResponseContent, isPortalModuleEnabled } = require("../utils");
|
||||||
const { SkynetClient, stringToUint8ArrayUtf8, genKeyPairAndSeed } = require("skynet-js");
|
const { SkynetClient, stringToUint8ArrayUtf8, genKeyPairAndSeed } = require("skynet-js");
|
||||||
|
|
||||||
const MODULE_BLOCKER = "b";
|
const MODULE_BLOCKER = "b";
|
||||||
|
|
||||||
const skynetClient = new SkynetClient(process.env.SKYNET_PORTAL_API);
|
const skynetClient = new SkynetClient(process.env.SKYNET_PORTAL_API, {
|
||||||
|
skynetApiKey: process.env.ACCOUNTS_TEST_USER_API_KEY,
|
||||||
|
});
|
||||||
const exampleSkylink = "AACogzrAimYPG42tDOKhS3lXZD8YvlF8Q8R17afe95iV2Q";
|
const exampleSkylink = "AACogzrAimYPG42tDOKhS3lXZD8YvlF8Q8R17afe95iV2Q";
|
||||||
|
|
||||||
// check that any relevant configuration is properly set in skyd
|
// check that any relevant configuration is properly set in skyd
|
||||||
|
@ -36,7 +38,6 @@ async function skydConfigCheck(done) {
|
||||||
|
|
||||||
// uploadCheck returns the result of uploading a sample file
|
// uploadCheck returns the result of uploading a sample file
|
||||||
async function uploadCheck(done) {
|
async function uploadCheck(done) {
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
const time = process.hrtime();
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
const payload = Buffer.from(new Date()); // current date to ensure data uniqueness
|
const payload = Buffer.from(new Date()); // current date to ensure data uniqueness
|
||||||
|
@ -47,7 +48,9 @@ async function uploadCheck(done) {
|
||||||
try {
|
try {
|
||||||
const response = await got.post(`${process.env.SKYNET_PORTAL_API}/skynet/skyfile`, {
|
const response = await got.post(`${process.env.SKYNET_PORTAL_API}/skynet/skyfile`, {
|
||||||
body: form,
|
body: form,
|
||||||
headers: { cookie: authCookie },
|
headers: {
|
||||||
|
"Skynet-Api-Key": process.env.ACCOUNTS_TEST_USER_API_KEY,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
data.statusCode = response.statusCode;
|
data.statusCode = response.statusCode;
|
||||||
|
@ -106,15 +109,14 @@ async function accountWebsiteCheck(done) {
|
||||||
|
|
||||||
// registryWriteAndReadCheck writes to registry and immediately reads and compares the data
|
// registryWriteAndReadCheck writes to registry and immediately reads and compares the data
|
||||||
async function registryWriteAndReadCheck(done) {
|
async function registryWriteAndReadCheck(done) {
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
const time = process.hrtime();
|
||||||
const data = { name: "registry_write_and_read", up: false };
|
const data = { name: "registry_write_and_read", up: false };
|
||||||
const { privateKey, publicKey } = genKeyPairAndSeed();
|
const { privateKey, publicKey } = genKeyPairAndSeed();
|
||||||
const expected = { dataKey: "foo-key", data: stringToUint8ArrayUtf8("foo-data"), revision: BigInt(0) };
|
const expected = { dataKey: "foo-key", data: stringToUint8ArrayUtf8("foo-data"), revision: BigInt(0) };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await skynetClient.registry.setEntry(privateKey, expected, { customCookie: authCookie });
|
await skynetClient.registry.setEntry(privateKey, expected);
|
||||||
const { entry } = await skynetClient.registry.getEntry(publicKey, expected.dataKey, { customCookie: authCookie });
|
const { entry } = await skynetClient.registry.getEntry(publicKey, expected.dataKey);
|
||||||
|
|
||||||
if (isEqual(expected, entry)) {
|
if (isEqual(expected, entry)) {
|
||||||
data.up = true;
|
data.up = true;
|
||||||
|
@ -204,12 +206,16 @@ async function blockerHealthCheck(done) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function genericAccessCheck(name, url) {
|
async function genericAccessCheck(name, url) {
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
const time = process.hrtime();
|
||||||
const data = { up: false, url };
|
const data = { up: false, url };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await got(url, { headers: { cookie: `nocache=true;${authCookie}` } });
|
const response = await got(url, {
|
||||||
|
headers: {
|
||||||
|
cookie: "nocache=true",
|
||||||
|
"Skynet-Api-Key": process.env.ACCOUNTS_TEST_USER_API_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
data.statusCode = response.statusCode;
|
data.statusCode = response.statusCode;
|
||||||
data.up = true;
|
data.up = true;
|
||||||
|
|
|
@ -2,7 +2,7 @@ const got = require("got");
|
||||||
const hasha = require("hasha");
|
const hasha = require("hasha");
|
||||||
const { detailedDiff } = require("deep-object-diff");
|
const { detailedDiff } = require("deep-object-diff");
|
||||||
const { isEqual } = require("lodash");
|
const { isEqual } = require("lodash");
|
||||||
const { calculateElapsedTime, ensureValidJSON, getResponseContent, getAuthCookie } = require("../utils");
|
const { calculateElapsedTime, ensureValidJSON, getResponseContent } = require("../utils");
|
||||||
const { parseSkylink } = require("skynet-js");
|
const { parseSkylink } = require("skynet-js");
|
||||||
|
|
||||||
// audioExampleCheck returns the result of trying to download the skylink
|
// audioExampleCheck returns the result of trying to download the skylink
|
||||||
|
@ -1130,13 +1130,18 @@ function parseHeaderString(header) {
|
||||||
|
|
||||||
// skylinkVerification verifies a skylink against provided information.
|
// skylinkVerification verifies a skylink against provided information.
|
||||||
async function skylinkVerification(done, expected, { followRedirect = true, method = "get" } = {}) {
|
async function skylinkVerification(done, expected, { followRedirect = true, method = "get" } = {}) {
|
||||||
const authCookie = await getAuthCookie();
|
|
||||||
const time = process.hrtime();
|
const time = process.hrtime();
|
||||||
const details = { name: expected.name, skylink: expected.skylink };
|
const details = { name: expected.name, skylink: expected.skylink };
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const query = `${process.env.SKYNET_PORTAL_API}/${expected.skylink}`;
|
const query = `${process.env.SKYNET_PORTAL_API}/${expected.skylink}`;
|
||||||
const response = await got[method](query, { followRedirect, headers: { cookie: `nocache=true;${authCookie}` } });
|
const response = await got[method](query, {
|
||||||
|
followRedirect,
|
||||||
|
headers: {
|
||||||
|
cookie: "nocache=true",
|
||||||
|
"Skynet-Api-Key": process.env.ACCOUNTS_TEST_USER_API_KEY,
|
||||||
|
},
|
||||||
|
});
|
||||||
const entry = { ...details, up: true, statusCode: response.statusCode, time: calculateElapsedTime(time) };
|
const entry = { ...details, up: true, statusCode: response.statusCode, time: calculateElapsedTime(time) };
|
||||||
const info = {};
|
const info = {};
|
||||||
|
|
||||||
|
|
|
@ -61,70 +61,6 @@ function getRequiredEnvironmentVariable(name) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Authenticate with given credentials and return auth cookie
|
|
||||||
* Creates new account if username does not exist
|
|
||||||
* Only authenticates when portal is set to authenticated users only mode
|
|
||||||
* @param {boolean} forceAuth forcibly ensure authentication with test credentials
|
|
||||||
*/
|
|
||||||
function getAuthCookie(forceAuth = false) {
|
|
||||||
// cache auth promise so only one actual request will be made
|
|
||||||
if (getAuthCookie.cache) return getAuthCookie.cache;
|
|
||||||
|
|
||||||
// accounts disabled, do not try to authenticate
|
|
||||||
if (!isPortalModuleEnabled("a")) return "";
|
|
||||||
|
|
||||||
// do not authenticate if it is not required by portal limit access rule
|
|
||||||
if (!forceAuth && !["authenticated", "subscription"].includes(process.env.ACCOUNTS_LIMIT_ACCESS)) return "";
|
|
||||||
|
|
||||||
// assign all required environment variables
|
|
||||||
const portalDomain = getRequiredEnvironmentVariable("PORTAL_DOMAIN");
|
|
||||||
const email = getRequiredEnvironmentVariable("ACCOUNTS_TEST_USER_EMAIL");
|
|
||||||
const password = getRequiredEnvironmentVariable("ACCOUNTS_TEST_USER_PASSWORD");
|
|
||||||
|
|
||||||
async function authenticate() {
|
|
||||||
const got = require("got");
|
|
||||||
|
|
||||||
try {
|
|
||||||
// authenticate with given test user credentials
|
|
||||||
const response = await got.post(`https://account.${portalDomain}/api/login`, {
|
|
||||||
json: { email, password },
|
|
||||||
});
|
|
||||||
|
|
||||||
// extract set-cookie from successful authentication request
|
|
||||||
const cookies = response.headers["set-cookie"];
|
|
||||||
|
|
||||||
// throw meaningful error when set-cookie header is missing
|
|
||||||
if (!cookies) throw new Error(`Auth successful (code ${response.statusCode}) but 'set-cookie' header is missing`);
|
|
||||||
|
|
||||||
// find the skynet-jwt cookie
|
|
||||||
const jwtcookie = cookies.find((cookie) => cookie.startsWith("skynet-jwt"));
|
|
||||||
|
|
||||||
// throw meaningful error when skynet-jwt cookie is missing
|
|
||||||
if (!jwtcookie) throw new Error(`Header 'set-cookie' found but 'skynet-jwt' cookie is missing`);
|
|
||||||
|
|
||||||
// extract just the cookie value (no set-cookie props) from set-cookie
|
|
||||||
return jwtcookie.match(/skynet-jwt=[^;]+;/)[0];
|
|
||||||
} catch (error) {
|
|
||||||
// 401 means that service worked but user could not have been authenticated
|
|
||||||
if (error.response && error.response.statusCode === 401) {
|
|
||||||
// sign up with the given credentials
|
|
||||||
await got.post(`https://account.${portalDomain}/api/user`, {
|
|
||||||
json: { email, password },
|
|
||||||
});
|
|
||||||
|
|
||||||
// retry authentication
|
|
||||||
return authenticate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// rethrow unhandled exception
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (getAuthCookie.cache = authenticate());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* isPortalModuleEnabled returns true if the given module is enabled
|
* isPortalModuleEnabled returns true if the given module is enabled
|
||||||
*/
|
*/
|
||||||
|
@ -137,7 +73,6 @@ module.exports = {
|
||||||
getYesterdayISOString,
|
getYesterdayISOString,
|
||||||
getResponseContent,
|
getResponseContent,
|
||||||
ensureValidJSON,
|
ensureValidJSON,
|
||||||
getAuthCookie,
|
|
||||||
isPortalModuleEnabled,
|
isPortalModuleEnabled,
|
||||||
ipCheckService,
|
ipCheckService,
|
||||||
ipRegex,
|
ipRegex,
|
||||||
|
|
Reference in New Issue