Packages

Cased provides user and employee activity audit trails to companies that need to monitor access to information or demonstrate regulatory compliance.

Current section

Files

Jump to
cased README.md
Raw

README.md

# cased-elixir
A Cased client for Elixir applications in your organization to publish audit trail events to Datadog Logs.
## Overview
- [Installation](#installation)
- [Configuration](#configuration)
- [Usage](#usage)
- [Publishing events to Cased](#publishing-events-to-cased)
- [Masking & filtering sensitive information](#masking-and-filtering-sensitive-information)
- [Context](#context)
- [Testing](#testing)
- [Contribution Guidelines](#contributing)
## Installation
The package can be installed by adding `cased` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:cased, "~> 1.0.0"}
]
end
```
## Configuration
`cased-elixir` follows Elixir's [Library Guidelines](https://hexdocs.pm/elixir/master/library-guidelines.html#avoid-application-configuration), avoiding the use of a global `:cased` application configuration in favor of more flexible, ad hoc configuration at runtime (using your own application configuration, environment variables, etc).
### For Publisher
Add a worker specification for `Cased.Publisher.Datadog` to your application's supervisor.
The publisher accepts the following options:
- `:key` — Your [Datadog API key](https://docs.datadoghq.com/account_management/api-app-keys/#api-keys) (**required**).
- `:silence` — Whether audit trail events will be discarded, rather than sent; useful for
non-production usage (**optional**; defaults to `false`).
- `:timeout` — The request timeout, in milliseconds (**optional**; defaults to `15_000`)
You can source your configuration values from your application configuration,
runtime environment variables, or hard-code them directly; the following is just
an example:
```elixir
children = [
# Other workers...
{
Cased.Publisher.Datadog,
key: System.get_env("DD_API_KEY") || Application.fetch_env!(:your_app, :datadog_api_key),
silence: System.get_env("CASED_SILENCE") || Application.fetch_env!(:your_app, :cased_silence, false),
}
]
# Other config...
Supervisor.start_link(children, opts)
```
In the event you provide an invalid configuration, a `Cased.ConfigurationError` will be raised with details.
## Usage
### Publishing events to Cased
Provided you've [configured](#for-publisher) the Cased publisher, use `Cased.publish/1`:
```elixir
iex> %{
...> action: "credit_card.charge",
...> amount: 2000,
...> currency: "usd",
...> source: "tok_amex",
...> description: "My First Test Charge (created for API docs)",
...> credit_card_id: "card_1dQpXqQwXxsQs9sohN9HrzRAV6y"
...> }
...> |> Cased.publish()
:ok
```
:information_source: See the documentation for `Cased.publish/2` for more options.
### Masking & filtering sensitive information
If you are handling sensitive information on behalf of your users, you should consider masking or filtering any sensitive information.
You can do this manually by using `Cased.Sensitive.String.new/2`:
```elixir
iex> %{
...> action: "credit_card.charge",
...> user: Cased.Sensitive.String.new("john@example.com", label: :email)
...> }
...> |> Cased.publish()
:ok
```
You can also use handlers to find sensitive values for you automatically. Here's an example checking for usernames:
```elixir
iex> username_handler = {Cased.Sensitive.RegexHandler, :username, ~r<@\w+>}
iex> %{
...> action: "comment.create",
...> body: "@username, I'm not sure."
...> }
...> |> Cased.publish(handlers: [username_handler])
:ok
```
If you're regularly using the same handlers, consider storing them in your application config and defining your own function to use them in your application:
```elixir
defmodule MyApp do
@doc """
Publish an audit event to Cased.
"""
@spec publish_to_cased(audit_event :: map()) :: :ok | {:error, any()}
def publish_to_cased(audit_event) do
handlers = Application.get_env(:my_app, :cased_handlers, [])
audit_event
|> Cased.publish(handlers: handlers)
end
end
```
For more information, see the `Cased.Sensitive.Handler` module.
### Context
One of the most easiest ways to publish detailed events to Cased is to push contextual information into the Cased context.
**Note that the Cased context is tied to the current process** (it's actually stored in the [process dictionary](https://hexdocs.pm/elixir/Process.html)). Different process, different context.
```elixir
iex> Cased.Context.merge(location: "hostname.local")
:ok
iex> %{
...> action: "console.start",
...> user: "john"
...> }
...> |> Cased.publish()
:ok
```
Any information stored using `Cased.Context` will be included any time an event is published.
```json
{
"cased_id": "5f8559cd-4cd9-48c3-b1d0-6eedc4019ec1",
"action": "user.login",
"user": "john",
"location": "hostname.local",
"timestamp": "2020-06-22T21:43:06.157336"
}
```
You can provide `Cased.Context.merge/2` a function and the context will only be present for the duration of the function execution:
```elixir
iex> Cased.Context.merge(location: "hostname.local") do
...> # Will include { "location": "hostname.local" }
...> %{
...> action: "console.start",
...> user: "john"
...> }
...> |> Cased.publish()
...> end
:ok
iex> # Will not include {"location": "hostname.local"}
iex> %{
...> action: "console.end",
...> user: "john"
...> }
...> |> Cased.publish()
:ok
```
(You can also use `Cased.Context.put/2` and `Cased.Context.put/3` for single-value additions to the context.)
To reset the context, use `Cased.Context.reset/0`:
```elixir
iex> Cased.Context.reset()
:ok # or `nil` if no data was stored in the context
```
See the `Cased.Context` module for more information.
### Testing
See `Cased.TestHelper`.
## Contributing
1. Fork it ( https://github.com/cased/cased-elixir/fork )
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create a new Pull Request