New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@spaship/api

Package Overview
Dependencies
Maintainers
3
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@spaship/api - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

lib/db.mongo-mock.js

14

CHANGELOG.md

@@ -6,2 +6,16 @@ # Change Log

# [0.6.0](https://github.com/spaship/spaship/compare/v0.5.0...v0.6.0) (2020-03-26)
### Bug Fixes
- **api:** dont cache invalid response from autosync ([#243](https://github.com/spaship/spaship/issues/243)) ([af57dc0](https://github.com/spaship/spaship/commit/af57dc0349477838b61d5fe9377fa4fd0524c2a7))
- **api:** exclude invalid spa paths ([#241](https://github.com/spaship/spaship/issues/241)) ([444db20](https://github.com/spaship/spaship/commit/444db202fb1a7a838ad39c8d87be9dba25888b68))
- **api:** fix connection to mongodb ([#216](https://github.com/spaship/spaship/issues/216)) ([2342c66](https://github.com/spaship/spaship/commit/2342c66ce414603d40ba5dafb96f8aaee72e9d31))
### Features
- **api:** add jwt and api key validation to the api ([#218](https://github.com/spaship/spaship/issues/218)) ([0d5437f](https://github.com/spaship/spaship/commit/0d5437ff1677d658c6d42f7d06b7d822bd8b7e8d))
- **api:** use UUID v4 format for API keys ([#223](https://github.com/spaship/spaship/issues/223)) ([3eff190](https://github.com/spaship/spaship/commit/3eff190271bbc215bcf3bd4c611fc4928c6157a6))
- **apiKeys:** add REST endpoints for managing API keys. ([#204](https://github.com/spaship/spaship/issues/204)) ([618f8b1](https://github.com/spaship/spaship/commit/618f8b1bc94793da660699f90de4482540d59ee3))
# [0.5.0](https://github.com/spaship/spaship/compare/v0.4.0...v0.5.0) (2020-03-09)

@@ -8,0 +22,0 @@

56

config.js
const path = require("path");
const nconf = require("nconf");
const { uniq } = require("lodash");
const { mapValues, flow, keyBy, identity } = require("lodash/fp");

@@ -10,16 +11,36 @@

const validOptions = [
let validOptions = [
// filesystem related
"config_file",
"upload_dir",
"webroot",
// network service options
"host",
"port",
// autosync stands alone
"autosync",
"mongo_user",
"mongo_password",
"mongo_url",
"mock_db"
// database
"db:mongo:user",
"db:mongo:password",
"db:mongo:url",
"db:mongo:db_name",
"db:mongo:mock",
// authentication
"auth:keycloak:url",
"auth:keycloak:realm",
"auth:keycloak:pubkey",
"auth:keycloak:pubkey_file",
"auth:keycloak:clientid",
"auth:keycloak:id_prop"
];
const filepathOptions = ["config_file", "upload_dir", "webroot"]; // config options that represent filepaths
// expand validOptions to include the nesting separator for environment variables (they use __ instead of :, since : is
// an invalid character in env var names).
validOptions = uniq(validOptions.concat(validOptions.map(p => p.replace(/:/g, "__"))));
// Read CLI flags first, then environment variables (argv).

@@ -43,2 +64,3 @@ nconf

.env({
separator: "__",
whitelist: validOptions,

@@ -60,8 +82,3 @@ lowerCase: true,

nconf.file({
file: configFile,
transform: obj => {
// use underscore as delimeter
obj.key = obj.key.replace(/-/g, "_");
return obj;
}
file: configFile
});

@@ -75,7 +92,14 @@ }

upload_dir: "/tmp/spaship_uploads",
mongo_user: null,
mongo_password: null,
mongo_url: "localhost:27017",
mongo_db: "spaship",
mock_db: process.env.NODE_ENV !== "production" // use a mock database by default in dev environments
db: {
mongo: {
url: "localhost:27017",
db_name: "spaship",
mock: process.env.NODE_ENV !== "production" // use a mock database by default in dev environments
}
},
auth: {
keycloak: {
jwt_uuid_prop: "sub"
}
}
});

@@ -82,0 +106,0 @@

#!/usr/bin/env node
const express = require("express");
const bodyParser = require("body-parser");

@@ -16,2 +17,6 @@ const { log, pinoExpress } = require("@spaship/common/lib/logging/pino");

// BodyParser for parsing application/json and for parsing application/x-www-form-urlencoded
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
routes.register(app);

@@ -18,0 +23,0 @@

@@ -116,3 +116,3 @@ const ms = require("ms");

try {
if (response) {
if (response && response.status === 200) {
// Make sure dest path exists

@@ -130,2 +130,4 @@ let exists = await this.isDirectory(path);

return true;
} else {
log.error("[Autosync] Invalid response while trying to get url:", url, response.status);
}

@@ -132,0 +134,0 @@ } catch (error) {

const db = require("./db");
const shortid = require("shortid");
const uuidv4 = require("uuid").v4;
const shajs = require("sha.js");

@@ -9,9 +9,7 @@

function hash(apikey) {
return shajs("sha256")
.update(apikey)
.digest("hex");
return shajs("sha256").update(apikey).digest("hex");
}
async function createKey(userid = null) {
const apikey = shortid.generate();
const apikey = uuidv4();
const doc = { userid, apikey };

@@ -29,3 +27,3 @@ await storeKey(doc);

userid: doc.userid,
apikey: hash(doc.apikey)
apikey: hash(doc.apikey),
});

@@ -35,10 +33,9 @@ }

async function deleteKey(apikey) {
await apikeys.deleteMany({ apikey });
const keys = await apikeys.find({ apikey }).toArray();
const result = keys.length ? await apikeys.deleteMany({ apikey }) : { error: "Hashed key not found" };
return result;
}
async function getKeysByUser(userid, limit = 100) {
return await apikeys
.find({ userid })
.limit(limit)
.toArray();
return await apikeys.find({ userid }).limit(limit).toArray();
}

@@ -53,5 +50,15 @@

return { getKeysByUser, getUserByKey, createKey, deleteKey };
async function getUserByHashedKey(apikey, limit = 100) {
return await apikeys.find({ apikey }).limit(limit).toArray();
}
async function deleteKeysByUser(userid) {
const users = await apikeys.find({ userid }).toArray();
const result = users.length ? await apikeys.deleteMany({ userid }) : { error: "User not found" };
return result;
}
return { getKeysByUser, getUserByKey, getUserByHashedKey, deleteKeysByUser, createKey, deleteKey };
}
module.exports = { attach };

@@ -1,2 +0,2 @@

const shortid = require("shortid");
const uuid = require("uuid");
const mockfs = require("mock-fs");

@@ -6,10 +6,10 @@ const db_apikey = require("./db.apikey");

// make shortid non-random while testing
jest.mock("shortid");
shortid.generate.mockResolvedValue("MOCK_KEY");
// make uuid non-random while testing
jest.mock("uuid");
uuid.v4.mockResolvedValue("MOCK_KEY");
// override some configuration values
config.get = jest.fn(opt => {
config.get = jest.fn((opt) => {
const fakeConfig = {
webroot: "/fake/webroot"
webroot: "/fake/webroot",
};

@@ -41,3 +41,3 @@ return fakeConfig[opt];

// special mock key for this test
shortid.generate.mockReturnValueOnce("fake.key");
uuid.v4.mockReturnValueOnce("fake.key");

@@ -56,3 +56,3 @@ const doc = await apikeys.createKey("fake.name");

// special mock key for this test
shortid.generate.mockReturnValueOnce(apikey);
uuid.v4.mockReturnValueOnce(apikey);

@@ -72,3 +72,3 @@ await apikeys.createKey(userid);

// special mock key for this test
shortid.generate.mockReturnValueOnce(apikey);
uuid.v4.mockReturnValueOnce(apikey);

@@ -81,2 +81,18 @@ await apikeys.createKey(userid);

test("should be able to get userid by hashedapikey", async () => {
const apikeys = await db_apikey.attach();
const userid = "babyyoda";
const apikey = "018265271839";
const apikeyhash = "fdf1fcb03dab8d28a97d2ab228225a196b9d99f0b3e1a2c1f6a05165ccb1d255";
// special mock key for this test
uuid.v4.mockReturnValueOnce(apikeyhash);
await apikeys.createKey(userid);
const doc = await apikeys.getUserByHashedKey(apikeyhash);
console.log(doc);
expect(doc).toMatchObject([{ userid, apikey: apikeyhash }]);
});
test("should be able to delete a key", async () => {

@@ -88,3 +104,3 @@ const apikeys = await db_apikey.attach();

// special mock key for this test
shortid.generate.mockReturnValueOnce(apikey);
uuid.v4.mockReturnValueOnce(apikey);

@@ -91,0 +107,0 @@ // create key, delete it, then check for its existance

@@ -7,34 +7,6 @@ /**

const mongoPkg = config.get("mock_db") ? "mongo-mock" : "mongodb";
const mongodb = require(mongoPkg);
mongodb.max_delay = 0; //you can choose to NOT pretend to be async (default is 400ms)
const MongoClient = mongodb.MongoClient;
MongoClient.persist = "mock-db.js"; //persist the data to disk
// Connection URL
const connectUrls = ["mongodb://"];
if (config.get("mongo_user") && config.get("mongo_password")) {
connectUrls.push(`${config.get("mongo_user")}:${config.get("mongo_password")}@`);
if (config.get("db:mongo:mock")) {
module.exports = require("./db.mongo-mock.js");
} else {
module.exports = require("./db.mongo.js");
}
connectUrls.push(config.get("mongo_url"));
connectUrls.push(`/${config.get("mongo_db")}`);
const url = connectUrls.join("");
let reusableClient;
async function connect(collectionName) {
if (!collectionName) {
throw new Error("db.connect must be given a collection name");
}
// if a connection is already open, use it
if (reusableClient) {
return reusableClient.collection(collectionName);
}
// otherwise, connect
reusableClient = await MongoClient.connect(url, {});
return reusableClient.collection(collectionName);
}
module.exports = { connect };

@@ -16,3 +16,4 @@ const path = require("path");

const webrootFiles = await fsp.readdir(config.get("webroot"));
const spaDirs = flow(map(get))(webrootFiles);
const validFiles = webrootFiles.filter(fileName => /^(?![_\.])[a-zA-Z0-9\_\-]*$/.test(fileName));
const spaDirs = flow(map(get))(validFiles);
return await Promise.all(spaDirs);

@@ -19,0 +20,0 @@ } catch (e) {

@@ -36,3 +36,4 @@ const metadata = require("./metadata");

// some non-SPAs in the webroot
chrome: {},
_include: {},
_test1: {},
".htaccess": "# global htaccess file"

@@ -78,9 +79,2 @@ }

deploykey: "arfgrn"
},
// some non-SPAs in the webroot
{
path: "/.htaccess"
},
{
path: "/chrome"
}

@@ -87,0 +81,0 @@ ];

{
"name": "@spaship/api",
"version": "0.5.0",
"version": "0.6.0",
"description": "The file synchronization service for the SPAship platform.",

@@ -22,3 +22,4 @@ "homepage": "https://github.com/spaship/spaship/tree/master/packages/api#readme",

"dev": "nodemon index.js",
"test": "../../node_modules/.bin/jest --restoreMocks"
"test": "../../node_modules/.bin/jest --restoreMocks",
"get-pubkey": "node scripts/fetchpk.js"
},

@@ -29,4 +30,5 @@ "keywords": [],

"dependencies": {
"@spaship/common": "^0.5.0",
"@spaship/common": "^0.6.0",
"axios": "^0.19.0",
"body-parser": "^1.19.0",
"cors": "^2.8.5",

@@ -38,2 +40,3 @@ "decompress": "^4.2.0",

"express": "^4.17.1",
"express-jwt": "^5.3.1",
"express-pino-logger": "^4.0.0",

@@ -50,6 +53,9 @@ "lodash": "^4.17.15",

"tmp-promise": "^2.0.2",
"url-join": "^4.0.1"
"url-join": "^4.0.1",
"uuid": "^7.0.2",
"uuid-validate": "0.0.3",
"validator": "^12.2.0"
},
"devDependencies": {
"mongo-mock": "3.9.0"
"mongo-mock": "4.0.0"
},

@@ -59,3 +65,3 @@ "publishConfig": {

},
"gitHead": "e964423b0c185b35d1adeee1465ca55ce1df0d45"
"gitHead": "a82a07fa6f0f337b753593a03a9dd046bb0e00d1"
}

@@ -7,3 +7,3 @@ # SPAship API

```
```bash
npm install -g @spaship/api

@@ -15,3 +15,3 @@ spaship-api

```
```bash
git@github.com:spaship/api.git

@@ -44,18 +44,24 @@ npm install

| Option | Description | CLI | Env | config.json | Default |
| ------------------ | ------------------------------------------------------------------------------------------ | ------------------ | ------------------------ | ------------------ | ---------------------------------------------- |
| **config file** | Where to find the config file. | `--config-file` | `SPASHIP_API_CONFIG_FILE`| N/A | none |
| **upload dir** | Directory to upload SPA archives. | `--upload-dir` | `SPASHIP_UPLOAD_DIR` | `"upload_dir"` | `/tmp/spaship_uploads` |
| **webroot** | Directory to extract/deploy SPAs. | `--webroot` | `SPASHIP_WEBROOT` | `"webroot"` | `/var/www` |
| **host** | Hostname to run on. | `--host` | `SPASHIP_HOST` | `"host"` | `localhost` |
| **port** | Port to run on. | `--port` | `SPASHIP_API_PORT` | `"port"` | `8008` |
| **log-level** | Granularity of log messages to print. | `--log-level` | `SPASHIP_LOG_LEVEL` | `"log_level"` | `info` |
| **log-format** | `pretty` for human-friendly logs, `json` for machine-friendly logs. | `--log-format` | `SPASHIP_LOG_FORMAT` | `"log_format"` | `pretty` |
| **mongo_url** | The hosts of your mongodb instance. | `--mongo-url` | `SPASHIP_MONGO_URL` | `"mongo_url"` | `"localhost:27017"` |
| **mongo_user** | (Optional) The username of your mongodb instance. | `--mongo-user` | `SPASHIP_MONGO_USER` | `"mongo_user"` | `null` |
| **mongo_password** | (Optional) The password of your mongodb instance. | `--mongo-password` | `SPASHIP_MONGO_PASSWORD` | `"mongo_password"` | `null` |
| **mongo_db** | The mongodb database name. | `--mongo-db` | `SPASHIP_MONGO_DB` | `"mongo_db"` | `"spaship"` |
| **mock_db** | Whether to use a mock database ([mongo-mock](https://github.com/williamkapke/mongo-mock)). | `--mock-db` | `SPASHIP_MOCK_DB` | `"mock_db"` | `true`, except when `NODE_ENV == "production"` |
| Option | Description | CLI | Env | config.json | Default |
| ---------------------------- | ------------------------------------------------------------------------------ | ----------------------------- | ------------------------------------- | --------------------------- | ------------------------------------------------------- |
| **config file** | Where to find the config file. | `--config-file` | `SPASHIP_API_CONFIG_FILE` | N/A | none |
| **upload dir** | Directory to upload SPA archives. | `--upload-dir` | `SPASHIP_UPLOAD_DIR` | `"upload_dir"` | `/tmp/spaship_uploads` |
| **webroot** | Directory to extract/deploy SPAs. | `--webroot` | `SPASHIP_WEBROOT` | `"webroot"` | `/var/www` |
| **host** | Hostname to run on. | `--host` | `SPASHIP_HOST` | `"host"` | `localhost` |
| **port** | Port to run on. | `--port` | `SPASHIP_API_PORT` | `"port"` | `8008` |
| **log-level** | Granularity of log messages to print. | `--log-level` | `SPASHIP_LOG_LEVEL` | `"log_level"` | `info` |
| **log-format** | `pretty` for human-friendly logs, `json` for machine-friendly logs. | `--log-format` | `SPASHIP_LOG_FORMAT` | `"log_format"` | `pretty` |
| **mongo_url** | The hosts of your mongodb instance. | `--db:mongo:url` | `SPASHIP_DB__MONGO__URL` | `"db.mongo.url"` | `"localhost:27017"` |
| **mongo_user** | (Optional) The username of your mongodb instance. | `--db:mongo:user` | `SPASHIP_DB__MONGO__USER` | `"db.mongo.user"` | `null` |
| **mongo_password** | (Optional) The password of your mongodb instance. | `--db:mongo:password` | `SPASHIP_DB__MONGO__PASSWORD` | `"db.mongo.password"` | `null` |
| **mongo_db** | The mongodb database name. | `--db:mongo:db_name` | `SPASHIP_DB__MONGO__DB_NAME` | `"db.mongo.db_name"` | `"spaship"` |
| **mock_db** | Whether to use a mock database ([mongo-mock][mongo-mock]). | `--db:mongo:mock` | `SPASHIP_DB__MONGO__MOCK` | `"db.mongo.mock"` | `true` for dev, `false` when `NODE_ENV == "production"` |
| **Keycloak URL** | The URL to a Keycloak instance you wish to use for authentication.<sup>2</sup> | `--auth:keycloak:url` | `SPASHIP_AUTH__KEYCLOAK__URL` | `auth.keycloak.url` | none |
| **Keycloak REALM** | The Keycloak Realm under which your SPAship Manager client is registered. | `--auth:keycloak:realm` | `SPASHIP_AUTH__KEYCLOAK__REALM` | `auth.keycloak.realm` | none |
| **Keycloak client id** | The Keycloak client id for your SPAship Manager instance. | `--auth:keycloak:client-id` | `SPASHIP_AUTH__KEYCLOAK__CLIENTID` | `auth.keycloak.clientid` | none |
| **JWT user UUID prop** | The JWT property to treat as a UUID.<sup>3</sup> | `--auth:keycloak:id_prop` | `SPASHIP_AUTH__KEYCLOAK__ID_PROP` | `auth.keycloak.id_prop` | `"sub"` <sup>4</sup> |
| **Keycloak public key** | Your Keycloak realm's public key. | `--auth:keycloak:pubkey` | `SPASHIP_AUTH__KEYCLOAK__PUBKEY` | `auth.keycloak.pubkey` | none |
| **Keycloak public key file** | A file path to your Keycloak realm's public key. | `--auth:keycloak:pubkey_file` | `SPASHIP_AUTH__KEYCLOAK__PUBKEY_FILE` | `auth.keycloak.pubkey_file` | none |
**Note** about the filepath configurations, `config file`, `upload dir`, and `webroot`: they must be absolute paths when defined in an environment variable or config file. When defined in CLI options like, they can be written relative to CWD. Example: `--config-file=./config.json`
**Note:** the filepath configurations (`config file`, `upload dir`, and `webroot`) must be absolute paths when defined in an environment variable or config file. When defined in CLI options like, they can be written relative to CWD. Example: `--config-file=../config.json`.

@@ -70,3 +76,3 @@ **Note:** When `mock_db` is on, the mocked database will be persisted as a flat file named `mock-db.js` right here in the same directory as this README.

```
```bash
www

@@ -88,3 +94,3 @@ ├── .my-app

```
```bash
NAME="My Awesome Application"

@@ -131,1 +137,25 @@ SPA_PATH="/my-app"

- The "SPAnonymous" app has `null` values for name and ref because it was not deployed with `/deploy`, but it's included so that `/list` provides a complete report of what paths are being made available.
## Scripts
During development, this repo includes a handy script (`npm run get-pubkey`) for downloading your Keycloak server's public key. It accepts the same `auth:keycloak:url` and `auth:keycloak:realm` options as the API itself. You can run it as follows:
```
npm run get-pubkey -- --auth:keycloak:url https://auth.spaship.io --auth:keycloak:realm SPAshipUsers
```
_(Just like the API, it also accepts environment variables or a config file instead of CLI options.)_
After running the script, a `.key` file will be saved in your current directory. You can reference that file in the `auth:keycloak:pubkey_file` configuration option.
[mongo-mock]: https://github.com/williamkapke/mongo-mock
### /apikeys
| HTTP Method | Endpoint | Description |
| ----------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/apikey` | Creates and returns an API key object such as `{ key: "ABCD" }`. It expects the body of the request body to contain an user id as `{ user: "babyyoda" }` |
| `DELETE` | `/apikey?hashedKey=e12e115a` | Deletes key ABCD (hashed to "e12e115a") |
| `GET` | `/apikey?user=babyyoda` | Returns an API key object such as `{ user: "babyyoda", key: "ABCD" }` |
| `GET` | `/user?hashedKey=e12e115a` | Returns an API key object such as `{ user: "babyyoda" }` |
| `DELETE` | `/user?user=babyyoda` | Deletes all API keys for user `babyyoda` |
const Autosync = require("../../lib/background/autosync");
// Return a function for getting list of deployed spas and info about them
module.exports = function createAutosyncMiddleware() {

@@ -5,0 +4,0 @@ const autosync = new Autosync();

@@ -5,3 +5,3 @@ const fs = require("fs");

// include our middlewares
// include our endpoint middlewares
const forceSyncAll = require("./forceSyncAll/forceSyncAllMiddleware");

@@ -11,5 +11,17 @@ const deploy = require("./deploy/deployMiddleware");

const getAPIKeysByUser = require("./apikeys/key/getKeysByUserMiddleware");
const deleteAPIKeysByUser = require("./apikeys/user/deleteKeysByUserMiddleware");
const getUserByAPIKey = require("./apikeys/user/getUserByKeyMiddleware");
const createAPIKey = require("./apikeys/key/createKeyMiddleware");
const deleteAPIKey = require("./apikeys/key/deleteKeyMiddleware");
// include our auth middlewares
const auth = require("./authMiddleware");
const jwt = require("./jwtMiddleware");
const apiKey = require("./apiKeyMiddleware");
const cors = corsMiddleware({
origin: true,
credentials: true
credentials: true,
});

@@ -25,3 +37,3 @@

.route("/deploy")
.post(cors, ...deploy())
.post(cors, auth(jwt(), apiKey()), ...deploy())
.get((req, res) => {

@@ -33,7 +45,18 @@ fs.readFile(path.resolve(__dirname, "..", "index.html"), (err, data) => {

app.route("/list").get(cors, list());
app.route("/list").get(cors, auth(apiKey(), jwt()), list()).options(cors); // for CORS preflight
app.post("/autosync/forceSyncAll", cors, forceSyncAll());
app.post("/autosync/forceSyncAll", cors, auth(apiKey(), jwt()), forceSyncAll());
// API Keys
app
.route("/apikey")
.get(cors, auth(jwt(), apiKey()), getAPIKeysByUser())
.post(cors, auth(jwt()), createAPIKey())
.delete(cors, auth(jwt(), apiKey()), deleteAPIKey());
app
.route("/user")
.get(cors, auth(jwt(), apiKey()), getUserByAPIKey())
.delete(cors, auth(jwt(), apiKey()), deleteAPIKeysByUser());
}
module.exports = { register };
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc