@fireaudit/functions
v1.1.2
Published
FireAudit client functions library
Downloads
50
Maintainers
Readme
FireAudit Functions Client
Add Audit Monitoring to any paid Firebase Plan. Track all changes to every document in time, and find any issue regarding data quality, find relations, and many many more. See https://fireaudit.studio.
Installation
npm install @fireaudit/functions --save
or
yarn add @fireaudit/functions
Basic Usage
Add this code to your index.ts
file to get started. Be sure to grab your API key with your free account at https://fireaudit.studio.
import {FireAudit} from '@fireaudit/functions/client/fireaudit';
exports.fireAudit = new FireAudit().build({
apiKey: 'EXAMPLE_API_KEY', // Get your key at https://fireaudit.studio
levels: 2, // How many levels of nesting does your Firestore database have
domain: 'EU' // Either EU or US
});
Done, that's it. Now deploy your functions as regular, and within minutes you should see FireAudit working for your at https://fireaudit.studio.
Advanced Usage
FireAudit can do more than just track history. It can audit changes in your data to see if there are any anomalies. For this many sensors can be configured, some of this sensors need metadata.
To get the most value out of FireAudit there is a fireAudit
property that should be used on all documents. FireAudit uses this property to track which users does what, when and why.
Metadata
uid: string
- Strongly recommended, The UID of the user causing this change, useFUNCTION
when a function causes a change and no specific user must be tagged.event: string
- Strongly recommended, The reason why the changed occurs, this typically is the name of the class that caused the change, or the name of the trigger.serverTime: Date
- Strongly recommended, The time of receiving in firestore, use FieldValue.serverTimestamp()userTime: Date
- Optional, a field to indicate the time on the users device.delete: boolean
- Optional, a flag to indicate that this document is marked for deletion. Use this when you want to delete a file, but want to keep the UID information. When set to true this document will be deleted after it has been logged by FireAudit.
Note that none of these fields is mandatory, but it's highly recommended to use these fields. FireAudit can do advanced processing on the data when these fields are present.
There is a convenience interface - FireAuditMetadataContainer
- which can be used as an interface for any model type. It defined the fireAudit
property used by FireAudit.
See further down for the exact definition of this property.
Security rules
To enforce that all changes to the database have the correct metadata, this check can be used in the security rules.
function assertFireAudit() {
return request.resource.data.fireAudit.uid == request.auth.uid
&& request.resource.data.fireAudit.serverTime == request.time
&& request.resource.data.fireAudit.event != null
&& request.resource.data.fireAudit.userTime != null
}
Multi Firebase
If you want to use FireAudit in an environment where you have custom settings for firebase-admin
, or have multiple instances of firestore
, you can use the FireAudit
constructor to configure such.
Below snippet is an example of a custom instance of admin
.
admin.initializeApp();
new FireAudit(admin)...
Functions
FireAudit exposes a convenience method to set the fireAudit
property. Any interface can extend FireAuditMetadataContainer
, and when writing to FireStore the fireAudit
convenience method can be used:
export interface MyType extends FireAuditMetadataContainer {
someKey: string,
complex: {
anotherKey: string
},
}
async saveMyType(data: MyType) {
await firestore.doc('my/path').set({
...data,
fireAudit: FireAudit.fireAudit('ExampleEvent')
});
}
Angular
Angular clients can draw inspiration from the examples below.
const data = {
someKey: 'This could be any data',
complex: {
anotherKey: 'Any format or structure needed by the application'
},
fireAudit: fireAuditService.fireAudit('ExampleEvent')
}
// FireAuditService
import {Injectable} from '@angular/core';
import {AngularFireAuth} from '@angular/fire/auth';
import * as firebase from 'firebase/app';
@Injectable({providedIn: 'root'})
export class FireAuditService {
constructor(private fireAuth: AngularFireAuth) {}
fireAudit(event: string) {
return {
uid: this.fireAuth.auth.currentUser ? this.fireAuth.auth.currentUser.uid : null,
event: event || 'Unknown',
serverTime: firebase.firestore.FieldValue.serverTimestamp(),
userTime: new Date()
};
}
}
Heuristics
FireAudit needs to make certain assumptions about the data to make sense of the data, without understanding the data. The following assumptions are made:
- There is a collection containing users documents:
users/${uid}
. This document contains a field nameddisplayName
orname
. - Any field that ends on
Id
orIds
(basically matches regex.+Ids?$
) is considered to be a possible relation identifier.
Export
To make your audit history start at a sensible place, when you already have data in FireStore before connecting FireAudit, you can export the current state of FireStore and import it into FireAudit. To make an export go to Pub/Sub in GCP, select your project and the topic FIRE_AUDIT_EXPORT
. Now Publish a message, their must be message body, it can be anything, it isn't used by FireAudit. Also, some additional attributes can be set, all optional:
firestorePath
- The root of the export. Can be any collection, default to the root of FireStore.bucketName
- The name of the storage bucket, defaults to the firebase default bucket.storagePath
- Folder to store the export file within the bucket, defaults tofireAudit
.timestamp
- Timestamp, default to now.
Options
The FireAudit functions have some advanced features, like hashing sensitive data, or in the near future encryption.
export interface FireAuditOptions {
/**
* Mandatory: How many levels of collection/document nesting does your deepest firestore path have.
*/
levels: number;
/**
* Mandatory: The domain of FireAudit, either EU or US, or possible a custom domain for enterprise usage.
*/
domain: 'EU' | 'US' | string;
/**
* Optional: The highest collection that FireAudit must monitor. When omitted FireAudit will monitor everything.
*/
root?: string;
/**
* Optional: The region the FireAudit function is deployed to. Use multiple configurations when you must deploy into multiple regions.
* Defaults to europe-west1.
*/
region?: 'us-central1' | 'us-east1' | 'us-east4' | 'europe-west1' | 'europe-west2' | 'asia-east2' | 'asia-northeast1';
/**
* Mandatory: The API key generated by FireAudit for your project, see https://console.fireaudit.studio for more.
*/
apiKey: string;
/**
* Optional: A transformer function to transform any data before it is processed by FireAudit. Possible use-cases are
* data anonymization, or exclusion of certain paths. When omitted all data is processed.
* @param data: The data which could be anonymized, or pre processed,
* @param path: The path of the data.
* @return Processed data, or null when FireAudit should ignore this change completely.
*/
transformer?: (data: any, path: string, util: FireAuditUtil) => FireAuditMetadataContainer;
/**
* Optional: The following settings are only mandatory when using client-side hashing.
*/
hashing?: {
/**
* Mandatory: Salt for hashing strings.
*/
sha512Salt: string;
};
/**
* Optional: The following settings are only mandatory when using client-side symmetric encryption.
*/
encryption?: {
/**
* Mandatory: Key for encryption. Please use a lengthy, random string, instead of a short word.
*/
encryptionPassword: string;
/**
* Optional: Keys for decryption. Can be used to update the encryption password, but still allow decrypting old data.
* By default the encryptionPassword is added to the list of decryption passwords.
*/
decryptionPasswords?: string[];
};
/**
* Optional: Disables deleting documents when fireAudit.delete is true.
*/
disableDelete?: boolean;
}
Metadata
/**
* Convenience interface which could be used as a interface for any model type.
*/
export interface FireAuditMetadataContainer {
fireAudit?: FireAuditMetadata;
}
/**
* FireAudit can do more than just track history. It can audit changes in your data to see if there are any anomalies.
* For this many _sensors_ can be configured, some of this sensors need metadata.
* To get the most value out of FireAudit there is a `fireAudit` property that should be used on all documents.
* FireAudit uses this property to track **which** users does **what**, **when** and **why**.
*/
export interface FireAuditMetadata {
/**
* Strongly recommended, The UID of the user causing this change, use `FUNCTION` when a function causes a change and no specific user must be tagged.
*/
uid?: string;
/**
* Strongly recommended, The reason why the changed occurs, this typically is the name of the class that caused the change, or the name of the trigger.
*/
event?: string;
/**
* Strongly recommended, The time of receiving in firestore, use FieldValue.serverTimestamp().
*/
serverTime?: Date;
/**
* Optional, a field to indicate the time on the users device.
*/
userTime?: Date;
/**
* Optional, a flag to indicate that this document is marked for deletion. Use this when you want to delete a file, but want to keep the UID information. When set to true this document will be deleted after it has been logged by FireAudit.
*/
delete?: boolean;
/**
* Optional, user defined properties.
*/
[prop: string]: any;
/**
* Internally used by FireAudit, will be set automatically, can be ignored.
*/
path?: string;
}
License
Copyright 2020 codecentric nl / David Hardy
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.