0.1.4 • Published 4 years ago

@casedinc/cased v0.1.4

Weekly downloads
-
License
MIT
Repository
github
Last release
4 years ago

cased-node

cased-node is the official node client for Cased, a web service for adding audit trails to any application in minutes. This node client makes it easy to record and query your Cased audit trail events. The client also includes a robust set of features to mask PII, add client-side resilience, and automatically augment events with more data.

Contents
Installation
Configuration
How To Guide
Tests

Installation

The recommended way to install the Cased library module is via npm.

npm install -g @casedinc/cased

Configuration

The client can be configured using environment variables or initialized programmatically.

To start sending events, the client will look for CASED_PUBLISH_KEY. This can be found in your Cased dashboard. The API key can also be provided programatically and this will take precedence if provided. Example environment variable usage:

$ CASED_PUBLISH_KEY="publish_test_c260ffa178db6d5953f11f747ecb7ee3" node app.js

Or programmatically:

import { config } from "@casedinc/cased";
config.publishKey = "publish_test_c260ffa178db6d5953f11f747ecb7ee3";

You can also send your API key with each request:

import { Event } from "@casedinc/cased";

await Event.publish("user.login", {
  publishKey: "publish_test_c260ffa178db6d5953f11f747ecb7ee3",
});

How To Guide

Record your first audit trail event using the publish() function on the Event module.

import { Event } from "@casedinc/cased";

await Event.publish({
  event: "user.login",
  location: "130.25.167.191",
  request_id: "e851b3a7-9a16-4c20-ac7f-cbcdd3a9c183",
  server: "app.fe1.disney.com",
  session_id: "e2bd0d0e-165c-4e2a-b40b-8e2997fd7915",
  user_agent:
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.149 Safari/537.36",
  user_id: "User;1",
  user: "mickeymouse",
});

Fetch an event

Event can then be fetched later by their ID.

import { Event } from "@casedinc/cased";
config.policyKey = "policy_test_f764a5f252aaca986b0526b42a6f7e95";

await Event.fetch("e19a2032-f841-426c-8a13-5a938e7934a3");

Result lists and pagination

Many resources, such as Event and Policy can return paged result sets.

import { Event } from "@casedinc/cased";

const events = await Event.search({ action: "team.update" });

for (event of events) {
  console.log(event);
}

By default, 25 items are returned per page. This can be adjusted, with a maximum of 50 items.

const events = await Event.search({ action: "team.update" }, { perPage: 50 });

You can get metadata:

events.totalCount; // Total count of objects
events.pageCount; // Total number of pages

Then fetch the next, previous, first or last page.

const nextPageOfEvents = await page.nextPage();
const previousPageofEvents = await page.previousPage();
const firstPageOfEvents = await page.firstPage();
const lastPageOfEvents = await page.lastPage();

An async iterator is also provided.

const events = await Event.search({ action: "team.update" });

for async (const event of events) {
  console.log(event)
}

Searching

You can search for events by using Event.search with an object:

import { Event } from "@casedinc/cased";

const events = await Event.search({ actor: "jill" });

You can use some convenience functions:

await Event.searchActor("jill");

await Event.searchAction("invite.created");

You can also do a raw search, using a string phrase:

await Event.search("actor:jill AND event:invite.created");

Event Processors

Event processors are custom functions that modify or add additional keys to published events.

Processors are just simple functions that take an audit event and return a new modified event.

Here's an example of the default plugin that ships with this library:

const defaultEventProcessor = (event) => {
  return {
    cased_id: randomBytes(16).toString("hex"),
    timestamp: new Date().toISOString(),
    ...event,
  };
};

Processors can be registered globally or passed along as an option to Event.publish.

import { config, Event } from "@casedinc/cased";

const myProcessor = (event) => {};

// run myProcessor on all events
config.eventPipeline.push(myProcessor);

// run myProcessor on just this publish
await Event.publish("user.login", { eventPipeline: [myProcessor] });

Sensitive Data

There is a special set of event processors designed specifically to help mask sensitive PII.

import { config, EventProcessor } from "@casedinc/cased";

config.eventPipeline.push(
  EventProcessor.sensitiveField("username"),
  EventProcessor.sensitiveField("address")
);

Any field, in any audit event, that matches one of those key name will be marked as sensitive when sent to Cased.

You can also mark patterns in your audit trail as sensitive in order to mask PII.

import { config, EventProcessor } from "@casedinc/cased";

config.eventPipeline.push(
  EventProcessor.sensitivePattern("username", /@([A-Za-z0-9_]+)/g)
);

A sensitive data handler includes a label, which makes it easy to identify what kind of data is being masked. Additionally, it includes a pattern, which is a regular expression matching a pattern you want to mark as sensitive.

Now any data you send that matches that pattern with will be marked as PII when sent to Cased.

Contextual Data

If you're using a Node v14 and are willing to use an experimental API, Async Hooks provide the ability to attach contextual data to all Cased events.

import { Context as CasedContext } from "@casedinc/cased"

app.use(CasedContext.middleware));

See the Node documentation on AsyncLocalStorage as it's API is subject to change in the future.

Testing

In your tests, you'll likely want to disable publishing events. You can do this globally by setting the mockPublishEvents config value.

import { config } from "@casedinc/cased";
config.mockPublishEvents = true;

You can also capture events published during a test to use for your own assertions.

import { config, Event } from "@casedinc/cased";

beforeEach(() => {
  config.mockPublishEvents = [];
});

test("publish", async () => {
  expect(config.mockPublishEvents.length).toBe(0);
  await Event.publish("user.login");
  expect(config.mockPublishEvents.length).toBe(1);
});

Contributing

Contributions to this library are welcomed and a complete test suite is available. Tests can be run locally using the following command:

$ npm test