Language: Imports
Imports enable a Sentinel policy to access reusable libraries and external data and functions. The exact list of available imports is dependent on the host system. Host systems may also make the available imports configurable via plugins.
Imports are extremely powerful. They enable policy decision based on arbitrary external information. For example, a behavior coming into a system can be allowed or denied based on information from a separate system where the two systems can be unaware of each other.
The example below shows a concrete example. For the example, imagine that
the import calendar
allows access to engineer calendars. This import
only exists for the purpose of this example and at the time of writing is
not a real available import.
import "calendar"
// Get the calendar for Bob for today
bob_calendar = calendar.for("bob").today
// Allow this policy to pass if Bob is not on vacation.
main = rule { not bob_calendar.has_event("vacation") }
This example shows an import being used to deny a behavior if Bob is on vacation. Hopefully real policies do not depend on a single person being in the office, but the example shows the power of imports.
In addition to consuming imports, you can also extend Sentinel by writing custom imports This allows you to add any arbitrary behavior to your Sentinel-protected systems.
Importing
Whether accessing a built-in library or an external plugin, the syntax for an import is the same:
import "name"
This adds the import with the name name
. It can be referenced using
the identifier name
. For example, if the import above exposed first
and last name data, you could access it via name.first
or name.last
.
You can shadow imports by assigning a variable. In the example below,
the import name
becomes unavailable within the function because it is
shadowed by a variable of the same name.
Shadowing an import is not the same as overwriting a variable. Imports are not part of the scope, meaning that the shadow only exists for the scope it is created within. For everything else, the import works even after it is shadowed. The example below shows that as well.
import "name"
f = func() {
name = "jane"
return name
}
print(f()) // "jane"
print(name.first) // "bob"
Aliases
An import can be aliased to another name using the syntax below:
import "name" as other
This would make the import "name" available as other
.
An import may only be imported once, regardless of aliases. The following would be invalid and would result in an error:
import "name" as one
import "name" as two
Accessing Import Data
Imports are accessed with dot-separated identifiers, such as name.first
or
name.stage.first
. These are called selector expressions. An import may
return nested data that further can be accessed via selector expressions.
In the first example on this page with the "calendar" import, we see this:
import "calendar"
a = calendar.for("bob") // selector expression calendar.for
b = a.today // selector expression on the result
c = b.vacation // accessing further nested data
The exact structure of an import is dependent on the import. Please reference the documentation for an import to learn more.