@sap/audit-logging
v6.2.0
Published
Provides audit logging functionalities for Node.js applications
Downloads
149,586
Readme
@sap/audit-logging
Provides audit logging functionalities for Node.js applications.
Overview
Audit logging is about writing entries in a specific format to a log storage. Subject to audit logging are events of significant importance. For example, security events which may impact the confidentiality, the integrity or the availability of a system. Another example of such an event would be access to personal data (both reading and altering) like bank accounts, political opinion, health status etc.
While the consumer of ordinary logs is a system administrator who would like to keep track of the state of a system, audit logs are read by an auditor. There are legal requirements (in some countries stricter than in others) regarding audit logging.
In general the events that are supposed to be audit logged can be grouped in 3 main categories:
- changes to system configurations (which may have significant effect on the system itself)
- access to personal data (related to data privacy)
- general security events (like starting/stopping a system, failed authorization checks etc.)
NOTE: SAP customers can use this library to access (read) audit logs. They cannot write log messages for their applications. Writing audit log messages is only possible for SAP internal users.
General audit logging principles
- All attempts to perform an action in a system should be audit logged no matter if they have been successful or not.
- Audit log entries should be consistent with the state of the system. If, for example, the writing of the audit log entry fails, but the changes to system critical parameters have been applied, then those changes should be reverted. Best practice is to wait for the callback of the logger before continuing with the execution of other code.
- Especially important is which user (or other agent) has triggered the corresponding event that is being audit logged. For most of the cases the library will validate that such a field is provided in the message.
- All audit log entries should be in English. Numbers should be converted to strings with English locale. All time fields should be in UTC time in order to avoid timezone and day light saving time issues.
- Passwords should never be audit logged.
Prerequisites
An application using the audit log library needs to be bound to an instance of the Audit log service.
Versions
The Audit log service provides REST APIs that are available to applications for logging relevant messages. The latest Audit log server supports 2 versions of the REST APIs. This library provides JavaScript programming interfaces for both of these versions of the server's APIs. Note: It is recommended to use REST APIs v2 if available on the Audit log server being in use (and respectively the JavaScript v2 APIs). The initial version of the Audit log server REST APIs is deprecated in favor of the v2 version. The same applies to the JavaScript APIs provided by this library.
Express Middleware
The Audit Log client provides an easier way to use the auditlog client in an express application. Below you can find a few examples on how to make use of the express middleware. The middleware works with both v1 and v2 of the Auditlog Service.
Simple Usage
const express = require("express");
const app = express();
const auditMiddleware = require('./node-audit-logging/middleware');
// Config for auditLoggingService.
const auditLogConfig = {...}; // Retrieved from the environment using @sap/xsenv.
// app.use(auditMiddleware(auditLogConfig, 1)); // Here we select using v1.
app.use(auditMiddleware(auditLogConfig, 2));
app.get('/', async function (req, res) {
let auditLog = req.auditLog;
// ...
});
Making Use of the SecurityContext and User Token Exchange Flow
In order to make use also of the SecurityContext that comes from the @sap/xssec library, you need to make sure you import the @sap/xssec middleware before the auditlogging. This only works with the OAuth2 plan.
const express = require('express');
// Includes required for xssec.
const passport = require('passport');
const { JWTStrategy } = require('xssec');
// Includes required for auditLogging.
const auditLogMiddleware = require('@sap/audit-logging/middleware');
const app = express();
// Config for xssec middelware.
const xsuaaConfig = {...}; // Retrieved from the environment using @sap/xsenv.
passport.use(new JWTStrategy(xsuaaConfig));
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false, failWithError: true }));
// Config for auditLoggingService.
const auditLogConfig = {...}; // Retrieved from the environment using @sap/xsenv.
app.use(auditLogMiddleware(auditLogConfig));
app.get('/', async function (req, res) {
let auditLog = req.auditLog;
// ...
});
Plugging Middleware Only When Needed
If you have the case where you do not want to include the auditLog to a request, you can plug the middleware only on the routes it is required:
const express = require('express');
// Includes required for xssec.
const passport = require('passport');
const { JWTStrategy } = require('xssec');
// Includes required for auditLogging.
const auditLogMiddleware = require('@sap/audit-logging/middleware');
const app = express();
// Config for xssec middelware.
const xsuaaConfig = {...}; // Retrieved from the environment using @sap/xsenv.
passport.use(new JWTStrategy(xsuaaConfig));
app.use(passport.initialize());
app.use(passport.authenticate('JWT', { session: false, failWithError: true }));
// Config for auditLoggingService.
const auditLogConfig = {...}; // Retrieved from the environment using @sap/xsenv.
// In this API we need auditlog, so we add the auditLogMiddleware
app.get('/withAuditLog', auditLogMiddleware(auditLogConfig), function(req, res) {
let auditLog = req.auditLog; // This will be undefined.
// ...
});
// Here we do not need the auditLog, so we do not add the auditLogMiddleware.
app.get('/withoutAuditLog', function(req, res) {
let auditLog = req.auditLog; // This will be undefined.
// ...
});
mTLS Support
The library also supports certificate based authentication. In order to use this type of authentication, you need to create x509 credentials and pass them to the auditlog library the same way you would pass all other credential types. If you are retrieving credentials via @sap/xsenv you might need to create binding and not service key.
For further information how to create the appropriate credential type, visit Audit Log Retrieval API Usage for the Cloud Foundry Environment.
API - v1
NOTE: API v1 will be deprecated by March 2023. We strongly recommend that you switch to API v2 as soon as possible.
The library provides an API for writing audit messages of type configuration changes, data modifications, data accesses and security events.
Importing the library
credentials
object is the bound audit log service's credentials.
Take a look at @sap/xsenv package for more information on how to retrieve service credentials.
Standard Plan (deprecated)
var credentials = {
"user": "user",
"password": "password",
"url": "https://host:port"
};
var auditLog = require('@sap/audit-logging')(credentials);
OAuth2 Plan
var credentials = {
"uaa": {
"clientid": "clientid",
"clientsecret": "clientsecret",
"url": "https://host:port"
}
"url": "https://host:port"
};
var auditLog = require('@sap/audit-logging')(credentials, securityContext);
credentials
- Retrieved from the environment.securityContext
- Optional - created manually or through usage of the req.AuthInfo from @sap/xssec.
Data access messages
Let's suppose we need to create an entry for a data access operation over personal data. We can achieve that with the following code:
Standard Plan (deprecated)
auditLog.read('user123')
.attribute('username', true)
.attribute('first name', true)
.attribute('last name', true)
.accessChannel('UI')
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
read
- takes a string which identifies the object which is being accessed.attribute(name, successful)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being accessed.successful
- specifies whether the access was successful or not.
by
- takes a string which identifies the user performing the action. This is mandatory.accessChannel
- takes a string which specifies channel of access.attachment(id, name)
- if attachments or files are downloaded or displayed, information identifying the attachment shall be logged.id
- attachment idname
- attachment name
tenant
- takes a string which specifies the tenant id. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.at(time)
- sets a custom timestamp for event time.log
- See here
OAuth2 Plan
auditLog.read('user123')
.attribute('username', true)
.attribute('first name', true)
.attribute('last name', true)
.accessChannel('UI')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
read
- takes a string which identifies the object which is being accessed.attribute(name, successful)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being accessed.successful
- specifies whether the access was successful or not.
by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.accessChannel
- takes a string which specifies channel of access.attachment(id, name)
- if attachments or files are downloaded or displayed, information identifying the attachment shall be logged.id
- attachment idname
- attachment name
tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.log
- See here
Data modification messages
Here is how to create an entry for a data modification operation:
Standard Plan (deprecated)
auditLog.update('user123')
.attribute('first name', 'john', 'John')
.tenant('tenantId')
.by('John Doe')
.log(...);
Note: Specifying an old and a new value for an attribute is only supported in newer versions of the Audit log service. Providing these values while working with an older version of the service results in an error in the callback. In such cases one may use the attribute
method with an alternative signature:
auditLog.update('user123')
.attribute('password', false)
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
update
- takes a string which identifies the object which is being updated.attribute(name, oldValue, newValue)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being modified.oldValue
- is the current value of the attribute.newValue
- is the value of the attribute after the change.
Note: One may use this signature of the
attribute
method only if the Audit log service being consumed supports old and new values.attribute(name, successful)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being modified.successful
- specifies whether the modification was successful or not.
Note: this signature of the method is deprecated. It should be used only if the consumed Audit log service does not support old and new values.
by
- takes a string which identifies the user performing the action. This is mandatory.tenant
- takes a string which specifies the tenant id. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.at(time)
- sets a custom timestamp for event time.log
- See here
OAuth2 Plan
auditLog.update('user123')
.attribute('first name', 'john', 'John')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.log(...);
Note: Specifying an old and a new value for an attribute is only supported in newer versions of the Audit log service. Providing these values while working with an older version of the service results in an error in the callback. In such cases one may use the attribute
method with an alternative signature:
auditLog.update('user123')
.attribute('password', false)
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
update
- takes a string which identifies the object which is being updated.attribute(name, oldValue, newValue)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being modified.oldValue
- is the current value of the attribute.newValue
- is the value of the attribute after the change.
Note: One may use this signature of the
attribute
method only if the Audit log service being consumed supports old and new values.attribute(name, successful)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being modified.successful
- specifies whether the modification was successful or not.
Note: this signature of the method is deprecated. It should be used only if the consumed Audit log service does not support old and new values.
by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.log
- See here
Update data modification
auditLog.updateDataModification(id, isSuccessful)
.log(...);
updateDataModification(id, isSuccessful)
- takes two arguments.id
- id of the data modification message saved earlier (see log)isSuccessful
- denotes whether the data modification was successful or not.
log
- See here
Note: This function should only be used with an Audit log service that supports old and new values.
Configuration change messages
Here is how to create an entry for a configuration change operation:
Standard Plan (deprecated)
auditLog.configurationChange('configuration object')
.attribute('session timeout', '5', '25')
.tenant('tenantId')
.by('Application Admin')
.successful(true)
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
configurationChange
- takes a string which identifies the object which is being configured.attribute(name, oldValue, newValue)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being accessed.oldValue
- is the current value of the attribute being changed.newValue
- is the value of the attribute after the change.
successful(isSuccessful)
- used to mark whether the configuration change is finished with success, failure. If not called configuration change will be marked as pending.isSuccessful
- should be a valid boolean.
by
- takes a string which identifies the user performing the action. This is mandatory.tenant
- takes a string which specifies the tenant id. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.at(time)
- sets a custom timestamp for event time.log
- See here
OAuth2 Plan
auditLog.configurationChange('configuration object')
.attribute('session timeout', '5', '25')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.successful(true)
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
configurationChange
- takes a string which identifies the object which is being configured.attribute(name, oldValue, newValue)
- sets object attributes. It is mandatory to provide at least one attribute.name
- is the name of the attribute being accessed.oldValue
- is the current value of the attribute being changed.newValue
- is the value of the attribute after the change.
successful(isSuccessful)
- used to mark whether the configuration change is finished with success, failure. If not called configuration change will be marked as pending.isSuccessful
- should be a valid boolean.
by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.log
- See here
Update configuration change
auditLog.updateConfigurationChange(id, isSuccessful)
.log(...);
updateConfigurationChange(id, isSuccessful)
- takes two arguments.id
- id of the configuration message saved earlier (see log)isSuccessful
- denotes whether the configuration change was successful or not.
log
- See here
General security messages
Here is how to create a general security audit log message:
Standard Plan (deprecated)
auditLog.securityMessage('%d unsuccessful login attempts', 3)
.tenant('tenantId')
.by('John Doe')
.externalIP('127.0.0.1')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
securityMessage
- takes a formatted string as in util.format().externalIP
- states the IP of the machine that contacts the system. It is not mandatory, but it should be either IPv4 or IPv6.by
- takes a string which identifies the user performing the action. This is mandatory.tenant
- takes a string which specifies the tenant id. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.at(time)
- sets a custom timestamp for event time.log
- See here
OAuth2 Plan
auditLog.securityMessage('%d unsuccessful login attempts', 3)
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.externalIP('127.0.0.1')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(...);
securityMessage
- takes a formatted string as in util.format().externalIP
- states the IP of the machine that contacts the system. It is not mandatory, but it should be either IPv4 or IPv6.by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory. The provided value is ignored by older versions of the Audit log service that do not support setting a tenant.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.log
- See here
Logging a message
Use the log
method to write a message to the Audit log. It takes one argument - a callback function.
Be aware that the state of the audit logs should be consistent with the state of the system.
Make sure you handle errors from the audit log writer properly.
Application code should wait for the logging to finish before executing any other code.
var message = /* any of the above example messages */;
message.log(function (err, id) {
// Do error handling and place all of the remaining logic here
});
message
- Any of the following:err
- error object in case of error.id
- Id of the message that is saved. Use it when you want to doupdateConfigurationChange
.id
is undefined in case ofupdateConfigurationChange
.
Note: When a message is logged, the library checks for missing properties and will throw an error if some are missing.
API - v2
NOTE: The API v2 is dual - it supports both the traditional callbacks and promises.
Importing the library
credentials
object with credentials for the Audit log service.
Take a look at @sap/xsenv package for more information on how to retrieve service credentials.
The callback will be called with an error if the Audit log server does not support version 2 of the REST APIs.
Standard Plan (deprecated)
var credentials = {
"user": "user",
"password": "password",
"url": "https://host:port"
};
var auditLogging = require('@sap/audit-logging');
auditLogging.v2(credentials, function(err, auditLog) {
if (err) {
// if the Audit log server does not support version 2 of the REST APIs
// an error in the callback is returned
return console.log(err);
}
});
Promise based
var credentials = {
"user": "user",
"password": "password",
"url": "https://host:port"
};
var auditLogging = require('@sap/audit-logging');
var auditLog = await auditLogging.v2(credentials);
OAuth2 Plan
var credentials = {
"uaa": {
"clientid": "clientid",
"clientsecret": "clientsecret",
"url": "https://host:port"
}
"url": "https://host:port"
};
var auditLogging = require('@sap/audit-logging');
auditLogging.v2(credentials, securityContext, function(err, auditLog) {
if (err) {
// if the Audit log server does not support version 2 of the REST APIs
// an error in the callback is returned
return console.log(err);
}
});
Promise based
var credentials = {
"uaa": {
"clientid": "clientid",
"clientsecret": "clientsecret",
"url": "https://host:port"
}
"url": "https://host:port"
};
var auditLogging = require('@sap/audit-logging');
var auditlog = await auditLogging.v2(credentials, securityContext);
credentials
- Retrieved from the environment.securityContext
- Optional - created manually or through usage of the req.AuthInfo from @sap/xssec. Note: This method is backwards compatible if you provide the callback instead of the securityContext it will still work.
Data access messages
Standard Plan (deprecated)
auditLog.read({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'date-of-birth', successful: true })
.attachment({ id: 'exam-results-9537' })
.attachment({ id: 'recommendations-4381', name: 'file.doc' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
// multiple data subjects can also be provided in array format as following:
// .dataSubjects([{ type: 'student', id: { student_id: 'st_913' }, role: 'foreign student' },
// { type: 'student', id: { student_id: 'st_619' }, role: 'foreign student' }])
.accessChannel('UI')
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(function (err) {
});
Promise based
await auditLog.read({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'date-of-birth', successful: true })
.attachment({ id: 'exam-results-9537' })
.attachment({ id: 'recommendations-4381', name: 'file.doc' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
// multiple data subjects can also be provided in array format as following:
// .dataSubjects([{ type: 'student', id: { student_id: 'st_913' }, role: 'foreign student' },
// { type: 'student', id: { student_id: 'st_619' }, role: 'foreign student' }])
.accessChannel('UI')
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log();
read
- takes a JavaScript object which identifies the object which contains the data being accessed. Should havetype
andid
properties.attribute(attribute)
- takes an object which describes an attribute. Should have aname
property and optionally asuccessful
property. It is mandatory to provide at least one attribute.attachment(attachment)
- takes an object which describes an attachment (used if attachments or files are downloaded or displayed). Should have anid
property and optionally aname
property.dataSubject
- takes an object describing the owner of the personal data. Should havetype
andid
properties. Therole
property is optional.dataSubject
ordataSubjects
is mandatory.dataSubjects
- takes an array of data subject objects.accessChannel
- takes a string which specifies channel of access.tenant
- takes a string which specifies the tenant id.by
- takes a string which identifies the user performing the action. This is mandatory.at(time)
- sets a custom timestamp for event time.log
- logs the message.
OAuth2 Plan
auditLog.read({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'date-of-birth', successful: true })
.attachment({ id: 'exam-results-9537' })
.attachment({ id: 'recommendations-4381', name: 'file.doc' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
// multiple data subjects can also be provided in array format as following:
// .dataSubjects([{ type: 'student', id: { student_id: 'st_913' }, role: 'foreign student' },
// { type: 'student', id: { student_id: 'st_619' }, role: 'foreign student' }])
.accessChannel('UI')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(function (err) {
});
Promise based
await auditLog.read({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'date-of-birth', successful: true })
.attachment({ id: 'exam-results-9537' })
.attachment({ id: 'recommendations-4381', name: 'file.doc' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
// multiple data subjects can also be provided in array format as following:
// .dataSubjects([{ type: 'student', id: { student_id: 'st_913' }, role: 'foreign student' },
// { type: 'student', id: { student_id: 'st_619' }, role: 'foreign student' }])
.accessChannel('UI')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log();
read
- takes a JavaScript object which identifies the object which contains the data being accessed. Should havetype
andid
properties.attribute(attribute)
- takes an object which describes an attribute. Should have aname
property and optionally asuccessful
property. It is mandatory to provide at least one attribute.attachment(attachment)
- takes an object which describes an attachment (used if attachments or files are downloaded or displayed). Should have anid
property and optionally aname
property.dataSubject
- takes an object describing the owner of the personal data. Should havetype
andid
properties. Therole
property is optional.dataSubject
ordataSubjects
is mandatory.dataSubjects
- takes an array of data subject objects.accessChannel
- takes a string which specifies channel of access.by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.log
- logs the message.
Data modification messages
Standard Plan (deprecated)
var message = auditLog.update({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'town', old: 'Birmingham', new: 'London' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
message.logPrepare(function (err) {
message.logSuccess(function (err) { });
// or
message.logFailure(function(err) { });
});
Promise based
var message = await auditLog.update({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'town', old: 'Birmingham', new: 'London' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
await message.logPrepare();
await message.logSuccess();
// or
await message.logFailure();
update
- takes a JavaScript object which identifies the object which contains the data being updated. Should havetype
andid
properties.attribute(attribute)
- takes an object which describes an attribute. Should have aname
property and optionally -old
andnew
properties. It is mandatory to provide at least one attribute.dataSubject
- takes an object describing the owner of the personal data. Should havetype
andid
properties. Therole
property is optional.dataSubject
is mandatory.tenant
- takes a string which specifies the tenant id.by
- takes a string which identifies the user performing the action. This is mandatory.at(time)
- sets a custom timestamp for event time.logPrepare
- Used to log that a user has started an operation over the data.logSuccess
- Used to log that the operation over the data has been completed successfully.logFailure
- Used to log that the operation over the data has not been completed successfully.
OAuth2 Plan
var message = auditLog.update({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'town', old: 'Birmingham', new: 'London' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
message.logPrepare(function (err) {
message.logSuccess(function (err) { });
// or
message.logFailure(function(err) { });
});
Promise based
var message = await auditLog.update({ type: 'online system', id: { name: 'Students info system', module: 'Foreign students' } })
.attribute({ name: 'status' })
.attribute({ name: 'town', old: 'Birmingham', new: 'London' })
.dataSubject({ type: 'student', id: { student_id: 'st_123' }, role: 'foreign student' })
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
await message.logPrepare();
await message.logSuccess();
// or
await message.logFailure();
update
- takes a JavaScript object which identifies the object which contains the data being updated. Should havetype
andid
properties.attribute(attribute)
- takes an object which describes an attribute. Should have aname
property and optionally -old
andnew
properties. It is mandatory to provide at least one attribute.dataSubject
- takes an object describing the owner of the personal data. Should havetype
andid
properties. Therole
property is optional.dataSubject
is mandatory.by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.logPrepare
- Used to log that a user has started an operation over the data.logSuccess
- Used to log that the operation over the data has been completed successfully.logFailure
- Used to log that the operation over the data has not been completed successfully.
Configuration change messages
Standard Plan (deprecated)
var message = auditLog.configurationChange({ type: 'online system', id: { name: 'Students info system', configuration: 'global-config' } })
.attribute({ name: 'session timeout', old: '5', new: '25' })
.tenant('tenantId')
.by('Application Admin')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
message.logPrepare(function (err) {
message.logSuccess(function (err) { });
// or
message.logFailure(function(err) { });
});
Promise based
var message = await auditLog.configurationChange({ type: 'online system', id: { name: 'Students info system', configuration: 'global-config' } })
.attribute({ name: 'session timeout', old: '5', new: '25' })
.tenant('tenantId')
.by('Application Admin')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
await message.logPrepare();
await message.logSuccess();
// or
await message.logFailure();
configurationChange
- takes a JavaScript object which identifies the object which contains the data being configured. Should havetype
andid
properties.attribute(attribute)
- takes an object which describes an attribute. Should have aname
,old
andnew
properties. It is mandatory to provide at least one attribute.tenant
- takes a string which specifies the tenant id.by
- takes a string which identifies the user performing the action. This is mandatory.at(time)
- sets a custom timestamp for event time.logPrepare
- Used to log that a user has started a configuration change operation.logSuccess
- Used to log that the operation has been completed successfully.logFailure
- Used to log that the operation has not been completed successfully.
OAuth2 Plan
var message = auditLog.configurationChange({ type: 'online system', id: { name: 'Students info system', configuration: 'global-config' } })
.attribute({ name: 'session timeout', old: '5', new: '25' })
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
message.logPrepare(function (err) {
message.logSuccess(function (err) { });
// or
message.logFailure(function(err) { });
});
Promise based
var message = await auditLog.configurationChange({ type: 'online system', id: { name: 'Students info system', configuration: 'global-config' } })
.attribute({ name: 'session timeout', old: '5', new: '25' })
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z');
await message.logPrepare();
await message.logSuccess();
// or
await message.logFailure();
configurationChange
- takes a JavaScript object which identifies the object which contains the data being configured. Should havetype
andid
properties.attribute(attribute)
- takes an object which describes an attribute. Should have aname
,old
andnew
properties. It is mandatory to provide at least one attribute.by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory.subdomain
is an optional value used only with '$SUBSCRIBER'.at(time)
- sets a custom timestamp for event time.logPrepare
- Used to log that a user has started a configuration change operation.logSuccess
- Used to log that the operation has been completed successfully.logFailure
- Used to log that the operation has not been completed successfully.
General security messages
Standard Plan (deprecated)
auditLog.securityMessage('%d unsuccessful login attempts', 3)
.externalIP('127.0.0.1')
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(function (err) {
});
Promise based
await auditLog.securityMessage('%d unsuccessful login attempts', 3)
.externalIP('127.0.0.1')
.tenant('tenantId')
.by('John Doe')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log();
securityMessage
- takes a formatted string as in util.format().externalIP
- states the IP of the machine that contacts the system. Specifying it is optional, but if provided, should be either IPv4 or IPv6.by
- takes a string which identifies the user performing the action. This is mandatory.tenant
- takes a string which specifies the tenant id.at(time)
- sets a custom timestamp for event time.log
- logs the message.
OAuth2 Plan
auditLog.securityMessage('%d unsuccessful login attempts', 3)
.externalIP('127.0.0.1')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log(function (err) {
});
Promise based
await auditLog.securityMessage('%d unsuccessful login attempts', 3)
.externalIP('127.0.0.1')
.tenant('$PROVIDER') // or .tenant('$SUBSCRIBER', subdomain)
.by('$USER')
.at(42 || new Date() || '1970-01-01T00:00:00.042Z')
.log();
securityMessage
- takes a formatted string as in util.format().externalIP
- states the IP of the machine that contacts the system. Specifying it is optional, but if provided, should be either IPv4 or IPv6.by
- takes a fixed string '$USER' that is a placeholder replaced by the service. This is mandatory.subdomain
is an optional value used only with '$SUBSCRIBER'.tenant
- takes a specific string placeholder ('$PROVIDER' or '$SUBSCRIBER') that is replaced by the service. This is mandatory.at(time)
- sets a custom timestamp for event time.flog
- logs the message.
OAuth2 User Token Exchange
In order to make full use of the OAuth2, you will need to provide a SecurityContext to the library to be able to exchange user tokens to create auditlog entries on their behalf. To understand how to create a SecurityContext, please review @sap/xssec library.
v1
var xssec = require('@sap/xssec');
let auditlog;
// access_token = user access token.
xssec.createSecurityContext(access_token, xsuaa, function(error, securityContext, tokenInfo) {
if (error) {
console.log('Security Context creation failed');
return;
}
auditlog = require('@sap/audit-logging')(auditLogCreds, securityContext)
});
v2
var xssec = require('@sap/xssec');
var auditLogging = require('@sap/audit-logging');
let auditlog;
// access_token = user access token.
xssec.createSecurityContext(access_token, xsuaa, function(error, securityContext, tokenInfo) {
if (error) {
console.log('Security Context creation failed');
return;
}
auditLogging.v2(auditLogCreds, securityContext, (err, auditlog) => {
auditLog = auditlog;
})
});
Local development
Without Audit log service
var credentials = {
logToConsole: true
};
var auditLog = require('@sap/audit-logging')(credentials);
// or
require('@sap/audit-logging').v2(credentials, function (err, auditLog) {
});
When logToConsole
is true
the library will ignore other credential properties and will not use the Audit log service,
but will write the messages to the console.
Hint: If you use the @sap/xsenv package, you can pass the credentials through the default-services.json file
or VCAP_SERVICES
environment variable.
With Audit log service
If your application is not deployed in Cloud Foundry or XS Advanced,
but you have a running Audit log service somewhere, you should set the VCAP_APPLICATION
environment variable to a string like
{ "application_name" : "my-app", "organization_name" : "my-org", "space_name" : "my-space" }
Hint: If you use the @sap/xsenv package, you can set environment variables like this:
var xsenv = require('@sap/xsenv');
xsenv.loadEnv();
var credentials = xsenv.getServices({ auditlog: 'auditlog-instance-name' }).auditlog;
var auditLog = require('@sap/audit-logging')(credentials);
default-env.json file:
{
"VCAP_APPLICATION": {
"application_name" : "my-app",
"organization_name" : "my-org",
"space_name" : "my-space"
},
"VCAP_SERVICES" : {
"auditlog" : [ {
"name" : "auditlog-instance-name",
"credentials" : {
"password" : "password",
"user" : "user",
"url" : "https://host:port"
}
} ]
}
}