Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Using Veza APIs to add custom data and identity providers to the Veza Entity Catalog
The Open Authorization API (OAA) is used to publish information about identities, authorization, and resources to the Veza Access Graph, making custom-built or otherwise-unsupported applications available for search, workflows, and monitoring. A typical motivation for using OAA is the need to integrate with enterprise applications that don't have an official Veza integration, such as a custom identity broker or source control management system.
Several community connectors built on OAA are already available for immediate use, enabling easy connection to SaaS providers such as GitHub, SalesForce, and others. You can also develop a custom connector using the Veza-provided Python SDK oaaclient
or your language of choice.
To integrate a custom application using OAA, you will typically rely on an API (or another method) to list identities and resources within the host system, and retrieve entity and authorization metadata such as permissions, roles, and activity status. You must then structure this information according to one of the supported OAA templates. Once you have assembled the JSON payload, you can publish it using REST API calls or the oaaclient
CLI.
Customers have utilized the Open Authorization API to accommodate many different scenarios. A few use cases include:
Using the GitHub connector to ensure that repositories holding critical source code are correctly configured.
Collecting infrastructure-as-a-code (IaC) configurations to audit which users can log in to important hosts.
Auditing the permissions granted by an internal developer portal.
Getting Started introduces important OAA workflows, the custom application template, and common API operations. When planning your connector, you may also want to review the Core Concepts for more information about naming considerations, mapping custom applications to the OAA schema, and other topics.
Veza provides a Python SDK and working example connectors, which you can download using GitHub or pip install oaaclient
. Examples and documentation are included with the source code.
Alternatively, you can parse a data source, compile the JSON payload, and publish it using your language of choice. For detailed documentation on the OAA schema and API operations, see:
Custom Application Template - suitable for most applications
Custom Identity Provider Template - for custom Identity Providers
API reference - operations for creating, updating, and deleting OAA providers and data sources
Overview and first steps for building an Open Authorization API connector
A built-in Veza integration or OAA connector might not be available for an application or identity provider you want to connect to Veza. However, you can still use the Open Authorization API to integrate compatible applications and identity providers with the rest of your Veza data catalog.
The adaptable OAA schema can model a wide range of authorization models and resource hierarchies, and you will typically have several options for sourcing authorization metadata from the application provider. To integrate a custom application with Veza, you will need to be able to:
Extract metadata from the source application using an API, command line, or another method to collect the needed authorization-related data. Depending on the application, this could include data resources and sub-resources as well as local users, groups, roles, permissions, and correlation to federated identities or external IDs.
Generate a payload describing the users, resources, and permissions, according to a standard JSON schema. The payload can describe any number of application resources and sub-resources, along with information about identities, groups, permissions, and relationships to other graph entities.
Publish the payload for processing using Veza APIs (after registering a new custom provider and data source if they don't already exist).
You can use the Python SDK to simplify the process of populating the payload, connecting to Veza, and managing OAA providers and data sources.
The process of retrieving user records, along with lists of resources, group and permissions information, will be unique to each technology and OAA connector. You may also need to consider how to adapt the application's implementation of roles, federated identities, and resource hierarchies to fit the OAA template.
Before building a connector, you should read the best practices for sourcing metadata and naming conventions, or see the current list of community connectors for real-world examples.
Most programming languages offer ways to populate and manipulate a JSON schema. You can also use the Python SDK for prebuilt functions:
custom_app.add_custom_permission("owner", [OAAPermission.DataRead, OAAPermission.DataWrite])
jane = custom_app.add_local_user("jane", identities="[email protected]")
resource1 = custom_app.add_resource(name="customers", resource_type="database")
jane.add_permission(permission="owner", resources=[customers])
Below is an example of a populated application
template. It demonstrates the basic principles of modeling local user permissions on application resources. For more details on all the available entities and properties see the full Custom Application reference.
In addition to built-in properties such as department
or last_login_at
, most entities can have additional user-defined custom properties. Using custom properties enables integrations to capture application-specific data that can be useful for reporting and provide additional insight during investigations.
You can also apply Veza tags to entities within the payload.
You will use the Veza REST API to create, delete, and update a custom data source under v1/providers/custom/{provider_id}/{datasource_id}
. Before you can push an OAA payload, you will need to:
Add a custom Provider to represent the generic application provider (such as "GitHub"), and set the name and template (application
or IdP
).
Bind a custom Data Source to the new custom Provider (such as "GitHub Organization") to activate it in the data pipeline. The data source will be the destination for future pushes and updates, and should usually represent the top-level instance of the modeled application.
Push the payload to populate the Veza Access Graph with new entities and metadata for the custom data source.
To update the data, push a new payload to the original provider ID and data source ID. By default, Veza will overwrite any existing data. The previous state will be available in the graph history, based on Veza's snapshot retention schedule.
Optionally, subsequent changes can be incremental updates, where the payload only contains entities to modify, add, or remove.
oaaclient
The module oaaclient.client
can be used to instantiate a Veza connection and manage OAA providers:
from oaaclient.client import OAAClient
from oaaclient.templates import CustomApplication, OAAPermission
# creates a connection class to communicate with Veza
veza_con = OAAClient(url=veza_url, token=veza_api_key)
# creates a new Custom Application model
custom_app = CustomApplication(name="Sample App", application_type="sample")
See Client for additional documentation.
Alternatively, you can make the required REST API calls using the client of your choice or by invoking oaaclient
as a command-line tool.
Typically, the provider should represent the provider name (such as "CustomSCM"). Any payload pushes will target the data source ("CustomSCMInstance") under the custom provider. The pushed payload can include many nested resources (such as multiple repositories). The template also supports resources with a variety of different types within a single application.
The data source and provider name
are primarily for internal use. The application_type
specified in the template is used when viewing and searching the custom app from the Veza UI.
Veza enforces strict validation of the json_data
field containing the OAA payload. Three checks are performed:
All required fields must be present.
String fields must be valid UTF-8 strings, no larger than 256 characters.
The maximum payload size is 100MB. If you need to compress the payload, you can enable the option when creating the OAA data source.
A successful response may include warnings for issues that didn't prevent the processing of OAA data but should still be investigated. When mapping identities to an external id, a warning will be raised if a matching IdP group name or user principal name can't be found.
To validate an AzureAD or other type of user, go to the Authorization Graph, search for the node of interest, select Show Details, and confirm the principal name is correct.
To validate the identifier for a group, find the node for the group and verify the name is as expected.
{
"warnings": [
{
"message": "Cannot find identity by names (values: [email protected])"
}
]
}
Veza will always return the warnings
key in the response. If a response is empty, there are no warnings.
For all Veza APIs, error messages include an error code (in standard gRPC error, such as 3
for Invalid Argument) and a descriptive error message. The error message consists of two parts:
The field in which the error occurs
The detailed reason for the error
Strategies and best practices for OAA connector development
See the included topics for more information about developing an Open Authorization API connector.
Setting unique identifiers and human-readable names using OAA templates
Veza requires each entity to have a unique identifier, used within the template to reference entities (which groups a user is a member of) and for Veza to use to track and display the entity. This is true for both the Application and IdP templates
By default the templates use the name
field for this purpose. However, names are not unique within some applications. In this case the templates offer an optional id
as a unique identifier, allowing name
to function as a non-unique display name.
Local User, Local Group and Local Role have an optional value id
that can be provided for each entity that serves as the unique identifier.
To use id
all Local Users, Local Groups and Local Roles must be defined with an ID. The name
and id
can be the same value as long it is unique for the entity type. For example a local role can have admin
for both the name
and id
.
The id
value becomes the key for referencing local users, groups and roles in the identity_to_permissions
section of the payload and for referencing group memberships.
Using id
s for mapping, instead of name
is recommended in most cases, especially if any of the following are true:
Entity names aren't unique in the application (if two users can have the name "Joe Doe" but each have a unique user id such as email
or login
or the applications unique id
).
The API references users, resources, and other entities by an ID instead of name. Using the same ID for the OAA payload will limit scenarios where you need to maintain a mapping of id
to name
in your connector.
For Custom Application to use
id
for Local Users, Groups and Roles all entities must use theid
field. To use theid
field for Resources all Resources and Sub-Resources must have anid
Resources and Sub-resource each can have an optional id
value. When provided, the resource name
does not need to be unique. To use id
, all resources and sub-resources must be defined with a unique ID. The id
value will be used to assign resource permissions in identity_to_permissions
.
Custom IdP also supports a optional unique identifier value identity
for Users and Groups. If not used, the entity name
must be unique and will be the primary identifier.
JSON schemas for describing custom applications and identity providers
OAA utilizes templates (JSON schema) for structuring authorization and identity metadata, combined with a REST API to register, update and manage the data. Once uploaded, Veza processes the template payload and incorporates the entities and permissions into the Authorization Metadata Graph.
Choosing the appropriate template (application
or identity provider
) is the first step in creating a new integration with OAA. The template provides a schema for describing the identities, resources, and authorization relationships local to the OAA data source.
For most applications, SaaS Apps and systems the provides a generic and flexible model to capture authorization data for users and groups to the system and its resources.
A custom application is structured with the following main entities:
Application
Resource
Sub-resource
Sub-resource
Additional sub-resources
Local Users
Local Groups
Local Roles
Local Permissions
Identity-to-permissions binding
Intended for modeling sources of users, group, and federated identity metadata, the can be used to enumerate users and groups that access other external applications and resources, similar to built-in connectors for Okta and AzureAD. These users and groups typically represent the top-level corporate identities within an organization.
A Custom Identity Provider can have the following entities:
Domains
Users
Groups
The Custom IdP template also includes the option to define AWS Roles that are assumable by users and groups and can work with Access Review Workflows to auto-assign resource managers.
Open Authorization API usage examples
The official Veza connectors are useful references for understanding the decisions and strategies that come into play when building a custom Open Authorization API (OAA) integration.
Each connector is similar in basic function, yet adapts to the resource hierarchies and interfaces unique to the technology provider. Each uses the Python package to construct a payload, create a custom provider and data source, and publish the extracted metadata.
Veza provides working connectors and source code for:
GitHub
GitLab
Jira
Looker
PagerDuty
Zendesk
See the for setup instructions and the most recent releases. The repository also includes sample applications demonstrating basic oaaclient
usage. You can also browse these examples in the .
Building blocks for your custom OAA integration
The Veza
package provides data models, methods, and helpers for using the Open Authorization API. It provides helper methods to populate OAA templates for custom applications, filesystems, HRIS systems, and identity providers, and push the payloads to Veza. The SDK can also be used as a generic Veza API client.
The Veza
SDK includes the following core components:
Veza.Sdk.Client
: A base API client for making REST calls to a Veza tenant
Veza.OAA.Client
: An OAA API client for interacting with integration providers, data sources, and pushing OAA metadata to a Veza tenant.
For example usage, see C# OAA Application Connector.
Sample Workflow
Create the Veza API connection and a new custom application:
using Veza.OAA;
using Veza.OAA.Application;
using Veza.OAA.Base;
// inside namespace/class
OAAClient oaaClient = new(api_key: <your_api_key>, url: <veza_tenant_url>);
CustomApplication customApp = new(name: "sample app",
applicationType: "sample", description: "This is a sample application");
Once the CustomApplication
class is instantiated, you can use its public methods to populate the new app with users, groups, resources, and permissions metadata:
// add custom permissions
customApp.AddCustomPermission(name: "Admin", permissions: new List<Permission>
{
Permission.DataRead,
Permission.DataWrite,
Permission.MetadataRead,
Permission.MetadataWrite,
Permission.NonData
},
applyToSubResources: true
);
// define custom user properties
customApp.DefinedProperties[typeof(User)].DefineProperty("is_guest", typeof(bool));
// add user
User user = customApp.AddUser(name: "bob");
user.AddIdentity("[email protected]");
user.IsActive = true;
user.CreatedAt = "2001-01-01T00:00:00.000Z".FromRFC3339();
user.DeactivatedAt = "2003-03-01T00:00:00.000Z".FromRFC3339();
user.LastLoginAt = "2002-02-01T00:00:00.000Z".FromRFC3339();
user.PasswordLastChangedAt = "2004-04-01T00:00:00.000Z".FromRFC3339();
user.SetProperty(name: "is_guest", value: false);
// define group properties
customApp.DefinedProperties[typeof(Group)].DefineProperty("group_id", typeof(int));
// add group
Group group1 = customApp.AddGroup("group1");
group1.CreatedAt = "2001-01-01T00:00:00.000Z".FromRFC3339();
group1.SetProperty(name: "group_id", 1);
customApp.Users["bob"].AddGroup("group1");
Group group2 = customApp.AddGroup("group2");
group2.AddGroup("group1");
// idp identities
customApp.AddIdPIdentity("[email protected]");
// define role properties
customApp.DefinedProperties[typeof(Role)].DefineProperty("custom", typeof(bool));
// add roles
Role role1 = customApp.AddRole(name: "role1", permissions: new List<string> { "all", "Admin", "Manage_Thing" });
role1.SetProperty(name: "custom", value: false);
// define resource properties
customApp.DefineResourceProperty("private", typeof(bool), "thing");
// add resources
Resource thing1 = customApp.AddResource(name: "thing1", resourceType: "thing", description: "thing1");
thing1.SetProperty(name: "private", false);
thing1.AddTag(name: "tag1", value: "This is a value @,-_.");
Resource cog1 = thing1.AddSubResource(name: "cog1", resourceType: "cog");
cog1.AddConnection(id: "[email protected]", nodeType: "GoogleCloudServiceAccount");
// authorizations
customApp.Users["bob"].AddRole(name: "role1", applyToApplication: true);
customApp.Groups["group2"].AddRole(name: "role1", resources: new List<Resource> { thing1 });
customApp.IdPIdentities["[email protected]"].AddRole(name: "role1", applyToApplication: true);
return customApp;
Once all identities, permissions, and resources are added to the CustomApplication object, use the client connection to push the data to Veza:
await oaaClient.CreateProvider(provider_name: "sample app", custom_template: "application");
await oaaClient.PushApplication(provider_name: "sample app", data_source_name: "sample app 1", customApp);
See the quickstarts directory for full examples.
The Veza.OAA
namespace provides exception types for common errors that occur when interacting with Veza APIs.
An OAAClientException
is raised if there are errors interacting with the Veza API.
A TemplateException
is raised if a provided payload does not conform to the template requirements. The inner exception will contain details about the exact issues encountered.
Each OAA connector will be slightly different, depending on the methods each source application or service provides for retrieving entities, authorization, and other required metadata. You should consult the API documentation for your application when considering how you will source the information, and refer to existing Veza-supported OAA connectors for real-world examples.
OAA connector source code and Veza
components are thoroughly annotated for reference when building your own integrations.
For additional information about developing a custom OAA integration, please see Open Authorization API section of the User Guide.
Building blocks for your custom OAA integration
The `oaaclient` package provides data models, methods and a command-line interface for using the . You can use it to populate OAA templates including as Application, IdP, and HRIS, pushing OAA data to Veza and even as a general Veza API client.
The oaaclient
SDK includes the following components:
oaaclient.client
: Veza API communication (data provider management, payload push, etc.). Requires an API key for authentication.
oaaclient.templates
: Classes for modeling and generating an OAA payload.
oaaclient.utils
: Additional utility functions (icon encoding, etc.).
For example usage, please see and the directory.
Sample Workflow
Create the Veza API connection and a new custom application:
Once the CustomApplication
class is instantiated, you can use the public methods to populate the new app with local users, groups, resources, and permissions metadata:
Once all identities, permissions and resources are added to the CustomApplication object, the client connection handles the final push to Veza:
See the directory for complete examples.
The OAAClient
class handles API connections to Veza. If there are errors connecting or the API returns errors OAAClient
will raise an OAAClientError
exception. If the payload doesn't conform to the template requirements the OAAClientError.details
will contain a list of any issues encountered.
Since any given source application or service will have different methods for retrieving entities, authorization, and other required metadata, each OAA connector will be slightly different. You should consult the API documentation for your application when considering how you will source the information, and refer to existing Veza-supported OAA connectors for real-world examples.
Connector source code and oaaclient
are thoroughly annotated, for reference when building your own integrations.
For additional information about developing a custom OAA integration, please see section of the User Guide.
from oaaclient.client import OAAClient
from oaaclient.templates import CustomApplication, OAAPermission
# creates a connection class to communicate with Veza
veza_con = OAAClient(url=veza_url, token=veza_api_key)
# creates a new Custom Application model
custom_app = CustomApplication(name="Sample App", application_type="sample")
# define a permission
custom_app.add_custom_permission("owner", [OAAPermission.DataRead, OAAPermission.DataWrite])
# create a local user
jsmith = custom_app.add_local_user(unique_id="jsmith", name="Jane Smith", identities=["[email protected]"])
# create a resource
resource1 = custom_app.add_resource(name="Resource 1", resource_type="Thing")
# assign a user to a resource
jsmith.add_permission(permission="owner", resources=[resource1])
veza_con.push_application(provider, data_source_name, application_object=custom_app)
try:
response = veza_con.push_application(provider_name=provider_name,
data_source_name=data_source_name,
application_object=custom_app,
)
if response.get("warnings"):
print("Push succeeded with warnings:")
for w in response["warnings"]:
print(w)
except OAAClientError as e:
print(f"Error: {e.error}: {e.message} ({e.status_code})", file=sys.stderr)
if hasattr(e, "details"):
for d in e.details:
print(d, file=sys.stderr)
API calls for managing and updating custom data sources
Use these REST API calls to manage and update custom providers and data sources with Open Authorization API.
Creates a custom provider and returns the provider ID.
Lists all configured custom providers.
Returns details for an individual custom provider.
Delete a custom provider by ID.
Return all data sources for a Custom Provider ID.
You can constrain large responses by adding a filter to the request query string. Include the operator (eq
), and value, for example:
CURL <VEZA_URL>/api/v1/providers/custom?filter=name eq "GitHub"&order_by=state
Register a new datasource for a custom provider. There can be more than one datasource for a single provider.
Returns details for a single datasource.
Unbind a datasource from a custom provider, and delete it.
To push authorization metadata for a custom datasource, you can specify the source and provider IDs, and upload a payload with the entities and permissions in JSON format.
A warning is returned for any non-critical errors during payload processing. These can indicate incomplete or inaccurate data in the payload that do not prevent processing, but may warrant attention.
For CSV Upload Integrations, this endpoint pushes CSV data to an existing datasource. Typically, you will first create the integration and define column mappings using the "Add Integration" flow in Veza.
CSV data must base64 encoded into the JSON body of the request.
{
"csv_data": "abc123="
}
CSV_PAYLOAD=$(cat my_app_data.csv | base64)
curl --location https://example.vezacloud.com/api/v1/providers/custom/40bdd318-d320-4574-be90-ca556d59889a/datasources/9bc29dc6-8cd0-4926-992e-7d720305ae2f:push_csv \
--request POST \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $VEZA_API_KEY" \
--data "{\"csv_data\": \"${CSV_PAYLOAD}\"}"
The populated template can be compressed and encoded, for significantly reduced payload size.
Specify the compression_type
. Currently supported: GZIP
.
If compression is selected, Veza will expect the payload json_data
as a compressed, base64-encoded string.
To compress using shell commands:
cat app_payload.json | gzip | base64 -o json_data.b64
Size is typically not an issue when updating custom datasources. However, you may want to compress large payloads. The maximum body size is 100MB (compressed or uncompressed).
Veza expects the populated template as a single JSON string, enclosed in the request body json_data
field. Any "
s and non-ASCII characters must be escaped.
To convert a template to JSON string using Python, the json.dumps()
method could be used:
payload = {"id": provider_id,
"data_source_id": data_source_id,
"json_data": json.dumps(template_contents)
}
You can optionally add an icon for your custom provider by uploading a PNG or SVG file (less than 64kb) as a base64-encoded string:
curl -X POST '{{VezaURL}}/api/v1/providers/custom/962d5eff-285c-4b08-a54e-400eead1e680:icon' \
-H "authorization: Bearer $API_KEY" \
-d '{"icon_base64": "PHN2ZyBmaWxsPSIjMDAwMDAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciICB2aWV3Qm94PSIwIDAgNjQgNjQiIHdpZHRoPSIzMnB4IiBoZWlnaHQ9IjMycHgiPjxwYXRoIGQ9Ik0zMiA2QzE3LjY0MSA2IDYgMTcuNjQxIDYgMzJjMCAxMi4yNzcgOC41MTIgMjIuNTYgMTkuOTU1IDI1LjI4Ni0uNTkyLS4xNDEtMS4xNzktLjI5OS0xLjc1NS0uNDc5VjUwLjg1YzAgMC0uOTc1LjMyNS0yLjI3NS4zMjUtMy42MzcgMC01LjE0OC0zLjI0NS01LjUyNS00Ljg3NS0uMjI5LS45OTMtLjgyNy0xLjkzNC0xLjQ2OS0yLjUwOS0uNzY3LS42ODQtMS4xMjYtLjY4Ni0xLjEzMS0uOTItLjAxLS40OTEuNjU4LS40NzEuOTc1LS40NzEgMS42MjUgMCAyLjg1NyAxLjcyOSAzLjQyOSAyLjYyMyAxLjQxNyAyLjIwNyAyLjkzOCAyLjU3NyAzLjcyMSAyLjU3Ny45NzUgMCAxLjgxNy0uMTQ2IDIuMzk3LS40MjYuMjY4LTEuODg4IDEuMTA4LTMuNTcgMi40NzgtNC43NzQtNi4wOTctMS4yMTktMTAuNC00LjcxNi0xMC40LTEwLjQgMC0yLjkyOCAxLjE3NS01LjYxOSAzLjEzMy03Ljc5MkMxOS4zMzMgMjMuNjQxIDE5IDIyLjQ5NCAxOSAyMC42MjVjMC0xLjIzNS4wODYtMi43NTEuNjUtNC4yMjUgMCAwIDMuNzA4LjAyNiA3LjIwNSAzLjMzOEMyOC40NjkgMTkuMjY4IDMwLjE5NiAxOSAzMiAxOXMzLjUzMS4yNjggNS4xNDUuNzM4YzMuNDk3LTMuMzEyIDcuMjA1LTMuMzM4IDcuMjA1LTMuMzM4LjU2NyAxLjQ3NC42NSAyLjk5LjY1IDQuMjI1IDAgMi4wMTUtLjI2OCAzLjE5LS40MzIgMy42OTdDNDYuNDY2IDI2LjQ3NSA0Ny42IDI5LjEyNCA0Ny42IDMyYzAgNS42ODQtNC4zMDMgOS4xODEtMTAuNCAxMC40IDEuNjI4IDEuNDMgMi42IDMuNTEzIDIuNiA1Ljg1djguNTU3Yy0uNTc2LjE4MS0xLjE2Mi4zMzgtMS43NTUuNDc5QzQ5LjQ4OCA1NC41NiA1OCA0NC4yNzcgNTggMzIgNTggMTcuNjQxIDQ2LjM1OSA2IDMyIDZ6TTMzLjgxMyA1Ny45M0MzMy4yMTQgNTcuOTcyIDMyLjYxIDU4IDMyIDU4IDMyLjYxIDU4IDMzLjIxMyA1Ny45NzEgMzMuODEzIDU3Ljkzek0zNy43ODYgNTcuMzQ2Yy0xLjE2NC4yNjUtMi4zNTcuNDUxLTMuNTc1LjU1NEMzNS40MjkgNTcuNzk3IDM2LjYyMiA1Ny42MSAzNy43ODYgNTcuMzQ2ek0zMiA1OGMtLjYxIDAtMS4yMTQtLjAyOC0xLjgxMy0uMDdDMzAuNzg3IDU3Ljk3MSAzMS4zOSA1OCAzMiA1OHpNMjkuNzg4IDU3LjljLTEuMjE3LS4xMDMtMi40MTEtLjI4OS0zLjU3NC0uNTU0QzI3LjM3OCA1Ny42MSAyOC41NzEgNTcuNzk3IDI5Ljc4OCA1Ny45eiIvPjwvc3ZnPg=="}'
Upload a custom icon to display for an OAA provider.
Return the type and string-encoded icon for a custom provider.
Delete the icon associated with an OAA provider.
Applying additional metadata to OAA entities
In addition to built-in fields such as last_login_at
, OAA supports custom-named properties for users, resources, groups, and other entities in a payload. Custom properties are validated against a set of custom property definitions as part of the JSON payload.
To use custom properties:
Include a definition of all properties as part of the payload push. The definition sets the type for the property.
Set the custom properties for each object.
Built-in properties (such as last_login
, created_at
, and is_active
for local users) enrich Veza graph entities with additional metadata. These built-in properties are described in the template documentation for applications and identity providers.
If a built-in property doesn’t exist for the provider you are modeling, you can define custom properties in the OAA payload. This can enable sophisticated queries on many possible fields (such as encryption_enabled
, password_last_used_at
). Custom properties have a declared data type and are indexed and filterable, like any built-in property.
For example, you could use the custom string property "state" for an app that can be either "active," "suspended," or "disabled." If there's only one state, you could instead use a boolean ("active": "false").
Custom Properties are defined as part of the custom_property_definition
section and require a type. Typing the data allows Veza to provide a better index and search experience. For example, the TIMESTAMP
type enables date-relative filters such as "in the last 30 days."
Allowed types are NUMBER
, STRING
, STRING_LIST
, TIMESTAMP,
and BOOLEAN
.
A type
is required and permanent.
Dates must be in RFC3339 format, which can include a timezone offset or use UTC (2021-01-01T22:47:31-07:00
, 2021-01-01T22:47:31Z
).
The maximum length of a string is 4096 characters.
In the Veza UI, underscores in property names are replaced by spaces, and first letters are capitalized (is_licensed
> Is Licensed).
If a custom property name collides with an existing built-in property, Veza will add the "Custom" prefix (ID
> Custom Id).
In the Query Builder API, custom properties are prefixed with custom_
in responses and must be prefixed with custom_
when used in filter statements.
Once pushed, properties and types can't be altered. You can re-submit the payload with additional custom property definitions to add new properties.
Property values on entities can be modified by pushing a complete payload with new custom_properties
or using incremental update operations.
The provider must be deleted and pushed again to remove properties from a definition or change their types.
The original custom property definition is not required in future submissions but should be saved for later reference.
Here are some example custom property definitions for the custom application and custom identity provider templates.
The Python SDK also supports creating and setting custom properties.
oaaclient
Python SDKapp = CustomApplication(name="Demo", application_type="Demo")
# Define a new local user string property `email`
app.property_definitions.define_local_user_property("email", OAAPropertyType.STRING)
local_user = app.add_local_user(name="name", unique_id="user_id")
# set the property by name
local_user.set_property("email", "[email protected]")
A custom property definition sets possible properties, their types, and the application or resource type they can apply to. The following entities can have custom properties:
Custom Application
application_properties
Scoped by application_type
Custom User
local_user_properties
Custom Group
local_group_properties
Custom Role
local_role_properties
Custom Resource
resources
Scoped by resource_type
The following example shows a custom property definition for GitLab:
"custom_property_definition": {
"applications": [
{
"application_type": "GitLab",
"application_properties": {},
"local_user_properties": {
"id": "NUMBER",
"bot": "BOOLEAN",
"is_licensed": "BOOLEAN",
"state": "STRING"
},
"local_group_properties": {},
"local_role_properties": {},
"resources": [
{
"resource_type": "project",
"properties": {
"id": "NUMBER",
"visibility": "STRING"
}
}
]
}
]
}
Properties are set on users and resources in custom_properties
:
{
"name": "support-bot",
"identities": ["[email protected]"],
"groups": null,
"is_active": true,
"created_at": "2022-01-25T18:55:19.146Z",
"last_login_at": null,
"deactivated_at": null,
"password_last_changed_at": null,
"tags": [],
"custom_properties": {
"id": 7,
"is_licensed": false,
"state": "active",
"bot": true
}
}
Custom IdP Domain
domain_properties
Custom IdP User
user_properties
Custom IdP Group
group_properties
{
"custom_property_definition": {
"domain_properties": null,
"group_properties": {
"group_lead": "STRING"
},
"user_properties": {
"birthday": "TIMESTAMP",
"description": "STRING",
"last_login": "TIMESTAMP",
"is_licensed": "BOOLEAN",
"region": "STRING"
}
},
"name": "My IdP",
"id": "custom_idp",
"domains": [
{
"name": "domain.biz"
}
],
"users": [
{
"name": "Colby Smith",
"custom_properties": {
"is_licensed": false,
"region": "US-West"
}
}
]
}
Strategies for extracting authorization, identity, and resource metadata
When planning an OAA connector, consider how you will gather the information you want to import into Veza. Refer to the application’s documentation to confirm you can obtain the required metadata from the host application.
Ideally, you will be able to list and collect metadata for:
User records
Group memberships
User roles and permissions
Resource names and metadata
For example, the Veza-GitHub connector utilizes the following endpoints (in addition to basic authentication and authorization APIs):
- orgs/{org_name} - Get Organization information
- orgs/{org_name}/members - List members for an organization
- orgs/{org_name}/teams - List teams for an organization
- orgs/{org_name}/teams/{team}/members - List members for a given team
- orgs/{org_name}/repos - List organization repositories
- repos/{org_name}/{repo}/teams - List Team permissions for repository
- repos/{org_name}/{repo}/collaborators?affiliation=direct - List team members with direct permissions
Web-based APIs are a common solution for SaaS apps, but not required for an OAA integration. Just because an endpoint exists does not mean that it returns useful information (some APIs are more designed for client automation than audits). Possible choices for sourcing metadata include:
From a database: Is data for a hosted app available in a database your connector can query?
File-based extraction: is the metadata available in source code or a configuration file, or an exportable report (such as CSV)?
Other options: does the provider have an SDK or CLI interface you can use to retrieve data?
If no machine-readable data is readily available, even screen scraping could be a solution. There are many creative options for extracting the information to populate the template, although an API will typically be the most usable option.
Naming, typing, and searching for top OAA entities
Before uploading entity metadata for an OAA integration, you must create a custom provider and at least one data source under that provider. The provider represents the type of application (such as "Jira Server") and determines the template used for all data sources (e.g."dev", "prod")created under that provider.
The OAA payload is pushed to a specific data source and updates the existing data.
A display icon can be set for the provider, shown throughout the Veza UI to identify the integration and its entities.
Naming the provider and data source is an important step in integration creation. In general,
The provider name should be unique to the integration. Avoid common terms such as "application". Separating providers for systems with several instances such as "production" and "development" can be a good idea.
The provider or data source value should be unique to the integration and incorporate an identifier such as hostname or other instance ID. This ensures that multiple instances of the integration can be separated if necessary.
Do not user random or date values in provider or data source names. Each run of the connector should result in the same provider and data source name, based on discovered values or a configuration parameter.
Each OAA Template supports a concept of name and type. The type enables search for all instances of a specific entity. For example, the Application Type identifies application entities such as users, groups, roles, and resources. It will enable search for all entities in an application. Setting the application type to Zendesk
will allow for search Zendesk Users
, Zendesk Groups
, and other entity types:
The app name
appears when searching for individual entities, and, to differentiate more than one instances of the same system. For example two instances of an application "Portal - Production" and "Portal - Development".
Mapping OAA objects to external and federated identities
In Veza, the Identity Provider (IdP) serves as the representation of the source of an identity (human or otherwise). That identity can have access to many applications, clouds and other data sources. By connecting OAA entities to source IdP identities, Veza can show all the access for that identity. This can also enable powerful correlation queries such as finding deactivated Okta Users with active application accounts.
Veza makes these connections based on the identity information provided in the OAA payload.
Veza Supports Mapping for the following Identity Providers:
Active Directory
Azure AD
Custom IdP
Google Workspace
Okta
OneLogin
For all Identity Providers, the IdP Unique ID
and email
attributes are used to match the source identity to an OAA principal. Some entity types have additional properties usable for identity mapping:
Active Directory
Account Name
Distinguished Name
Azure AD
Principal Name
On Premises SAM Account Name
Okta
User Login
The Application Template's Local User entity represents a user within the application. That Local User can map to an Identity Provider (IdP) by setting the identity value(s) in the Local Users identities
array. Veza will use these identities to create an association between the IdP Identity and the Local User.
Setting external IdP Group identities
is also supported on Local Groups. This should be used when there are no Local User records in the application that correlate to the external user identities. Veza will create a connection between the IdP Group and the Local Group, indicating that all IdP users from that group will have the access granted to the Local Group.
Unknown identities set on users will result in a warning that the identity can not be found. The OAA Local User will still be successfully created.
Note on Identity Mapping from the IdP: You can confirm these values by finding the corresponding entity in Veza search or the data catalog and checking the identities Idp Unique Id and other fields in the details view. Identities that cannot be resolved are returned as warnings when the application payload is pushed.
OAA Identity Provider Users can be connected to other IdPs by using the source_identity
property on the IdP users. For details see
Veza will automatically correlate HRIS Employee records and IdP identities based on the HRIS employee's email
property. The HRIS employee has an optional parameter for identity_id
if the required value is different from the user's email.
Choosing your development environment and deploying an OAA connector
The Open Authorization API enables developers to push metadata to the Veza Authorization Graph using a standard JSON schema. To automate this process, you will need a way to periodically query the data source you want to integrate, populate the schema, and publish the populated template payload to your Veza instance using API calls.
You can interact with the Veza API from your client or language of choice. OAA connectors can (and have) been developed in a variety of languages. The only requirements are the ability to:
query the source application (or read a data file, or another method) to collect identity, resource, and authorization metadata
assemble a dict
or similar data structure and convert to JSON
make REST API calls to publish the payload
Veza provides a Python SDK oaaclient
. This package provides functions for generating and submitting the JSON payload. The Veza developed community connectors use this toolkit, along with all integrations built by active customer developers.
You can run OAAClient as a manual CLI tool for trial purposes, but should automate any long-running OAA implementation. A DevOps or SecOps team is typically responsible for establishing continuous integration of a Veza connector.
Customers are responsible for running their own connectors. The frequency your app must run will depend on use case, ranging from daily to hourly. Veza typically refreshes metadata for built-in integrations on an hourly basis. However, you can decide to publish OAA payloads on any schedule.
At a minimum, a deployed OAA connector should be able to:
Parse the data source and push an updated payload on a schedule
Securely handle secrets such as API keys
Teams should follow their preferred processes and use familiar or existing platforms. Some options include:
AWS Lambda function
Docker/k8s-based container
cron task
GitHub action
Veza expects the default push to contain a full payload. This will overwrite all information in the updated data source.
During development, it is usually most efficient to design your parser for a full discovery. It will be easiest to design the connector to assemble and push the full payload, and use the same code to publish updates.
OAA does support incremental update as an optional behavior–if your custom data source or identity provider provides a way to get individual changes over time, you could design your connector to parse only new or changed entities, and push updates individually (perhaps in response to detected changes). However, this approach will be more complicated. A full parse-and-push will be more efficient, unless there's a method to query the provider for differences since the last push.
To use incremental updates:
Set the is_incremental
flag in the payload to true
Add an add
, modify
or delete
operation to all entities
Copyright 2023 Veza Technologies Inc.
Use of this source code is governed by the MIT license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
CaseInsensitiveDict
Case Insensitive Key Dictionary
Dictionary like object with case insensitive keys for types that support .lower()
such as strings.
Keys do not have to be strings, in the case where the key type does not support .lower()
such as integers the value is used as is.
Example: from oaaclient.structures import CaseInsensitiveDict
>>> x = CaseInsensitiveDict() >>> x["User"] = "value" >>> x.get("user") 'value' >>> "USER" in x True >>> print(x) {'user': 'value'} >>> x CaseInsensitiveDict({'user': 'value'})
__init__
__init__(data=None, **kwargs) → None
Usage examples for the oaaclient Python SDK
The examples on this page can be used as a starting point for building new connectors for custom applications. You should also refer to the current list of for real-world examples, and to check if an integration already exists for your use case.
To run the code, you will need to export environment variables for the Veza URL, user and API keys, for example:
The most common OAA use case is modeling local identities, data sources, and permissions within an application containing sensitive information such as a database, ticket desk, or SCM platform. This sample app uses the CustomApplication
class to create an OAA payload for a typical application that includes users, groups, roles and resources.
Customers that implement a custom Identity Provider (or one that doesn't have a native Veza integration) can use the OAA Custom IdP template to describe federated users and groups. This sample app generates a custom payload containing users, groups, and identity metadata, using the CustomIdPProvider
class.
The example also demonstrates OAA support for AWS role entitlements, for scenarios where users can assume AWS roles in ways that can't be discovered by Veza's native AWS integration.
Sample app for importing Custom Identity Provider users from a CSV file.
This example can be used as a simple starting point for an OAA data source importer. Demonstrates use of CustomIdPProvider
to create IdP users and assign properties using an input file with with the column headings:
identity,name,full_name,is_active,is_guest,manager_id
Both custom properties and can be used to add rich metadata and apply labeling strategies across entities in the Veza data catalog. Both can be viewed by checking an entity's details from tables view or Authorization Graph, and are fully available when searching and filtering results.
Veza tags can be assigned to objects in an OAA payload. One typical use case for tagging within the OAA payload is assigning using Veza SYSTEM_resource_managers
tags.
Tags are applied by providing a key and an optional value for each. A new tag will be created if a matching one doesn't already exist.
Due to superior integration with other Veza functionality and ease of updates, custom properties are recommended as the best approach for adding metadata to OAA entities, unless tagging is required as part of a larger campaign.
Note that while tags can be removed using the tags applied within the template are persistent: an existing tag won't be deleted when pushing a payload with a new tag or empty tag.
After the initial metadata push (which must contain the full payload), you can modify, add, or remove the domain, users, and groups without resubmitting other entities. An is enabled by setting "incremental_change": true
in the json_data
push payload, and specifying the update operation
for each entity to change.
The operation
field indicates the change to make. Valid operation
s are:
"add"
, "modify"
, "delete"
to create, change, or remove an entity.
"add_tag"
, "delete_tag"
to update a tag without altering the entity.
{
"resource_type": "Cluster",
"description": "release staging cluster",
"sub_resources": [],
"tags": [
{
"key": "environment",
"value": "development"
}
]
}
...
"local_groups": [
{
"name": "LGroup1",
"identities": [
"[email protected]"
],
"tags": [
{
"key": "NewTag2Key",
"value": "NewTag2Value"
}
],
"operation": "add_tag"
}
]
...
export OAA_TOKEN="xxxxxxx"
export VEZA_URL="https://myveza.vezacloud.com"
./sample-app.py
#!env python3
"""Example of using `oaaclient` to import users from a CSV file.
Reads user properties from the input file, populates an OAA payload,
and creates a new OAA identity provider containing the imported users.
Expected CSV headers are `identity,name,full_name,is_active,is_guest,manager_id`
Can be updated to match custom column headings or apply custom properties.
Example:
```
export VEZA_URL=<Veza URL>
export VEZA_API_KEY=<Veza API key>
./idp-importer-csv.py --provider MyIdpProvider --datasource MyDatasource ./my-users.csv
```
Copyright 2022 Veza Technologies Inc.
Use of this source code is governed by the MIT
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
"""
from oaaclient.client import OAAClient, OAAClientError
from oaaclient.templates import CustomIdPProvider
import click
import csv
import logging
import os
import sys
logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO)
log = logging.getLogger()
def load_users(idp: CustomIdPProvider, source: str) -> None:
"""Populate the idp with user from the source csv file"""
log.info(f"Loading users from {source}")
with open(source) as f:
for r in csv.DictReader(f):
# get the identity, name, full_name and email
try:
identity = r["identity"]
name = r["name"]
full_name = r["full_name"]
email = r["email"]
except KeyError as e:
log.error(f"Incorrect CSV column headers, missing column {e}")
sys.exit(1)
# create a new IDP user
new_user = idp.add_user(name=name, full_name=full_name, email=email, identity=identity)
# set the user to active and guest or not, look for strings like true/false or yes/no
if r.get("active"):
active_string = r["active"].lower()
if active_string in ["true", "yes"]:
new_user.is_active = True
elif active_string in ["false", "no"]:
new_user.is_active = False
if r.get("is_guest"):
guest_string = r["is_guest"].lower()
if guest_string in ["true", "yes"]:
new_user.is_guest = True
elif guest_string in ["false", "no"]:
new_user.is_guest = False
# if the manager id column is filled in, set the new users manager
if r.get("manager_id"):
new_user.manager_id = r.get("manager_id")
return
@click.command()
@click.option("--provider", required=True)
@click.option("--datasource", required=True)
@click.option("--save-json", is_flag=True)
@click.argument("file", required=True)
def main(provider, datasource, save_json, file):
# load the Veza URL and API key from the environment
veza_url = os.getenv("VEZA_URL")
veza_api_key = os.getenv("VEZA_API_KEY")
if not (veza_url or veza_api_key):
log.error("Must set VEZA_URL and VEZA_API_KEY")
sys.exit(1)
try:
log.info("Testing Veza credentials")
veza_con = OAAClient(url=veza_url, api_key=veza_api_key)
except OAAClientError as e:
log.error("Unable to connect to Veza API")
log.error(e)
sys.exit(1)
# create a new provider if the provider doesn't already exist
if not veza_con.get_provider(provider):
log.info(f"Creating new provider {provider}")
veza_con.create_provider(provider, "identity_provider")
# Configure the Custom IdP
idp = CustomIdPProvider("Custom IdP", idp_type="custom_idp", domain="example.com", description=None)
# load the user from
load_users(idp, file)
try:
# push the IdP to Veza, log and errors or warnings
log.info("Sending to Veza")
response = veza_con.push_application(provider, datasource, idp, save_json=save_json)
if response.get("warnings", None):
log.warning("Push succeeded with warnings")
for w in response['warnings']:
log.warning(w)
log.info("Success")
except OAAClientError as e:
log.error(f"Error during push {e.message} {e.status_code}")
if e.details:
log.error(e.details)
sys.exit(1)
return
if __name__ == "__main__":
main()
#!env python3
"""
Copyright 2022 Veza Technologies Inc.
Use of this source code is governed by the MIT
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
Example of using the `CustomApplication` class to model a typical application where users and groups are assigned
permissions to the application or resources.
If you want to run the code you will need to export environment variables for the Veza URL, user and API keys.
```
export OAA_TOKEN="xxxxxxx"
export VEZA_URL="https://myveza.vezacloud.com"
./sample-app.py
```
"""
from oaaclient.client import OAAClient, OAAClientError
from oaaclient.templates import CustomApplication, OAAPermission
import os
import sys
def main():
# OAA requires an API token, which you can generate from your Veza user profile
# Export the API token, and Veza URL as environment variables
# Making them available to your connector in this way keeps credentials out of the source code
veza_api_key = os.getenv('VEZA_API_KEY')
veza_url = os.getenv('VEZA_URL')
if None in (veza_url, veza_api_key):
print("Unable to local all environment variables")
sys.exit(1)
# Instantiates a client connection. The client will confirm the credentials and Veza URL are valid
# Checking this early helps prevents connection failures in the final stage
veza_con = OAAClient(url=veza_url, api_key=veza_api_key)
# Create an instance of the OAA CustomApplication class, modeling the application name and type
# `name` will be displayed in the Veza UI
# `application_type` should be a short key reflecting the source application authorization is being modeled for
# You can use the same type for multiple applications
custom_app = CustomApplication(name="Sample App", application_type="sample")
# In the OAA payload, each permission native to the custom app is mapped to the Veza effective permission (data/nondata C/R/U/D).
# Permissions must be defined before they can be referenced, as they are discovered or ahead of time.
# For each custom application permission, bind them to the Veza permissions using the `OAAPermission` enum:
custom_app.add_custom_permission("admin", [OAAPermission.DataRead, OAAPermission.DataWrite])
custom_app.add_custom_permission("operator", [OAAPermission.DataRead])
# Create resources and sub-resource to model the entities in the application
# To Veza, an application can be a single entity or can contain resources and sub-resources
# Utilizing resources enables tracking of authorizations to specific components of the system being modeled
# Setting a `resource_type` can help group entities of the same type for reporting/queries
entity1 = custom_app.add_resource(name="Entity1", resource_type="thing", description="Some entity in the application")
entity2 = custom_app.add_resource(name="Entity2", resource_type="thing", description="Another entity in the application")
other = custom_app.add_resource(name="Other", resource_type="other", description="Something totally different")
# Sub-resources can be added to any resource (including other sub-resources)
child1 = entity1.add_sub_resource(name="Child 1", resource_type="child", description="My information about resource")
child1.add_sub_resource(name="Grandchild 1", resource_type="grandchild", description="My information about resource")
# Any users and groups local to the application can be defined.
# IdP users can be mapped directly without defining them in the OAA application (see below)
custom_app.add_local_user("bob")
# A local user can be associated with an IdP user by adding an identity to the user:
custom_app.local_users["bob"].add_identity("[email protected]")
# Identities, groups and roles can be assigned to local users at creation or after (groups and roles must exist):
jane = custom_app.add_local_user("jane", identities="[email protected]")
# when adding a user the new user is returned and can updated if needed, there are multiple built in properties that can be set for a user
jane.is_active = False
jane.created_at = "2022-01-26T20:48:12.460Z"
# Define a local group and add a user to it
custom_app.add_local_group("admins")
custom_app.local_users["jane"].add_group("admins")
# adding local users and groups requires that the name not already exist
if "yan" not in custom_app.local_users:
custom_app.add_local_user("yan")
# For each Identity (user, group, IdP) assign permissions to the application or resource.
# The identities (users, groups) permissions and resources must already be defined
# To add a permission directly to the application use `apply_to_application=True`
custom_app.local_users["bob"].add_permission(permission="operator", apply_to_application=True)
custom_app.local_groups["admins"].add_permission(permission="admin", apply_to_application=True)
# You can describe specific permissions to individual resources or sub-resources:
custom_app.local_users["yan"].add_permission(permission="operator", resources=[entity1, child1])
# Authorization can also be created directly for an IdP identity
custom_app.add_idp_identity("[email protected]")
# resources can also be referenced by name from the application model
custom_app.idp_identities["[email protected]"].add_permission(permission="admin", resources=[custom_app.resources['Entity1']])
# Once all authorizations have been mapped, the final step is to publish the app to Veza
# Connect to the API to Push to Veza, define the provider and create if necessary:
provider_name = "Sample"
provider = veza_con.get_provider(provider_name)
if provider:
print("-- Found existing provider")
else:
print(f"++ Creating Provider {provider_name}")
provider = veza_con.create_provider(provider_name, "application")
print(f"-- Provider: {provider['name']} ({provider['id']})")
# Push the metadata payload:
try:
response = veza_con.push_application(provider_name,
data_source_name=f"{custom_app.name} ({custom_app.application_type})",
application_object=custom_app,
save_json=False
)
if response.get("warnings", None):
# Veza may return warnings on a successful uploads. These are informational warnings that did not stop the processing
# of the OAA data but may be important. Specifically identities that cannot be resolved will be returned here.
print("-- Push succeeded with warnings:")
for e in response["warnings"]:
print(f" - {e}")
except OAAClientError as e:
print(f"-- Error: {e.error}: {e.message} ({e.status_code})", file=sys.stderr)
if hasattr(e, "details"):
for d in e.details:
print(f" -- {d}", file=sys.stderr)
return
if __name__ == '__main__':
main()
#!env python3
"""
Copyright 2022 Veza Technologies Inc.
Use of this source code is governed by the MIT
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
Example of using the `CustomIdPProvider` class to model an identity provider as a source of users.
If you want to run the code you will need to export environment variables for the Veza URL, user and API keys.
```
export VEZA_API_KEY="xxxxxxx"
export VEZA_URL="https://myveza.vezacloud.com"
./sample-idp.py
```
Since the example includes fake AWS ARNs that Veza will not have discovered the expected output will
contain warning like "Cannot find IAM role by names ..."
"""
from oaaclient.client import OAAClient, OAAClientError
from oaaclient.templates import CustomIdPProvider, OAAPropertyType
import os
import sys
def main():
# OAA requires a Veza API key, which you can generate from Administration > API Keys
# Export the API key, and Veza URL as environment variables
# Making them available to your connector in this way keeps credentials out of the source code
veza_api_key = os.getenv("VEZA_API_KEY")
veza_url = os.getenv("VEZA_URL")
if not veza_api_key or not veza_url:
print("Unable to load VEZA_API_KEY and VEZA_URL from OS")
sys.exit(1)
# Instantiates a client connection. The client will confirm the credentials and Veza URL are valid
# Checking this early helps prevents connection failures in the final stage
veza_con = OAAClient(url=veza_url, api_key=veza_api_key)
# create a CustomIdPProvider to represent your IdP. This can be named generically or specific to the environment if you have
# multiple namespaces to model. idp_type will typically be the technology/vendor for the provider.
idp = CustomIdPProvider("My IdP", domain="example.com", idp_type="custom_idp")
# add users to the idp, properties for users can be set during creation or updated after
idp.add_user("mrichardson", full_name="Michelle Richardson", email="[email protected]")
evargas_user = idp.add_user("evargas")
evargas_user.full_name = "Elizabeth Vargas"
evargas_user.email = "[email protected]"
# users and groups can have optional identity property. The identity serves as the unique reference identifier across
# Veza. If omitted CustomIdPProvider will automatically populate the identity with the name
idp.add_user("willis", email="[email protected]", identity="cwilliams")
# OAA can support custom properties for users to track additional metadata unique to the environment
# to use custom properties the property must first be defined and given a type, then can be set for the individual entity
idp.property_definitions.define_user_property("region", OAAPropertyType.STRING)
idp.property_definitions.define_user_property("is_contractor", OAAPropertyType.BOOLEAN)
idp.users['willis'].set_property("region", "NorthAmerica")
idp.users['willis'].set_property("is_contractor", True)
# Create Groups
idp.add_group("developers")
idp.add_group("sec-ops")
idp.add_group("everyone", full_name="All Company Employees")
# users can be added to groups using the add_group function, users can be added to multiple groups in a single call
for username in idp.users:
idp.users[username].add_groups(["everyone"])
evargas_user.add_groups(["developers", "sec-ops"])
idp.users["mrichardson"].add_groups(["developers"])
# Veza CustomIdP supports tracking the AWS Roles a user can assume. For users who can assume roles Veza can calculate
# their effective permissions to AWS resources based on the role(s)
# roles are added by ARN
idp.users["mrichardson"].add_assumed_role_arns(["arn:aws:iam::123456789012:role/role001", "arn:aws:iam::123456789012:role/role002"])
# After adding users and groups, the IdP information is pushed to Veza using the OAA API
provider_name = "Sample-IdP"
provider = veza_con.get_provider(provider_name)
if provider:
print("-- Found existing provider")
else:
print(f"++ Creating Provider {provider_name}")
provider = veza_con.create_provider(provider_name, "identity_provider")
print(f"-- Provider: {provider['name']} ({provider['id']})")
# Push the metadata payload:
try:
response = veza_con.push_application(provider_name,
data_source_name=f"{idp.name} ({idp.idp_type})",
application_object=idp,
save_json=True
)
if response.get("warnings", None):
# Veza may return warnings on a successful uploads. These are informational warnings that did not stop the processing
# of the OAA data but may be important, for example: AWS role ARNs assigned to users that Veza has not discovered
print("-- Push succeeded with warnings:")
for e in response["warnings"]:
print(f" - {e}")
except OAAClientError as e:
print(f"-- Error: {e.error}: {e.message} ({e.status_code})", file=sys.stderr)
if hasattr(e, "details"):
for d in e.details:
print(f" -- {d}", file=sys.stderr)
if __name__ == '__main__':
main()
client
: Classes for calling Veza APIs and managing OAA providers and data sources.
templates
: Classes for constructing an OAA JSON payload (Custom "Application" or "IdP").
utils
: oaaclient
utility functions.
client.OAAClient
: OAA API Connection and Management.
client.OAAClientError
: Error raised by OAAClient.
templates.Application
: Base class for CustomApplication.
templates.ApplicationPropertyDefinitions
: Model for defining custom properties for application and its entities (users, groups, roles, resources).
templates.CustomApplication
: Class for modeling application authorization using the OAA Application template.
templates.CustomIdPDomain
: Domain model for Custom IdP provider.
templates.CustomIdPGroup
: Group model for CustomIdPProvider.
templates.CustomIdPProvider
: CustomIdPProvider class for modeling Identity Providers (IdP) using OAA Custom Identity Provider Template.
templates.CustomIdPUser
: User model for CustomIdPProvider.
templates.CustomPermission
: CustomPermission class for defining CustomApplication
permissions.
templates.CustomResource
: Class for resources and sub-resources.
templates.IdPEntityType
: IdP entity types.
templates.IdPIdentity
: IdP identity derived from Identity base class.
templates.IdPPropertyDefinitions
: Model for defining custom properties for CustomIdPProvider and its entities (users, groups, domain).
templates.IdPProviderType
: Veza supported IdP provider types.
templates.Identity
: Base class for deriving all identity types (should not be used directly).
templates.LocalGroup
: LocalGroup identity.
templates.LocalRole
: Represent a Custom Application Local Role.
templates.LocalUser
: LocalUser identity, derived from Identity base class.
templates.OAAIdentityType
: Types of identities for permission mapping.
templates.OAAPermission
: Canonical permissions used by Veza Authorization Framework.
templates.OAAPropertyType
: Supported types for custom properties on OAA entities such as application, resource, and identity.
templates.OAATemplateException
: General exception used for violations of the template schema.
templates.Provider
: Base class for CustomProvider.
templates.Tag
: Veza tag data model.
templates.append_helper
: Helper function to simplify appending.
templates.unique_strs
: Returns a list of unique strings from input list case insensitive
utils.encode_icon_file
: read an icon file to a base64 encoded string
utils.load_json_from_file
: Load JSON from file
utils.log_arg_error
: Helper function for logging errors when loading parameters
oaaclient
utility functions.
Copyright 2022 Veza Technologies Inc.
Use of this source code is governed by the MIT license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
helper functions commonly used by OAA integrations
log_arg_error
log_arg_error(log: object, arg: str = None, env: str = None) → None
Helper function for logging errors when loading parameters
Helper function used to create consistent messages in connectors when required parameters can be set at command line or as environment variables.
Message can include information on parameter and/or environment variable but must provide one.
Args:
log
(object): logging facility object to log to
arg
(str, optional): Command line option for parameter such as --veza-url
. Defaults to None.
env
(str, optional): OS Environment variable for parameter such as VEZA_URL
. Defaults to None.
Raises:
Exception
: if neither arg
or env
are supplied
load_json_from_file
load_json_from_file(json_path: str) → dict
Load JSON from file
Args:
json_path
(str): path to JSON file on disk
Raises:
Exception
: Unable to process JSON
Exception
: Error reading JSON file
Returns:
dict
: JSON decoded to dictionary
encode_icon_file
encode_icon_file(icon_path: str) → str
read an icon file to a base64 encoded string
Args:
icon_path
(str): Path to icon file on disk
Returns:
str
: base64 encoding of file
exists_in_query_array
exists_in_query_array(value_to_find, input_array) → bool
build_report
build_report(veza_con, report_definition: dict) → dict
Creates or updates a Veza report from a dictionary
Creates a report and containing queries from a dictionary definition. Function will create any queries it does not find based on name. If a query with the same name already exists the existing query will be added to the report.
If a report already exists with the same name any missing queries will be added to the report.
report_definition
must be a dictionary with name
for the report and queries
list of Veza query definitions:
{"name": "My Report", "queries": [{..},{...}]}
Args:
veza_con
(OAAClient): OAAClient connection to make Veza API calls
report_definition
(dict): Report definition
Raises:
ValueError
: Missing name or queries key
Returns:
dict
: API response from Report creation or update
truncate_string
truncate_string(source_str: str, length: int = 256) → str
Helper function to truncate strings
Helper function to truncate strings to conform to maximum length requirements for templates.
Returns a string that is the first N bytes of the source string
Args:
source_str
(str): Source string to truncate
length
(int, optional): Length to shorten to. Defaults to 256.
Returns:
str
: truncated string
Modifying custom providers using a partial OAA payload
When developing your OAA integration, whether to implement incremental updates depends on your use case. If you don't have a convenient way to track provider-side changes, it is typically easier to do a full extraction and metadata push, to not miss changes within the app or IdP.
After the initial metadata push, you can modify, add, or remove OAA entities, permissions, and properties without needing to submit the full payload each time. A first push can't be an incremental update.
An incremental update is specified by setting "incremental_change": true
in the json_data
push payload and adding an update operation
for each entity to change. Most payload objects support incremental update operations:
application, user, group, role, permission, role_assignments
domain, group, user
The operation
field indicates the change to make. Valid operation
s are:
"add"
, "modify"
, "delete"
to create, change, or remove an entity.
"add_resource"
, "delete_resource"
to modify resources in applications, permissions and role scopes.
"add_tag"
, "delete_tag"
to update a tag without altering the entity.
The object to update must contain an ID (name
or identity
) and the properties to create or modify. An error response will provide more details if an operation is invalid or unavailable.
See the sample payload below for more examples.
When modifying dynamic properties, all five properties must be present.
add
and modify
can't be used on the same object. For example, to update both tags and properties on an entity, use:
"resources": [
{
"name": "resource1",
"resource_type": "type1",
"tags": [
{
"key": "keyDelta1",
"value": "valDelta1"
}
],
"operation": "add_tag"
},
{
"name": "resource1",
"resource_type": "type1",
"operation": "modify"
}
]
The following JSON example for custom application includes a range of update operations. Note that when modifying tags, only include the identity and tags (no other properties such as manager_id
can be present).
{
"incremental_change": true,
"applications": [
{
"name": "Controller1",
"application_type": "WebServer",
"description": "The base web server",
"local_users": [
{
"name": "LUser1",
"identities": [
"[email protected]"
],
"operation": "delete"
},
{
"name": "LUser1",
"identities": [
"[email protected]"
],
"operation": "add"
}
],
"local_groups": [
{
"name": "LGroup1",
"identities": [
"[email protected]"
],
"tags": [
{
"key": "groupTag2Key",
"value": "groupTag2Value"
}
],
"operation": "add_tag"
}
],
"local_roles": [
{
"name": "LRole2",
"permissions": [
"FULL"
],
"operation": "delete"
}
],
"resources": [
{
"name": "resource1",
"sub_resources": [
{
"name": "sub1b",
"operation": "delete"
}
],
"operation": "modify"
}
]
}
],
"permissions": [
{
"name": "Just Access",
"operation": "modify"
},
{
"name": "NonData",
"PermissionType": [
"NonData"
],
"operation": "add"
},
{
"name": "READ",
"permission_type": [
"DataRead"
],
"operation": "delete"
},
{
"name": "READ",
"permission_type": [
"DataRead"
],
"operation": "add"
}
],
"identity_to_permissions": [
{
"identity": "[email protected]",
"identity_type": "IDP",
"role_assignments": [
{
"application": "Controller1",
"role": "LRole1",
"apply_to_application": true,
"operation": "delete"
},
{
"application": "Controller1",
"role": "LRole2",
"resources": [
"resource1",
"resource1.sub1a.sub2b"
],
"operation": "add"
}
]
},
{
"identity": "[email protected]",
"identity_type": "IDP",
"role_assignments": [
{
"application": "Controller1",
"role": "LRole1",
"resources": [
"resource1.sub1b",
"resource1.sub1c"
],
"operation": "delete_resource"
},
{
"application": "Controller1",
"role": "LRole1",
"resources": [
"resource1.sub1a"
],
"operation": "add_resource"
}
],
"application_permissions": [
{
"application": "WebServer2",
"permission": "Just Access",
"operation": "delete"
}
]
}
]
}
Using local roles, local users, and canonical permissions to describe access
local_users
to describe local and federated identities and service accountsMost applications will have some concept of a “local user” that can have varying permissions to data and metadata. Most modern cloud applications offer some form of single sign-on. In some cases, local user accounts correspond to an external federated identity, or even only exist ephemerally.
The Open Authorization API is able to represent local users, local groups, and external identities. An external identity could be an user connecting directly to a resource through Single Sign On, or an SSO group. A local user could be a just-in-time account created when an IdP user first connects.
You should represent system users using local_user
objects, even for apps that are SSO-enabled. The local user object is able to contain useful attributes such as activity status and timestamps, enabling rich search on those entities. A local user can also reference a federated identity, creating graph relationships between IdP entities and local users and groups when the payload is parsed. Veza handles these cross service connections similarly to built-in integrations such as Snowflake.
The OAA template supports direct permissions from IdP users to resources. However, including a local user entity in your OAA mapping strategy is still recommended when possible.
Users can also be grouped and assigned permissions with local_groups
.
Veza understands an identity's level access to a resource in terms of raw system permissions in the provider's terms (such as AWS IAM s3:DeleteBucket
), and canonical permissions (such asDATA DELETE
). This allows Veza to answer the questions: "Who has MERGE
permission on production repositories?" and "who can read data from any resource flagged for PII compliance?".
OAA requires you to map each granular application permission (like "view ticket" or "close ticket") to its corresponding C/R/U/D permission. This enables search and audit of effective and configured access.
Manage Access
Metadata Write
Merge
Data Write
Pull
Data Read
SaaS apps tend to be role-based, and organizations that follow security best practices will typically restrict the assignment of permissions directly to users. Following standard RBAC concepts, an OAA role represents a collection of permissions which can be scoped to individual resources or specific resource types.
If permissions granted by a role only apply to specific resource types or sub-resources, create the custom permissions individually and add them to the role in a list:
self.app.add_local_role("Write", ["Pull", "Fork", "Push", "Merge"])
Under the custom application OAA schema, users can have direct permissions and role-based permissions to resources. Modeling these role-based permission assignments will enable searches such as "show all users with the maintain
role in GitHub."
Note that “roles” in some apps actually function more like groups (lists of users), in which case they should be modeled using
local_groups
.
For payloads where permissions apply for the entire application, you can create a single custom permission for a role, containing all the associated canonical permissions:
self.app.add_custom_permission("limited_user", permissions=[OAAPermission.DataRead, OAAPermission.MetadataRead])
self.app.add_local_role("Responder", unique_id="limited_user", permissions=["limited_user"])
As demonstrated in the OAA PagerDuty connector, this is a quick way to model authorization structures such as:
Account Owner
owner
Data Write, Data Read, Data Delete, Metadata Read, Metadata Write
Responder
limited_user
Data Read, Metadata Read
Observer
limited_user
Data Read, Metadata Read
POST /api/v1/providers/custom HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 1234
{
"name": "text",
"custom_template": "text",
"provisioning": true,
"push_type": 1,
"internal_app_name": "text",
"configuration_json": "text",
"data_plane_id": "text",
"custom_templates": [
"text"
],
"csv_mapping_configuration": {
"template_type": "text",
"column_mappings": [
{
"column_name": "text",
"destination_type": "text",
"destination_property": "text",
"custom_property": {
"name": "text",
"type": 1,
"lcm_unique_identifier": true
},
"as_list": true
}
],
"application": {
"application_name": "text",
"application_type": "text",
"identity": [
"text"
],
"resource_type": "text"
},
"advanced": {
"list_delimiter": "text"
},
"idp": {
"idp_type": "text",
"domain": "text"
},
"hris": {
"hris_name": "text",
"hris_type": "text",
"hris_url": "text",
"hris_identity_mapping": {
"mappings": [
{
"destination_datasource_type": "text",
"destination_datasource_oaa_app_type": "text",
"type": 1,
"mode": 1,
"transformations": [
1
],
"custom_value": "text",
"property_matchers": [
{
"source_property": 1,
"destination_property": 1,
"custom_source_property": "text",
"custom_destination_property": "text"
}
],
"id_matchers": [
{
"source_id": "text",
"destination_id": "text"
}
],
"destination_datasources": [
{
"type": "text",
"oaa_app_type": "text"
}
]
}
],
"use_email": true
},
"hris_provisioning_source": true
}
},
"secret_references": [
{
"secret_id": "text",
"vault_id": "text"
}
]
}
{
"value": {
"id": "text",
"external_id": "text",
"name": "text",
"custom_template": "text",
"custom_templates": [
"text"
],
"state": 1,
"application_types": [
"text"
],
"idp_types": [
"text"
],
"file_system_types": [
"text"
],
"hris_types": [
"text"
],
"principal_types": [
"text"
],
"schema_definition_json": "text",
"provisioning": true,
"push_type": 1,
"rbac_id": "text",
"internal_app_name": "text",
"configuration_json": "text",
"data_plane_id": "text",
"lifecycle_management_state": 1,
"team_id": "text",
"csv_mapping_configuration": {
"template_type": "text",
"column_mappings": [
{
"column_name": "text",
"destination_type": "text",
"destination_property": "text",
"custom_property": {
"name": "text",
"type": 1,
"lcm_unique_identifier": true
},
"as_list": true
}
],
"application": {
"application_name": "text",
"application_type": "text",
"identity": [
"text"
],
"resource_type": "text"
},
"advanced": {
"list_delimiter": "text"
},
"idp": {
"idp_type": "text",
"domain": "text"
},
"hris": {
"hris_name": "text",
"hris_type": "text",
"hris_url": "text",
"hris_identity_mapping": {
"mappings": [
{
"destination_datasource_type": "text",
"destination_datasource_oaa_app_type": "text",
"type": 1,
"mode": 1,
"transformations": [
1
],
"custom_value": "text",
"property_matchers": [
{
"source_property": 1,
"destination_property": 1,
"custom_source_property": "text",
"custom_destination_property": "text"
}
],
"id_matchers": [
{
"source_id": "text",
"destination_id": "text"
}
],
"destination_datasources": [
{
"type": "text",
"oaa_app_type": "text"
}
]
}
],
"use_email": true
},
"hris_provisioning_source": true
}
},
"secret_references": [
{
"id": "text",
"secret_id": "text",
"vault_id": "text",
"vault": {
"id": "text",
"name": "text",
"vault_provider": "text",
"insight_point_id": "text",
"deleted": true
}
}
]
}
}
The maximum number of results to be returned. Fewer results may be returned even when more pages exist.
The token specifying the specific page of results to retrieve.
GET /api/v1/providers/custom HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{
"values": [
{
"id": "text",
"external_id": "text",
"name": "text",
"custom_template": "text",
"custom_templates": [
"text"
],
"state": 1,
"application_types": [
"text"
],
"idp_types": [
"text"
],
"file_system_types": [
"text"
],
"hris_types": [
"text"
],
"principal_types": [
"text"
],
"schema_definition_json": "text",
"provisioning": true,
"push_type": 1,
"rbac_id": "text",
"internal_app_name": "text",
"configuration_json": "text",
"data_plane_id": "text",
"lifecycle_management_state": 1,
"team_id": "text",
"csv_mapping_configuration": {
"template_type": "text",
"column_mappings": [
{
"column_name": "text",
"destination_type": "text",
"destination_property": "text",
"custom_property": {
"name": "text",
"type": 1,
"lcm_unique_identifier": true
},
"as_list": true
}
],
"application": {
"application_name": "text",
"application_type": "text",
"identity": [
"text"
],
"resource_type": "text"
},
"advanced": {
"list_delimiter": "text"
},
"idp": {
"idp_type": "text",
"domain": "text"
},
"hris": {
"hris_name": "text",
"hris_type": "text",
"hris_url": "text",
"hris_identity_mapping": {
"mappings": [
{
"destination_datasource_type": "text",
"destination_datasource_oaa_app_type": "text",
"type": 1,
"mode": 1,
"transformations": [
1
],
"custom_value": "text",
"property_matchers": [
{
"source_property": 1,
"destination_property": 1,
"custom_source_property": "text",
"custom_destination_property": "text"
}
],
"id_matchers": [
{
"source_id": "text",
"destination_id": "text"
}
],
"destination_datasources": [
{
"type": "text",
"oaa_app_type": "text"
}
]
}
],
"use_email": true
},
"hris_provisioning_source": true
}
},
"secret_references": [
{
"id": "text",
"secret_id": "text",
"vault_id": "text",
"vault": {
"id": "text",
"name": "text",
"vault_provider": "text",
"insight_point_id": "text",
"deleted": true
}
}
]
}
],
"next_page_token": "text",
"has_more": true
}
GET /api/v1/providers/custom/{id} HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{
"value": {
"id": "text",
"external_id": "text",
"name": "text",
"custom_template": "text",
"custom_templates": [
"text"
],
"state": 1,
"application_types": [
"text"
],
"idp_types": [
"text"
],
"file_system_types": [
"text"
],
"hris_types": [
"text"
],
"principal_types": [
"text"
],
"schema_definition_json": "text",
"provisioning": true,
"push_type": 1,
"rbac_id": "text",
"internal_app_name": "text",
"configuration_json": "text",
"data_plane_id": "text",
"lifecycle_management_state": 1,
"team_id": "text",
"csv_mapping_configuration": {
"template_type": "text",
"column_mappings": [
{
"column_name": "text",
"destination_type": "text",
"destination_property": "text",
"custom_property": {
"name": "text",
"type": 1,
"lcm_unique_identifier": true
},
"as_list": true
}
],
"application": {
"application_name": "text",
"application_type": "text",
"identity": [
"text"
],
"resource_type": "text"
},
"advanced": {
"list_delimiter": "text"
},
"idp": {
"idp_type": "text",
"domain": "text"
},
"hris": {
"hris_name": "text",
"hris_type": "text",
"hris_url": "text",
"hris_identity_mapping": {
"mappings": [
{
"destination_datasource_type": "text",
"destination_datasource_oaa_app_type": "text",
"type": 1,
"mode": 1,
"transformations": [
1
],
"custom_value": "text",
"property_matchers": [
{
"source_property": 1,
"destination_property": 1,
"custom_source_property": "text",
"custom_destination_property": "text"
}
],
"id_matchers": [
{
"source_id": "text",
"destination_id": "text"
}
],
"destination_datasources": [
{
"type": "text",
"oaa_app_type": "text"
}
]
}
],
"use_email": true
},
"hris_provisioning_source": true
}
},
"secret_references": [
{
"id": "text",
"secret_id": "text",
"vault_id": "text",
"vault": {
"id": "text",
"name": "text",
"vault_provider": "text",
"insight_point_id": "text",
"deleted": true
}
}
]
}
}
DELETE /api/v1/providers/custom/{id} HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{}
The maximum number of results to be returned. Fewer results may be returned even when more pages exist.
The token specifying the specific page of results to retrieve.
GET /api/v1/providers/custom/{id}/datasources HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{
"values": [
{}
],
"next_page_token": "text",
"has_more": true
}
POST /api/v1/providers/custom/{id}/datasources HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 52
{
"id": "text",
"name": "text",
"custom_template": "text"
}
{
"value": {
"id": "text",
"name": "text"
}
}
GET /api/v1/providers/custom/{id}/datasources/{data_source_id} HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{
"value": {}
}
DELETE /api/v1/providers/custom/{id}/datasources/{data_source_id} HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{}
POST /api/v1/providers/custom/{id}/datasources/{data_source_id}:push HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 98
{
"id": "text",
"data_source_id": "text",
"json_data": "text",
"compression_type": 1,
"priority_push": true
}
{
"warnings": [
{
"message": "text"
}
]
}
POST /api/v1/providers/custom/{id}/datasources/{data_source_id}:push_csv HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 125
{
"id": "text",
"data_source_id": "text",
"csv_data": "text",
"name": "text",
"type": "text",
"compression_type": 1,
"priority_push": true
}
{
"warnings": [
{
"message": "text"
}
]
}
POST /api/v1/providers/custom/{id}/datasources/{data_source_id}:push HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 98
{
"id": "text",
"data_source_id": "text",
"json_data": "text",
"compression_type": 1,
"priority_push": true
}
{
"warnings": [
{
"message": "text"
}
]
}
POST /api/v1/providers/custom/{id}/datasources/{data_source_id}:push_csv HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 125
{
"id": "text",
"data_source_id": "text",
"csv_data": "text",
"name": "text",
"type": "text",
"compression_type": 1,
"priority_push": true
}
{
"warnings": [
{
"message": "text"
}
]
}
POST /api/v1/providers/custom/{id}:icon HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Content-Type: application/json
Accept: */*
Content-Length: 34
{
"id": "text",
"icon_base64": "text"
}
{}
GET /api/v1/providers/custom/{id}:icon HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{
"mime_type": "text",
"icon_base64": "text"
}
GET /api/v1/providers/custom:icon HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{
"mime_type": "text",
"icon_base64": "text",
"provider_id": "text"
}
DELETE /api/v1/providers/custom/{id}:icon HTTP/1.1
Host:
Authorization: Bearer Bearer <API key>
Accept: */*
{}
How to use the Application Template to represent various types of application
The OAA Application template allows for representing the basic elements of an application and is flexible to allow for multiple different patterns.
The custom application template provides a framework for describing:
Who: Users, Groups, and other identities
What: Application, resources, sub-resources
How: Permissions, canonical mappings, roles, authorization
Application
The Application itself is the first entity as part of the Application submission. The application has a name and a type. The name can be specific such as "West Production" and the type is typically the vendor, technology, or product such as "GitLab". Veza will group the rest of the entities by the application type such as "GitLab Users" across all instances of an application type, however, entities will only be directly associated with the application they are submitted with. Applications additionally can have custom properties assigned to further enrich the data. This can be especially useful to distinguish between multiple instances of an application type.
Local Users
The Local User object represents the local user of the application. It can store application-specific information such as the identifier, name, active status, last login time, and custom properties specific to the application. An external Identity can be associated with the user, enabling Veza to connect the source Identity Provider (IdP). This creates a connection in the Authorization Graph between the application user and a source identity such as Okta or Active Directory. Users can be added to groups, or have roles and permissions directly assigned.
Local Groups
Local Groups represent a collection of users. Groups can store additional metadata about the group to support search and filters. Groups can also contain other groups, to model applications that support nested groups. Groups can have role and permission assignments in the application.
Local Roles
Local Roles represent a collection of permissions within an application. When a user or group is assigned a role, that user or group is assigned all the permissions from the role. Like other entities, roles can contain additional properties to enrich the metadata of the role, such as description or application-specific attributes.
Permissions
OAA allows for expressing the permissions of the application in its own terms. In the Application Template, permissions are defined by application-specific terms such as "Close Ticket" or "Approve User". These permissions will also allow for setting corresponding Veza Canonical Permissions such as "Data Read", "Data Write", or "Metadata Read". This enables search and filters across permissions using CRUD-like equivalents.
Available Canonical Permissions: - DataRead - DataWrite - DataCreate - DataDelete - MetadataRead - MetadataWrite - MetadataCreate - MetadataDelete - NonData - Uncategorized
Resources
Resources can be used to model components within applications that users can be assigned roles or permissions on. For example, an OAA resource may be used to model a Project in an application like Jira. Users can have different roles in different projects, by assigning the correct users or groups their respective roles on the resources. Each resource has a Type which Veza uses when displaying and searching the resources for an application. Applications can have multiple types of Resources. Like other entities, resources can have properties containing additional resource metadata.
Below are some examples of how applications may be modeled in OAA. These examples have all been implemented using the Application template.
PagerDuty is an example of an application where users can have roles at the application level (global roles) but also within the PageDuty Teams that a user is a member of. Understanding which Users are part of which Teams and what level of access they have in those Teams is important for reviewing access in an application like PagerDuty. The OAA Application template can be used to model this type of application using the following mappings.
User
Local User
Team
Local Group
To group the Users
Application Role
Local Role
Applied to the Application
Team
Local Resource
To represent the User's role in the Team
Team Role
Local Role
Applied to the Team Resource
With this model, Veza can show the levels of access users have within the overall Application and their individual Teams. In this example, we can see the User Gary Ward has the Role "Restricted Access" in the PagerDuty Application, and the "Observer" Role in the "Support" Team Resource.
Python Code for the PagerDuty connector can be referenced on the Veza GitHub
GitLab is a good example of a more complex application that can be modeled using the Veza OAA Application Template. By correctly modeling the applications, Veza can show which users have access to GitLab Groups and Projects to show details beyond just "who has access to GitLab?"
User
Local User
Group
Resource
Project
Sub Resource
Each GitLab User is represented with the Application Local User. GitLab assigns each user a unique ID number to each user, using this as the ID for the Local User makes it easier when associating groups and roles to Projects later. The Local User also allows for setting properties to represent the user's state such as is_active
or last_login_at
, custom properties to represent GitLab-specific properties can also be utilized like if the user is licensed or not. The identity for the Local User is set to enable OAA connection to source Identity Providers configured in Veza.
A GitLab Group represents both a collection of Users, a collection of Projects, and possibly other sub-groups. This is a good example of where some mapping needs to be done to represent GitLab using the Application Template. Since GitLab Users have a specific role in the GitLab Group, it is correct to represent the GitLab Group as an OAA Resource. Members are assigned an OAA Local Role to the Resource that represents the GitLab Group, denoting their level of access (Owner, Developer, Guest, and so on). Veza can return all the members of a GitLab Group by querying for the relation between the OAA User and the OAA Resource. Additionally, the role within the GitLab Group is preserved by the OAA Role.
GitLab Roles are assigned to users or Groups as part of a Group or Project. GitLab is an example of an application with fixed roles where the roles and behaviors are coded directly into the application. For the OAA Application Template, the corresponding Local Roles are also coded directly into the connector by reviewing the GitLab documentation on available roles and the permissions associated with the roles. It is not always necessary to capture every Permission as part of the Local Role. Which permissions are important and how much detail is needed will be determined by the queries you intend to run, and the scope of Access Reviews that will be conducted in Veza.
GitLab Projects are also represented as an OAA Sub-Resource with the type set to Project. The Project Sub-Resource is added to the corresponding Group resource. Local User and Local Groups can be assigned roles on the project based on their direct assignments. In the case of GitLab, permissions can be inherited from above. This is an optional behavior for permissions, which can be set to automatically apply to sub-resources, or only apply directly to the assigned resources. Additionally, permissions can be set to apply to certain types of resources so that roles can be used across resources and only the correct permissions will be applied.
Python Code for the GitLab connector can be referenced on the Veza GitHub
Methods for working with Custom Data Providers and Sources
This document provides a basic overview of the API requests for creating and updating an OAA data source. These steps and API calls can be adapted for your client or programming language of choice. You can also use the oaaclient
Python module to handle Veza authentication, register the data source, and push the populated template.
While registering sources and pushing authorization metadata with Open Authorization API is relatively straightforward, it is important to understand how Veza organizes custom providers and data sources as endpoints:
You will first register a new custom application provider with a POST request to /api/providers/custom
(specifying the app name and template).
The template determines the application type of the provider's custom data sources (identity_provider
or application
).
Each custom provider can have one or more data sources (such as different instances or domains), generated using Create Datasource.
The populated template can describe additional resources and sub-resources, such as individual databases, repositories, or views.
You can push authorization metadata for each custom data source.
All custom data sources are shown on the Configuration > Apps & Data Sources menu, and can be retrieved using List Custom Provider Datasources.
You should typically name the provider based on the generic application provider (such as GitHub
) and the data source after the top-level instance (such as GitHub - Organization
). See the Core Concepts for more information about parsing and identifying entities using metadata from the source application.
Your requests will need to include a Veza API Key. For OAA APIs, using a Team API key is recommended. Provide it as the bearer auth token in the header of each request, for example:
curl -X GET "https://$VEZA_URL/api/v1/providers/custom" \
-H "authorization: Bearer $API_KEY"
Follow best practices for managing API keys: Do not save credentials in connector code. Consider using a secrets manager to securely store API keys and make them available at run time.
To add a custom application, you will need to:
Create a new custom provider and data source.
push the entity and authorization data in a JSON payload.
Use Create Custom Provider to register a new top-level custom provider:
curl -X POST "https://$VEZA_URL/api/v1/providers/custom" \
-d '{"name":"DemoApp","custom_template":"application"}'
The response will return the Provider's ID, which you will need to create and manage the data sources:
{
"value": {
"id": "a6ef8d8d-d17b-4491-a67a-635ad70f1ba9",
"name": "DemoApp",
"custom_template": "application",
"state": "ENABLED",
"application_types": [],
"resource_types": [],
"idp_types": [],
"schema_definition_json": "e30="
}
}
Name the provider generically after the application or SaaS provider. Use the same Provider for all data sources for that application.
If you are creating providers dynamically, your OAA integration should check if the provider and data source exists before creating a new one.
Each provider needs at least one data source. Create one with Create Custom Provider Datasource
curl -X POST "https://${VEZA_URL}/api/v1/providers/custom/${PROVIDER_ID}/datasources" \
-H 'accept: application/json' \
-H 'authorization: Bearer '${API_KEY} \
-d '{"id": "<PROVIDER ID>", "name":"DemoApp - Documentation Datasource"}'
The response will include the data source ID:
{
"value": {
"id": "1bd31da0-64ee-4dfe-82c9-cb9f0f2fc369",
"name": "DemoApp - Documentation Datasource"
}
}
Datasources should be unique to the data collected by an OAA integration instance. For example, if an application has a "prod" and "dev" instance, creating a datasource for each will enable independent updates to each environment.
Name the data source uniquely based on the application instance to discover. Try to include the hostname or organization name in the data source. For example, don't use "GitHub" use "GitHub - Acme Inc" or "Portal - prod.corp.com"
Note that a provider id
is required in both the request path and body.
Once the data source and provider are active, publish the payload with Push Custom Provider Datasource. The request body must include the Provider ID and Data Source ID.
json_data
must contain the populated OAA template as a single JSON string (escaping any unsafe characters such as "
).
{
"id": "532f6fe3-189f-4576-afdf-8913088961e4",
"compression_type": "none",
"data_source_id": "b6a32af6-b854-47e1-8325-e5984f78bb4d",
"json_data": "{\"name\":\"CustomApp\",\"application_type\"...}"
}
curl -X POST "https://$VEZA_URL/api/v1/providers/custom/$PROVIDER_ID/datasources/$DATASOURCE_ID:push" \
-H 'accept: application/json' \
-H 'authorization: Bearer '$API_KEY \
--compressed --data-binary @body.json
curl -X GET "https://$VEZA_URL/api/v1/providers/custom" \
-H 'accept: application/json' \
-H 'authorization: Bearer '$API_KEY
curl -X GET "https://$VEZA_URL/api/v1/providers/custom/$PROVIDER_ID" \
-H 'accept: application/json' \
-H 'authorization: Bearer '$API_KEY
Specifying the Provider ID and Data Source ID, perform the same post operation used for the initial push.
curl -X POST "https://$VEZA_URL/api/v1/providers/custom/$PROVIDER_ID/datasources/$DATASOURCE_ID:push" \
-H 'accept: application/json' \
-H 'authorization: Bearer '$API_KEY \
--compressed --data-binary @payload.json
To update an existing data source, use the operations List Provider and List Datasources operations to get the provider and data source IDs.
OAA Template for Human Resources Information Systems
Use this Open Authorization API template to publish employee metadata for Human Resources Information Systems (HRIS) platforms, typically used by organizations as a single source of truth for employee information.
Unlike an Identity Provider, HR platforms typically do not provide access to other systems. Employee profiles within an HRIS platform are instead used to store important details such as employment status, who individuals report to, department, and country. Veza can use this metadata to:
Trigger Lifecycle Management events when there is a change in the integrated HRIS data source.
Correlate employees in the HRIS system with identities in your identity provider (IdP).
Enrich Access Reviews with details about linked HRIS employees for users under review.
The template supports:
A top-level System entity representing the HRIS tenant, organization, or account.
Employee entities representing current and inactive workers
Group entities representing teams, departments, cost centers, or other units to which users are assigned.
To enable this payload format, specify the hris
custom template when creating an OAA provider with the API.
{
"System": {
"URL": "https://examplehris.com"
},
"Employees": [
{
"Employee Number": "123456",
"Company": "Example Corp",
"First Name": "John",
"Last Name": "Doe",
"Preferred Name": "Johnny",
"Display Full Name": "Johnny Doe",
"Canonical Name": "John Doe",
"Username": "john.doe",
"Email": "[email protected]",
"IDP ID": "1234-5678-9012",
"Personal Email": "[email protected]",
"Home Location": "City A",
"Work Location": "City B",
"Cost Center": "001",
"Department": "002",
"Managers": ["987654"],
"Groups": ["team-1", "dept-1"],
"Employment Status": "ACTIVE",
"Is Active": true,
"Start Date": "2021-05-01T00:00:00Z",
"Termination Date": null,
"Job Title": "Software Engineer",
"Employment Types": ["FULL_TIME"],
"Primary Time Zone": "America/New_York"
}
],
"Groups": [
{
"Group Type": "TEAM",
"Parent": "dept-1"
}
]
}
The HRIS template supports custom properties. After specifying a custom property definition in the payload, you can assign additional attributes to entities. These enable attribute filters for searches and access reviews in Veza, and enrich results with entity metadata unique to the source system or your organization.
{
"name": "BambooHR",
"hris_type": "BambooHR",
"custom_property_definition": {
"employee_properties": {
"division": "STRING",
"office_extension": "STRING"
},
"group_properties": {
"headquarters_location": "STRING"
}
},
"system": {
"name": "BambooHR",
"id": "BambooHR",
"url": "https://vezai.bamboohr.com",
"idp_providers": ["okta"]
},
"employees": [
{
"name": "Charlotte Abbott",
"id": "1",
"custom_properties": {
"division": "North America",
"office_extension": "1234"
},
"employee_number": "1",
"email": "[email protected]",
"work_location": "Lindon, Utah",
"job_title": "Sr. HR Administrator"
},
{
"name": "Cheryl Barnet",
"id": "10",
"custom_properties": {
"division": "North America",
"office_extension": "5678"
},
"employee_number": "10",
"email": "[email protected]",
"work_location": "Lindon, Utah",
"job_title": "VP of Customer Success"
}
],
"groups": [
{
"name": "North America-Human Resources",
"id": "North America-Human Resources",
"group_type": "Department",
"custom_properties": {
"headquarters_location": "Lindon, Utah"
}
}
]
}
Veza maps HRIS employees to identities from integrated Identity Providers (IdPs) such as Okta by matching the idp_id
, email
, or id
value in the HRIS payload with the IdP entity's Name, Principal Name, or Identity. The matching process checks these fields in the following sequence:
idp_id
email
id
If the idp_id
is unset, Veza uses the email
field for matching. If the email
field is also absent, the id
is used. Veza issues a warning if no matching entity is found.
The account/tenant/etc. that contains the HR information.
URL
String
Y
N
The url for this HRIS system.
Used to represent any person who has been employed by a company.
Employee Number
String
Y
Y
The employee's number that appears in the third-party integration.
Company
String
N
N
The company (or subsidiary) the employee works for.
First Name
String
Y
N
The employee's first name
Last Name
String
Y
N
The employee's last name
Preferred Name
String
N
N
The employee's preferred first name.
Display Full Name
String
N
N
The employee's full name, to use for display purposes. If a preferred first name is available, the full name will include the preferred first name.
Canonical Name
String
N
N
The employee's canonical name.
Username
String
N
N
The employee's username that appears in the integration UI.
String
N
Y
The employee's work email.
IDP ID
String
N
N
The ID for this employee on the destination IDP provider used to automatically connect to it, if not supplied email is used
Personal Email
String
N
N
The employee's personal email.
Home Location
String
N
N
The employee's home location.
Work Location
String
N
N
The employee's work location.
Cost Center
String
N
N
The cost center ID (Group ID) that the employee is in.
Department
String
N
N
The department ID (Group ID) that the employee is in.
Managers
STRINGLIST
N
N
The employee IDs of the employee's managers.
Groups
STRINGLIST
N
N
The IDs of groups this user is in
Employment Status
String
Y
N
The employment status of the employee. Possible values include - ACTIVE
, PENDING
, INACTIVE
.
Is Active
BOOLEAN
Y
N
If the employee is active or not.
Start Date
TIMESTAMP
N
N
The date that the employee started working. If an employee was rehired, the most recent start date will be returned.
Termination Date
TIMESTAMP
N
N
The employee's termination date.
Job Title
String
N
N
The title of the employee.
Employment Types
STRINGLIST
N
N
The employee's type of employment. Possible values include - FULL_TIME
, PART_TIME
, INTERN
, CONTRACTOR
, FREELANCE
.
Primary Time Zone
String
N
N
The time zone which the employee primarily lives.
Used to represent any subset of employees, such as PayGroup or Team. Employees can be in multiple Groups.
Group Type
String
Y
N
The type of group, possible values include - TEAM, DEPARTMENT, COST_CENTER, BUSINESS_UNIT, GROUP. This is intended as to not have each type as their own nodes.
Parent
String
N
N
The group ID of its parent group.
Template for pushing IdP domain, user, and group metadata
Use this template to model authorization metadata for custom identity providers using the .
This document includes an example template and notes for designing and a model of your IdP.
A can define additional properties, used to add supplemental metadata to entities in the payload.
Veza will handle federated identities just as those in supported IdPs such as Okta or Entra ID, enabling search and access review for OAA entities alongside the rest of your data catalog.
The metadata payload describes the Identity Provider domain, users, and groups to add to the Veza authorization graph:
For cases where federated IdP entities are granted AWS permissions via IAM roles, the template supports defining assumable roles per-user. Binding a custom IdP user or group to an AWS role or group ARN enables Veza to parse and display the resource-level actions permitted within AWS.
Custom IdP users and groups can be assigned permissions in other OAA applications by setting the principal type
to idp
in identity_to_permissions
in the payload.
For use cases where a custom IdP is federated with another identity provider user identities can be linked between the two. Authorizations granted to the user will also be granted the source identity. The link is created by providing the unique identity ID and provider type as part of the user entry.
For provider_type
the following values are accepted:
New in Veza release
2022.2.1
To assign an IdP user or group as the manager of any resource Veza has discovered, list the node type and node id in the entities_owned
field, for example:
When parsing the payload, resources in the data catalog will be updated with a SYSTEM_resource_managers
tag to enable entitlement reviews. The owner(s) will be suggested as reviewers for Veza Workflows that target an individual named resource with the correct tag.
Users and groups can be mapped to the identity
of another user they report to. When configured, the manager will be suggested as a review for Workflow certifications where the assigned reporter is the single query target "named entity."
are the recommended method for adding additional metadata to custom identities and resources.
Additionally, can be applied to the IdP domain, users, and groups:
Use incremental updates to remove tags: Resubmitting a payload with different tags will apply any new tags, but not remove existing ones. To remove a tag already applied to an entity, you will need to use the remove_tag
operation.
After the initial metadata push (which must contain the full payload), you can modify, add, or remove the domain, users, and groups without resubmitting other entities. An is enabled by setting "incremental_change": true
in the json_data
push payload, and specifying the update operation
for each entity to change.
Custom Identity Provider
definitionThe identity provider object models one instance of the custom IdP:
IdP Domain
One domain is supported for each custom IdP. Users and groups are mapped to the IdP domain, and connected in Veza Search:
IdP Users
Each IdP user object contains the display name, login email, and identity, along with other identity-related properties:
IdP Groups
Add a group by name
in the groups
section of the template to enable mapping IdP users
to those groups:
IdP entities can be granted permissions on custom applications in the
identity_to_permissions
section of the .
IdP Apps
Use the apps
section to define any applications used to manage access within the identity provider. Apps can be associated with users and groups to model application assignments across your organization.
Users and Groups can be assigned to an application by setting the app_assignments
in the user or group.
The steps to add a custom IdP are the same as for any other OAA provider: you will need to register the new provider and data source, and then push the domain, user, and group descriptions in a JSON payload.
To create a new custom provider using the identity_provider
template, POST the name and template type to /providers/custom:
The response will return the custom IdP ID, which you will need when pushing the metadata payload:
Note that the provider id is required in both the path and body of the request. The response will include the new data source ID.
The payload file must contain the provider and data source ID, and the authorization metadata as a single string, for example:
Identity Mapping Configuration
The identity_mapping_configuration
parameter defines rules for connecting users in a custom IdP to users from other data sources in the Veza graph.
This is useful when:
The connected data source does not natively support returning information about external identities
A correlation between IdP identities and local users can be assumed based on values like username
, email
, or another property value.
The identity_mapping_configuration
is a top-level property of the Custom IDP submission, and is optional. The mapping configuration can include multiple mappings to connect IDP users to users from different data source types,
each based on its own mappings.
You can update just the identity mappings without resubmitting the entire provider payload by using an incremental update. This is useful when you need to modify only the mapping configuration while leaving other provider data unchanged.
Example curl command to apply the incremental identity mapping update:
Supported transformations:
IGNORE_SPECIAL
: Ignore special characters (_
, -
, .
) when matching identities
IGNORE_DOMAIN
: Match identities after removing domain portions (e.g., "@example.com")
{
"name": "Custom User",
"assumed_role_arns": {
"identity": [
"arn:aws:iam::123456789012:role/S3Access"
]
},
}
{
"name": "Custom User",
"identity": "00001",
"source_identity": {
"identity": "[email protected]",
"provider_type": "okta"
}
}
Provider
provider_type
string
Active Directory
active_directory
Any
any
AzureAD
azure_ad
OAA
custom
Google Workspace
google_workspace
Okta
okta
One Login
one_login
{
"name": "Custom User",
"identity": "000011",
"entities_owned": [
{
"node_type": "S3Bucket",
"id": "arn:aws:s3:::amazon-connect-53f87966654d"
}
]
}
{
"name": "Custom User",
"identity": "000013",
"manager_id": "000011"
}
"tags": [
{
"key": "Tag1key",
"value": "optional_Tag1Val"
}
]
name
string
Name to associate with the provider in Veza.
custom_property_definition
Defines the key and types for properties that can be applied to other objects in the push payload
idp_type
string
Type descriptor for IdP, can be unique or share across multiple IdP (for example ldap
, IPA
)
idp_description
string
Any notes to add as entity details (optional)
domains
Domain model
users
Dictionary of CustomIdPUser class instances
groups
Dictionary of CustomIdPGroup class instances
incremental_change
boolean
When true
, enables incremental update operations (optional).
identity_mapping_configuration
Configuration for mapping identities between IdP User and other User types from external data sources
name
string
IdP Domain name
custom_properties
Each element of the push payload can have property_values
, validated against the custom_property_definition
.
dynamic_properties
Dynamic Properties
Up to 5 attributes to apply to the domain (deprecated, use custom properties instead)
tags
Veza Tags list
Any tags to create and apply to the domain.
operation
enum
For incremental updates, the operation to use.
{
"name": "willis",
"email": "[email protected]",
"identity": "000001",
"full_name": "Charles Willis",
"department": "Sales",
"is_active": true,
"is_guest": false,
"groups": [
{
"identity": "everyone"
}
],
"assumed_role_arns": {
"identity": [
"arn:aws:iam::123456789012:role/S3Access"
]
},
"source_identity": {
"identity": "[email protected]",
"provider_type": "okta"
},
"tags": [],
"custom_properties": {},
"manager_id": "string",
"entities_owned": {
"node_type": "S3Bucket",
"id": "arn:aws:s3:::amazon-connect-53f87966654d"
}
}
name
string
Primary ID for user
email
string
Optional email for user
identity
string
Optional unique identifier for user
groups
string list
Assign groups memberships by group identity
(optional)
full_name
string
Full name to display in Veza
department
string list
Any departments to apply as a searchable property (optional).
is_active
boolean
If available, will be applied to the entity as a searchable property (optional).
is_guest
boolean
If available, will be applied to the entity as a searchable property (optional).
assumed_role_arns
array
AWS IAM roles that can be assumed by the IdP user, in the format {"identity": ["arn:aws:iam::123456789012:role/S3Access"]}
(optional).
tags
Veza Tags list
Any tags to create and apply to the user.
dynamic_properties
Dynamic Properties
Up to 5 attributes to apply to the user (deprecated, use custom properties instead)
custom_properties
Each element of the push payload can have property_values
, validated against the custom_property_definition
.
manager_id
string
If the same as another user's identity
, that user will be recommended for governance reviews. Entity details for the user will be updated on push to include the manager as a searchable property.
entities_owned
Entities Owned array
If another resource is specified by entity type and entity id, a Veza tag will be created on the resource to indicate the owner.
operation
enum
For incremental updates, the operation to use (optional).
source_identity
Optionally link IdP user to user from another IdP for federation use cases.
"groups": [
{
"name": "developers",
"identity": "developers",
"full_name": null,
"is_security_group": null,
"assumed_role_arns": {
"identity": ["arn:aws:iam::123456789012:role/S3Access"]
},
"tags": [],
"groups": [
{ "group_1_identity": "parent" },
{ "group_2_identity": "parent" }
],
"custom_properties": {}
}
]
name
string
IdP group name.
identity
string
Unique ID used for user-group assignments.
full_name
string
Optional display name for group
groups
string list
other custom IdP groups this group is a member of
is_security_group
boolean
Sets the is security group
searchable property for the entity in Veza (optional).
tags
Veza Tags list
Any tags to create and apply to the group.
custom_properties
Each element of the push payload can have property_values
, validated against the custom_property_definition
.
dynamic_properties
Dynamic Properties
Up to 5 attributes to apply to the domain. (deprecated, use custom properties instead)
operation
enum
For incremental updates, the operation to use (optional).
assumed_role_arns
array
AWS IAM roles the group can assume, in the format {"identity": ["arn:aws:iam::123456789012:role/S3Access"]}
(optional).
"apps": [
{
"id": "app1",
"name": "Application 1",
"description": "This is a sample application",
"assumed_role_arns": [
{
"identity": "arn:aws:iam::1234567890:role/DevAppRole"
}
],
"custom_properties": {
"owner_org": "engineering"
},
"tags": []
}
]
id
string
App unique identifier.
name
string
IdP app name.
description
string
Description for the App (optional).
assumed_role_arns
array
AWS IAM roles the app can assume, in the format {"identity": ["arn:aws:iam::123456789012:role/S3Access"]}
(optional).
custom_properties
Each element of the payload can have property_values
, validated against the custom_property_definition
.
tags
Veza Tags list
Any tags to create and apply to the group.
operation
enum
For incremental updates, the operation to use (optional).
{
"name": "willis",
"email": "[email protected]",
"identity": "cwilliams",
"groups": [
{
"identity": "everyone"
}
],
"custom_properties": {
"region": "NorthAmerica",
"is_contractor": true
},
"app_assignments": [
{
"id": "assignment1",
"name": "Assignment",
"app_id": "app1",
"custom_properties": {
"assigned_on": "2024-12-05T12:42:25+00:00"
}
}
]
}
id
string
Assignment unique identifier.
name
string
Display name for the assignment.
app_id
string
Unique ID of the App to assign the identity to.
custom_properties
Each element of the payload can have property_values
, validated against the custom_property_definition
.
curl -X POST 'https://<veza_url>/api/v1/providers/custom' \
-H 'authorization: Bearer '<access_token> \
--data-binary '{"name":"SimpleIdP","custom_template":"identity_provider"}'
{
"value": {
"id": "532f6fe3-189f-4576-afdf-8913088961e4",
"name": "Simple IdP",
"custom_template": "identity_provider",
"state": "ENABLED",
"application_types": [],
"resource_types": [],
"idp_types": []
}
}
curl -X POST 'https://<veza_url>/api/v1/providers/custom/532f6fe3-189f-4576-afdf-8913088961e4/datasources' \
-H 'authorization: Bearer '<access_token> \
--data-binary '{"id":"532f6fe3-189f-4576-afdf-8913088961e4", "name":"SimpleDataSource"}'
{"value":{"id":"b6a32af6-b854-47e1-8325-e5984f78bb4d","name":"SimpleDataSource"}}
curl -X POST 'https://<veza_url>/api/v1/providers/custom/532f6fe3-189f-4576-afdf-8913088961e4/datasources/b6a32af6-b854-47e1-8325-e5984f78bb4d:push' \
-H 'authorization: Bearer '<access_token> \
--compressed --data-binary @payload.json
{
"id": "532f6fe3-189f-4576-afdf-8913088961e4",
"data_source_id": "b6a32af6-b854-47e1-8325-e5984f78bb4d",
"json_data": "{\n\"name\":\"CustomIdentityProvider\",\n\"idp_type\": ... "
}
{
"identity_mapping_configuration": {
"mappings": [
{
"destination_datasource_type": "OKTA",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "EMAIL"
}
],
"transformations": [
"IGNORE_SPECIAL"
]
},
{
"destination_datasource_type": "AZURE_AD",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "EMAIL"
}
],
"transformations": [
"IGNORE_DOMAIN"
]
},
{
"destination_datasource_type": "GITHUB_USERS",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "UNIQUE_ID"
}
]
}
]
}
}
{
"incremental_change": true,
"identity_mapping_configuration": {
"operation": "modify",
"mappings": [
{
"destination_datasource_type": "GITHUB_USERS",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "UNIQUE_ID"
}
]
},
{
"destination_datasource_type": "SQL_SERVER",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "EMAIL"
}
],
"transformations": [
"IGNORE_DOMAIN"
]
}
]
}
}
curl --location 'https://<veza_url>/api/v1/providers/custom/816d6e51-6d6a-4279-ba41-2e7c732be880/datasources/716026b5-4b84-4b2f-a805-b41a6ec69cf3:push' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <API_KEY>' \
--data '{
"id": "816d6e51-6d6a-4279-ba41-2e7c732be880",
"data_source_id": "716026b5-4b84-4b2f-a805-b41a6ec69cf3",
"json_data": "{\"incremental_change\":true,\"identity_mapping_configuration\":{\"operation\":\"modify\",\"mappings\":[{\"destination_datasource_type\":\"GITHUB_USERS\",\"property_matchers\":[{\"source_property\":\"EMAIL\",\"destination_property\":\"UNIQUE_ID\"}]},{\"destination_datasource_type\":\"SQL_SERVER\",\"property_matchers\":[{\"source_property\":\"EMAIL\",\"destination_property\":\"EMAIL\"}],\"transformations\":[\"IGNORE_DOMAIN\"]}]}}"
}'
mappings
IdentityMappingSubmission
List of mappings to create between IDP Users and external data sources
operation
enum
For incremental updates, the operation to use.
destination_datasource_type
string
Veza Type for the destination data source, GITHUB_USERS
, SQL_SERVER
, CUSTOM_APPLICATION
destination_datasource_oaa_app_type
string
Optional specifically for mapping to OAA Custom Application to provide a specific App Type
property_matchers
IdentityMappingPropertyMatchersSubmission
List of properties to match on
transformations
list enum
Optional transformations to perform on the property values, available values: ignore_special
, ignore_domain
source_property
enum
IDP User property to match on, unique_id
, email
, property
or custom_property
destination_property
enum
Destination User property to match on, unique_id
, email
, property
or custom_property
custom_source_property
string
When using property
or custom_propert
the property name to match on
custom_destination_property
string
When using property
or custom_propert
the property name to match on
{
"name": "My IdP",
"idp_type": "custom_idp",
"domains": [
{
"name": "example.com",
"tags": [],
}
],
"users": [
{
"name": "m_richardson",
"email": "[email protected]",
"identity": "m_richardson",
"full_name": "Michelle Richardson",
"department": null,
"is_active": true,
"is_guest": false,
"groups": [
{
"identity": "everyone"
},
{
"identity": "developers"
}
],
"assumed_role_arns": [
{
"identity": "arn:aws:iam::123456789012:role/role001"
},
{
"identity": "arn:aws:iam::123456789012:role/role002"
}
],
"tags": [],
},
{
"name": "evargas",
"email": "[email protected]",
"identity": "evargas",
"full_name": "Elizabeth Vargas",
"department": null,
"is_active": true,
"is_guest": false,
"groups": [
{
"identity": "everyone"
},
{
"identity": "developers"
},
{
"identity": "sec-ops"
}
],
"assumed_role_arns": [],
"tags": [],
},
{
"name": "willis",
"email": "[email protected]",
"identity": "c_williams",
"full_name": null,
"department": null,
"is_active": true,
"is_guest": false,
"groups": [
{
"identity": "everyone"
}
],
"assumed_role_arns": [],
"tags": []
}
],
"groups": [
{
"name": "developers",
"identity": "developers",
"full_name": null,
"is_security_group": null,
"tags": []
},
{
"name": "sec-ops",
"identity": "sec-ops",
"full_name": null,
"is_security_group": null,
"tags": []
},
{
"name": "everyone",
"identity": "everyone",
"full_name": "All Company Employees",
"is_security_group": null,
"tags": []
}
],
"identity_mapping_configuration": {
"mappings": [
{
"destination_datasource_type": "GITHUB_USERS",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "UNIQUE_ID"
}
]
},
{
"destination_datasource_type": "SQL_SERVER",
"property_matchers": [
{
"source_property": "EMAIL",
"destination_property": "EMAIL"
}
],
"transformations": [
"IGNORE_DOMAIN"
]
}
]
}
}
This document provides a high-level overview of and examples for getting started with a new OAA connector to integrate Veza with SaaS applications, infrastructure systems, custom-built applications, and other systems. These examples use C# and the Veza.OAA
SDK.
When developing a connector, source system specifics and individual customer requirements will require alterations to code flow. However, the overall goals, best practices, and development flow are common to most integrations.
The example code was written with the following goals in mind:
Connector should be easy to run from automation platforms and the Command Prompt.
Parameters are passed through environment variables as well as command line flags.
Connector does not require maintenance of state:
Connector does not require any persistent data between invocations.
There is no special invocation for the first execution.
The connector handles all provider and data source management logic.
Data source name is unique to the discovered environment.
The exact flow of an OAA connector can change to meet specific requirements, but the general steps are as follows:
Process and validate configuration parameters. Ensure that all required values are present and valid.
Initialize the API client connection to the Veza tenant. Doing so early in the application flow validates the URL and API key before continuing discovery.
Create an instance of the Veza.OAA.Application.CustomApplication
class to populate with application metadata.
Connect to the system and perform discovery of required entities.
In your custom integrations, discovery order for users, groups, roles, and other entities can adapt to suit application requirements.
Populate the CustomApplication
instance with the identity, role, permission, resource, and authorization information collected.
Check if the Provider and Data Source exist on Veza. Create them if they do not exist.
Push the application to the Data Source on Veza. The SDK creates the required JSON payload from the CustomApplication
instance.
Process any returned warnings or errors.
Exit.
To use this example as a starting point for your application integration, follow these steps:
Update the name
, applicationType
, and description
of the CustomApplication
object based on the source system for integration with Veza.
Define any custom_properties
needed. Properties must be defined on the CustomApplication
object before their values are set on any entities.
Implement the discovery steps in the Discover()
function to collect user, group, role, resource, and permission data for the application. As entities are collected, add them to the CustomApplication
object.
Run the connector to validate the output in Veza.
The following code provides a template and examples for creating a new application integration using the Veza.OAA
SDK.
using RestSharp;
using RestSharp.Authenticators;
using Veza.OAA;
using Veza.OAA.Application;
using Veza.OAA.Client;
using NLog;
namespace Example.Integrations
{
/// <summary>
/// Example application group object
/// </summary>
public class AppGroup {
public string Name { get; set; }
public string Type { get; set; }
public string CreatedAt { get; set; }
public List<string> Users { get; set; }
}
/// <summary>
/// Example application resource object
/// </summary>
public class AppResource {
public string Name { get; set; }
public int SiteId { get; set; }
public string ResourceType { get; set; }
}
/// <summary>
/// Example application role object
/// </summary>
public class AppRole {
public string Name { get; set; }
public int SystemId { get; set; }
public string CreatedAt { get; set; }
public List<string> Users { get; set; }
public List<string> Resources { get; set; }
}
public class ExampleApp
{
// Optional NLog logger; can be replaced with any standard logging interface
private static Logger logger = LogManager.GetCurrentClassLogger();
// The base64-encoded logo for the application (displayed on the Veza UI)
public string LogoBase64;
// The name of the provider for the application
public string ProviderName;
// The custom application object for the example application
// This object is used to build the application payload for Veza
public CustomApplication CustomApp { get; set; }
// The example application client for the example application API (not shown in this example)
private ExampleAppClient AppClient { get; set; }
# region "Construction"
public ExampleApp()
{
// Instantiate the example application
CustomApp = new CustomApplication(
name: "Example Application 1",
applicationType: "Example App",
description: "Example application for Veza integration"
);
// Define an "uncategorized" permission for the example application
// This can be used to group permissions that do not fit into another category,
// or if the application API does not provide permission information
CustomApp.AddCustomPermission(
name: "Uncategorized",
permissions: new List<Permission> { Permission.Uncategorized }
);
// define custom properties for the example application
DefineCustomProperties();
// Set the application icon for the Veza UI
// This should be a base64-encoded SVG or PNG image
LogoBase64 = "abc123...def987654"
}
/// <summary>
/// Define custom properties for the example application.
/// This method is called by the constructor to define any custom properties
/// that the example application entities may have.
/// </summary>
private void DefineCustomProperties()
{
CustomApp.DefinedProperties[typeof(User)].
DefineProperty("locked_out", typeof(bool);
CustomApp.DefinedProperties[typeof(Group)].
DefineProperty("type", typeof(string));
CustomApp.DefinedProperties[typeof(Role)].
DefineProperty("system_id", typeof(int));
CustomApp.DefineResourceProperty("site_id", typeof(int)), "widget");
}
# endregion
# region "Discovery"
// Individual methods for discovering users, groups, roles, and resources.
// Should be scoped as private to ensure that they are called only by the Discover method
// and not directly by the client application.
/// <summary>
/// Discover users for the example application.
/// This method is called by the Discover method to discover users for the example application.
/// </summary>
private void DiscoverUsers()
{
Logger.Info("Discovering users for the example application");
// Get users from the example application client.
// In this example, the GetUsers method returns a list of dictionaries of user data.
List<Dictionary<string, string>> users = AppClient.GetUsers();
// Iterate over the users response and add them to the application payload
foreach (Dictionary<string, string> u in users)
{
// Create a new user object and add it to the application payload
User user = CustomApp.AddUser(name: u["username"]);
user.AddIdentity(u["email"]);
user.IsActive = u["active"];
user.CreatedAt = u["created_at"];
}
Logger.Info("User discovery complete");
}
/// <summary>
/// Discover groups for the example application.
/// This method is called by the Discover method to discover groups for the example application.
/// </summary>
private void DiscoverGroups()
{
Logger.Info("Discovering groups for the example application");
// Get groups from the example application client
// in this example, the GetGroups method returns a list of AppGroup objects
List<AppGroup> groups = AppClient.GetGroups();
// Iterate over the groups response and add them to the application payload
foreach (AppGroup g in groups)
{
// Create a new group object and add it to the application payload
Group group = CustomApp.AddGroup(name: g.Name);
group.CreatedAt = g.CreatedAt;
group.SetProperty(name: "type", value: g.Type);
// Iterate users in the group and add them to the group
foreach (string user in g.Users)
{
CustomApp.Users[user].AddGroup(g.Name);
}
}
Logger.Info("Group discovery complete");
}
/// <summary>
/// Discover resources for the example application.
/// This method is called by the Discover method to discover resources for the example application.
/// </summary>
private void DiscoverResources()
{
Logger.Info("Discovering resources for the example application");
// Get resources from the example application
// In this example, the GetResources method returns a list of AppResource objects
List<AppResource> resources = AppClient.GetResources();
foreach (AppResource r in resources)
{
// Create a resource object and add it to the application payload
Resource resource = CustomApp.AddResource(name: r.Name, resourceType: r.ResourceType);
// Set the `site_id` property for the resource if the resource is a "widget"
// Cstom resource properties are defined per-resource type, so non-"widget" resources will not have this property
if (r.ResourceType == "widget")
{
resource.SetProperty(name: "site_id", value: r.SiteId);
}
}
Logger.Info("Resource discovery complete");
}
/// <summary>
/// Discover roles for the example application.
/// This method is called by the Discover method to discover roles for the example application.
/// </summary>
private void DiscoverRoles()
{
Logger.Info("Discovering roles for the example application");
// get roles from the example application
// in this example, the GetRoles method returns a list of AppRole objects
List<AppRole> roles = AppClient.GetRoles();
foreach (Role role in roles)
{
// create a new role object and add it to the application payload
Role role = CustomApp.AddRole(name: role.Name);
role.CreatedAt = role.CreatedAt;
role.SetProperty(name: "system_id", value: role.SystemId);
// iterate users in the role and add them to the role
// set the applyToApplication flag to true to apply the role to the entire application
foreach (string user in role.Users)
{
CustomApp.Users[user].AddRole(name: role.Name, applyToApplication: true);
}
Application.AddRole(role);
}
Logger.Info("Role discovery complete");
}
# endregion
# region "Execution"
/// <summary>
/// Discover the example application.
/// This method is called by the Run method to discover the example application.
/// It serves as a place to instantiate a client for the example application API
/// and then call each of the discovery methods required to build the application payload.
/// </summary>
/// <param name="exampleApiKey">The API key for the example application API</param>
/// <param name="exampleUrl">The URL for the example application API</param>
/// <returns></returns>
public void Discover(string exampleApiKey, string exampleUrl)
{
// instantiate an API client for the example application
AppClient = new ExampleAppClient(exampleApiKey, exampleUrl);
Logger.Info("Beginning example application discovery");
// discover the example application components
DiscoverUsers();
DiscoverGroups();
DiscoverResources();
DiscoverRoles();
Logger.Info("Example application discovery complete");
}
/// <summary>
/// Handle integration execution.
/// This is separated from the Main method to more easily adapt to changes
/// to application execution (e.g. from Command Prompt, as part of another application, etc.)
/// </summary>
/// <param name="exampleApiKey">The API key for the Example application API</param>
/// <param name="exampleUrl">The URL for the Example application API</param>
/// <param name="vezaApiKey">The API key for the Veza tenant</param>
/// <param name="vezaUrl">The URL for the Veza tenant</param>
/// <returns></returns>
public async Task Run(
string exampleApiKey,
string exampleUrl,
string vezaApiKey,
string vezaUrl
)
{
Logger.Info("Starting example application discovery");
// Instantiate a connection to the Veza API.
// The client will attempt to reach the Veza API and throw an exception if it fails.
// This is to ensure that the Veza API is reachable before attempting to discover and push data.
try
{
OAAClient oaaClient = new(vezaApiKey, vezaUrl);
}
catch (ClientException e)
{
Logger.Error("Failed to connect to Veza API: {0}", e.Message);
return;
}
// Discover the example application
Discover(exampleApiKey, exampleUrl);
// Get or create the Veza provider for the payload
Logger.Debug("Checking Veza for an existing provider for the example application");
Sdk.Client.ApiClient.VezaApiResponse providerResponse = await oaaClient.GetProvider(ProviderName);
// Create the provider if none exists
if (providerResponse is null)
{
Logger.Info("No Veza provider exists; creating provider");
await oaaClient.CreateProvider(ProviderName, "application", base64_icon: LogoBase64);
}
// Push OAA payload to Veza
Logger.Info("Pushing example application metadata to Veza");
RestResponse<Sdk.Client.ApiClient.VezaApiResponse> pushResponse = await oaaClient.PushApplication(ProviderName, Environment.MachineName, CustomApp, save_json: true);
if (pushResponse.IsSuccessful)
{
Logger.Info("Example application discovery complete");
} else
{
Logger.Error($"Push response code: {pushResponse.StatusCode}");
Logger.Error($"Push response content: {pushResponse.Content}");
}
}
# endregion
}
}
This document provides a high-level overview and examples for getting started with a new OAA connector to integrate Veza with SaaS applications, infrastructure systems, custom-built applications, and other systems. These examples use Python and the oaaclient
SDK.
When developing a connector, the source systems and customer needs can require changes to code flow for different deployment scenarios. The overall goals, best practices, and flow will apply for most integrations:
The sample code was written with the following goals in mind:
Connector should be easy to run from automation platforms and the command line.
Parameters are passed through environment variables as well as command line flags. This makes the code easier to package into containers or serverless stacks and control through outside configuration. Connectors by nature require secrets such as the Veza API key. Managing these through variables affords additional options for secrets management.
Connectors do not require state:
Connectors do not require any persistent data between runs.
There is no special invocation for the first run or subsequent runs.
The connector handles all of the logic for provider and data source creation internally, as needed, or by discovering existing ones.
Data Source name should be unique to the discovered environment. This can be achieved by including a hostname or instance ID in the data source name: discoverable by the code and consistent between runs. This ensures that two instances of the same connector do not interfere with each other if discovering separate instances of the application. When such a property cannot be determined, a command line parameter is an option.
Connector code is importable:
The flexibility to import connector code into another piece of Python code enables a setup wrapper to handle job management tasks such as:
Secrets management
Output logging
Other configurations required by the environment
A separate run
function implements connector end-to-end logic. This can be imported and invoked by providing the necessary parameters. The main
function that runs when invoked from the command line should only process command line inputs and environment variables for setup.
The exact flow of the connector can change to meet specific requirements, but the general steps are as follows:
Process and validate configuration parameters. Ensure you have supplied all necessary values such as host names, user names, and API keys.
Call the main run
function with all the required and optional parameters.
Initialize the oaaclient
connection to the Veza tenant. Initializing the client verifies that the Veza URL and API key are valid before starting any application discovery.
Create an instance of the oaaclient.templates.CustomApplication
to populate with the application information.
Connect to the system and perform discovery of required entities:
The discovery order for users, groups, roles, and so on can be changed based on relationships and interface.
Populate the CustomApplication
instance with the identity, roles and permissions, resources, and authorization information.
Check if Provider and Data Source exist on Veza. Create them if they do not exist.
Push the application to the Data Source. The SDK creates the JSON payload from the CustomApplication
instance.
Process any returned warnings or errors.
Exit.
Update the Provider Name, App Type, and Application Name based on the source system. These values will typically be set to the product name. The data source name must be unique - check that an appropriate distinguishing value such as hostname is included in the data source name. For more information on naming, see Providers, Data Sources, Names and Types
Define any custom properties needed in the __init__
method. Properties must be defined on the CustomApplication
entities before setting them.
Implement the discovery steps for the discover()
function to collect Users, Groups, Roles, and any resources for the application. As the entities are discovered, add them to the CustomApplication
object using the appropriate SDK operations.
Run the connector to validate the output in Veza.
Automate the connector to run on a regular schedule.
The code below can serve as a template and the comments explain the reasoning beyond the patterns.
#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import logging
import os
import sys
import requests
from requests.exceptions import HTTPError, RequestException
from oaaclient.client import OAAClient, OAAClientError
from oaaclient.templates import CustomApplication, OAAPermission, OAAPropertyType, LocalUser
import oaaclient.utils as oaautils
"""
Logging in connectors is very important. Establishing a local logging function here
allows the connector log even if its imported into another block of code
"""
logging.basicConfig(
format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO)
log = logging.getLogger(__name__)
"""
The main logic for connecting to the destination application.
Authorization data collection and OAA template population should be structured into a class.
The class can store the authentication tokens making connections through an API or SDK easier.
The class should also instantiate the OAA CustomApplication or CustomIdP class
to manage populating all the identity, resource and authorization data.
"""
class OAAConnector():
APP_TYPE="SampleApp"
def __init__(self, auth_token: str) -> None:
self.auth_token = auth_token
# Set the application name and type. The type will generally be the vendor or product.
# The App Name can be the same, or contain additional context like the instance name.
app_name = "My App - West"
self.app = CustomApplication(app_name, application_type=self.APP_TYPE)
# declaring custom properties as part of the `__init__` keeps them together
self.app.property_definitions.define_local_user_property("email", OAAPropertyType.STRING)
self.app.property_definitions.define_local_user_property("has_mfa", OAAPropertyType.BOOLEAN)
"""
A `discover` method that starts the discovery cycle.
The discovery cycle should be invoked separately from the init.
More complex connectors may have additional setup steps between init and discovery
"""
def discover(self) -> None:
"""Discovery method"""
log.info(
"Start App discovery") # Start and stop log messages provide progress as discovery proceeds
self._discover_users()
self._discover_roles()
log.info("Finished App discovery")
return
"""
Smaller functions to perform portions of the discovery like users, groups, roles
should be private `_` functions to imply that they should not be run alone,
unless care is taken to ensure no dependencies.
For example, discovery of roles may assume that users are already discovered.
"""
def _discover_users(self) -> None:
log.info("Start user discovery")
# perform user discovery, for example process each user returned from an API call
for user in self._api_get("/Users"):
local_user = self.app.add_local_user(id=user["id"], name=user["name"])
local_user.is_active = user["active"]
local_user.set_property("has_mfa", user["mfa_enabled"])
log.info("Finished user discovery")
return
def _discover_roles(self) -> None:
log.info("Start role discovery")
# perform user discovery
log.info("Finished role discovery")
return
"""
Any required methods to interface with the application should be defined
as part of the class. Not all connectors need these methods, as they may use
other SDKs to interface with the application.
"""
def _api_get(self, path: str) -> dict:
# implement logic to make API call, process results, handle errors, retries ect.
# Could be done with a vendor SDK, SQL, or any method supported by the application
return {}
"""
A run function that is separate from `main` makes it easy to import the connector
into another piece of Python code. This may be useful to call the connector from
code that retrieves secrets or manages the job in other ways.
All necessary parameters should be taken in through the `run` function
"""
def run(veza_url: str, veza_api_key: str, app_key: str, **config_args) -> None:
# Process any configuration arguments
if config_args.get("debug"):
log.setLevel(logging.DEBUG)
logging.getLogger("urllib3").setLevel(logging.INFO)
log.info("Enabling debug logging")
else:
log.setLevel(logging.INFO)
save_json = config_args.get("save_json", False)
if not isinstance(save_json, bool):
raise TypeError("save_json argument must be boolean")
# Connect to the Veza instance before discovery to validate that the credentials are valid
try:
conn = OAAClient(url = veza_url, api_key = veza_api_key)
except OAAClientError as error:
log.error(f"Unable to connect to Veza {veza_url}")
log.error(error.message)
# run function should raise any exception so that they can be handled by the parent code, never exit
raise error
# Initialize the connector class and run discovery
try:
app = OAAConnector(auth_token=app_key)
app.discover()
except RequestException as e:
# process possible exceptions from the app discovery
log.error("Error during discovery")
log.error(f"{e} - {e.response.status_code} {e.response.text}")
raise e
# After discovery is complete, set up the Provider and Data Source to push the data to
# Provider name should be consistent with the vendor and application
provider_name = "My App"
provider = conn.get_provider(provider_name)
if provider:
log.info("found existing provider")
else:
log.info(f"creating provider {provider_name}")
provider = conn.create_provider(provider_name, "application", base64_icon=APP_SVG_B64)
log.info(f"provider: {provider['name']} ({provider['id']})")
# Data Source name should be unique to the instance of the app that is discovered but consistent.
# For example the hostname of the application or deployment name. Do not use something that will change.
data_source_name = f"App - {app.unique_identifier}"
try:
log.info("uploading application data")
response = conn.push_application(provider_name,
data_source_name = data_source_name,
application_object = app.app,
save_json = save_json
)
# An OAA Push can succeed with warnings, you can log out the warnings
if response.get("warnings", None):
log.warning("Push succeeded with warnings:")
for e in response["warnings"]:
log.warning(e)
log.info("success")
except OAAClientError as error:
# if there is an issue with the OAA payload the error details should contain useful information to help resolve the problem
log.error(f"{error.error}: {error.message} ({error.status_code})")
if hasattr(error, "details"):
for detail in error.details:
log.error(detail)
raise error
return
"""
Setting an application icon helps visually identify the app in the Veza UI.
A Base64 encoding of an SVG or PNG in the app code is an option.
You can also import an icon from a file with `oaaclient.utils.encode_icon_file`.
"""
APP_SVG_B64 = """
PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9InllcyI/Pg0KPCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFRE
IFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4NCjxzdmcgd2lkdGg9IjQwMCIgaGVpZ2h0
PSI0MDAiIHZpZXdCb3g9IjAgMCAyMDAgMjAwIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPg0KICA8cGF0aCBmaWxsPSJyZWQiIHN0cm9r
ZT0iIzAwMCIgc3Ryb2tlLXdpZHRoPSIyLjUiIGQ9Ik0xNjggMTY4SDMyVjMyaDEzNnoiLz4NCjwvc3ZnPg0K
"""
"""
The main entry point should only deal with processing parameters and invoking
the run function. No OAA or application discovery should happen during the main
function. All required parameters should be configurable through the OS
environment. It should be possible to run the connector from the command line
with no arguments.
"""
def main():
"""
process command line and OS environment variables, then call `run`
"""
parser = argparse.ArgumentParser(description = "OAA Connector")
# using the `default=os.getenv()` pattern makes it easier to get parameters from command line or OS environment
parser.add_argument("--veza-url", default=os.getenv("VEZA_URL"), help="the URL of the Veza instance")
parser.add_argument("--debug", action="store_true", help="Set the log level to debug")
parser.add_argument("--save-json", action="store_true", help="Save OAA JSON payload to file")
args = parser.parse_args()
# Secrets should only be passed in through ENV
veza_api_key = os.getenv("VEZA_API_KEY")
veza_url = args.veza_url
save_json = args.save_json
if not veza_api_key:
oaautils.log_arg_error(log, None, "VEZA_API_KEY")
if not veza_url:
oaautils.log_arg_error(log, "--veza-url", "VEZA_URL")
# ensure required variables are provided
if None in [veza_api_key, veza_url]:
log.error(f"missing one or more required parameters")
sys.exit(1)
try:
run( veza_url=veza_url, veza_api_key=veza_api_key, save_json=save_json, debug=args.debug)
except (OAAClientError, RequestException):
log.error("Exiting with error")
sys.exit(1)
if __name__ == "__main__":
# replace the log with the root logger if running as main
log = logging.getLogger()
logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s', level=logging.INFO)
main()
Template for pushing custom data source entities and authorization
The Custom Application Template can be used to model most applications and services. It can describe many common entity types (such as users, groups, and resources), and should be the starting point for most custom connectors.
The template has three primary elements, covered in detail in this document:
Applications - describes one or more application instances for the custom data source. An application may consist of any of the following entities:
Local Users - defines the users of the application. The local user entity can be used to store the properties of the user specific to the application (such as last_login_at
) and can be linked to a source identity like Okta or AzureAD.
Local Groups - defines a group of users, permissions to the application or resources can be assigned to a group.
Local Roles - defines a collection of permissions. A role can be used to link an identity (local user, group, or IdP) to an application or resource. An identity assigned to a role will be assigned all permissions from that role.
Resources - for more fine grain authorization tracking resources can be used to represent components of the application that have their own authorization. Users and groups can be assigned permission or roles to resources.
Sub Resources - resources can additionally have sub-resources for additional levels of depth.
Permissions - define the applications specific permissions and map to Veza canonical permissions.
Identity to Permissions - Assign local and federated users and groups to permissions or roles to the application and resources.
Additionally, a custom property definition may describe user-configured key:pair values that can be applied to entities in the payload.
To use the generic app template, set the template type to application
when creating a new data provider:
curl -X POST "https://{VEZA_URL}/api/v1/providers/custom" \
-H "authorization: Bearer {API_KEY}" \
--compressed --data-binary '{"name":"DemoApp","custom_template":"application"}'
Custom properties and Veza Tags can be applied to most objects in the OAA payload: the application
and its local_users
, local_groups
, local_roles
and resources
/sub_resources
.
Define custom properties
"custom_property_definition": {
"applications": [
{
"application_type": "sample",
"application_properties": {},
"local_user_properties": {
"license_type": "STRING",
"license_expires": "TIMESTAMP"
},
"local_group_properties": {},
"local_role_properties": {},
"resources": []
}
]
}
In the rest of the payload, for each object that should have additional properties, add a custom_properties
array containing the property keys and values:
"local_users": [
{
"name": "bob",
"identities": [
"[email protected]"
],
"groups": null,
"is_active": true,
"created_at": "2022-01-26T20:48:12.460Z",
"last_login_at": null,
"deactivated_at": null,
"password_last_changed_at": null,
"tags": [],
"custom_properties": {
"license_type": "pro",
"license_expires": "2023-01-01T00:00:00.000Z"
}
}
]
Use incremental updates to remove tags: Resubmitting a payload with different tags will apply any new tags, but not remove existing ones. To remove a tag already applied to an entity, you will need to use the incremental update
remove_tag
operation.
Validation and Troubleshooting
The API response will provide information for invalid data submission. You can check Veza events for updates on parsing status. Errors won't typically occur during parsing, as the metadata is validated upon push. To ensure a valid payload, you should:
Confirm all string fields are are valid UTF-8 strings no larger than 256 bytes.
Check that all required fields are present. Tags and properties are optional. You can null empty groups, roles, and other "empty" but required keys.
A 200 OK response may include warnings when matching IDP identities can't be found
The OAA payload must contain at least one top-level application. To model data systems with multiple components (such as different servers or repositories), applications can have resources and sub-resources.
You can also specify more than one application in the OAA payload, each with its own identities, permissions, roles, and resources.
The application_type
is applied to all application resources and identities, and can be used as a filterable property in Veza search.
{
"applications": [
{
"name": "Custom App",
"application_type": "Source Control",
"description": "Has a resource for each repository",
"custom_properties": {},
"tags": [],
"local_users": [],
"local_groups": [],
"local_roles": [],
"resources": []
}
]
}
name
string
Identifies the app in Veza Search. Used to bind permissions to the application
application_type
string
Applied to all entities within the application as a searchable property. Multiple instances of an application can share the same type
description
string
Any additional notes to show in the entity details, limit 256 characters
custom_properties
dictionary
contain property_values
validated against the custom_property_definition
tags
array
Specify tags with a key and optional value (optional)
local_users
array
Contains zero or more local users (see ).
local_groups
array
Contains zero or more (collections of users).
local_roles
array
Defines permissions for any within the application.
resources
array
Contains any and sub-resources.
Optional fields: some values in the schema are optional. When submitting a payload without a required field, an error message will help identify the issue. The following guidelines apply:
Any type of data in a JSON payload can be null (not set).
Unused optional arrays and objects should be empty {}
or []
.
Unused optional strings, numbers, and booleans should be null.
Strings and string lists intended to have constant values (enums) such as identity_type
may have a default value when not set.
OAA apps need to contain at least one identity, which could be a local_group
, local_role
, or an IdP identity. Role assignments are made in the identity_to_permissions
section.
Each application can contain one or more resources that users can access. Resources can have additional searchable properties and may contain additional sub-resources.
Sub-resources describe additional layers of the application principals can have authorization to, and support the same properties as resources, including additionally nested sub-resources.
"resources": [
{
"name": "Entity1",
"id": "Unique ID",
"resource_type": "thing",
"description": "Some entity in the application",
"sub_resources": [
{
"name": "Child 1",
"resource_type": "child",
"description": "My information about resource",
"sub_resources": [],
"custom_properties": {},
"tags": []
}
],
"custom_properties": {},
"tags": []
},
{
"name": "Entity2",
"id": "Another Unique ID",
"resource_type": "thing",
"description": "Another entity in the application",
"sub_resources": [],
"custom_properties": {},
"tags": []
}
]
An application can have any number of nested sub resources.
name
string
Resource name. Primary ID for mapping users to individual resource permissions.
id
string
Optional value to use as the unique ID, instead of the resource name
*.
resource_type
string
Searchable label for the resource type. The application entity details in Veza will show the contained resource types as properties.
description
string
Shown in Veza entity details, max 255 characters.
custom_properties
dictionary
See .
sub_resources
array
Used for additional resource layers, nested data sources, services, and so on.
connections
Optional list of resource connections to external entities discovered by Veza
tags
array
Specify tags with a key and optional value (optional)
* A specific resource type must have only resources with an id
, or only resources without an id
. When used identity_to_permissions
assignments are made by the id
value and name
functions as a display name.
In the system being modeled, application resources and sub-resources (such as virtual machines or Looker views) have access to other entities in the Veza authorization graph.
If an application resource or sub-resource is able to assume the permissions of a local user, IAM role, or Enterprise application, you can specify the connections
to another graph entity node_type
and id
:
{
"name": "cog1",
"resource_type": "cog",
"connections": [
{
"id": "[email protected]",
"node_type": "GoogleCloudServiceAccount"
}
]
}
The following node types are currently available:
SnowflakeUser
GoogleCloudServiceAccount
AwsIamRole
AzureADEnterpriseApplication
TrinoUser
Applications can have local users and groups for identities. For users and groups that correlate to an external Identity Provider (for example accounts automatically provisioned by the IdP), you can map the principal to the IdP entity name or login email in identities
.
Contains any users whose profiles and authentication are handled and stored by the custom application. Local users include their group assignments and any federated identities that should be mapped to the local user:
"local_users": [
{
"name": "Evan Gray",
"unique_id": "egray",
"identities": ["[email protected]"],
"groups": ["contractors"],
"is_active": true,
"created_at": "2020-12-19T16:39:57-08:00",
"last_login_at": "2021-11-19T14:19:30-08:00",
"password_last_changed_at": null,
"deactivated_at": null,
"custom_properties": {},
"tags": []
}
]
name
string
Name of the local user, shown in the Veza UI.
unique_id
string
Optional identifier to use for mapping users to groups, roles, and permissions.
identities
identities array
Maps the user to a federated identity by login email or group name. Use when your IdP provisions local accounts, or if the local user can be assumed by an external group. Must match a discovered Okta, Google Workspace, or Azure AD entity Name, PrincipalName, or Identity.
groups
groups array
list of any memberships as strings. Must exist in local groups.
is_active
boolean
If activity state is available from the provider, use this field to make the value available as a searchable property (optional).
created_at
RFC3339 string
User creation date (optional), for example 1996-12-19T16:39:57-08:00
last_login_at
RFC3339 string
(optional)
password_last_changed_at
RFC3339 string
(optional)
deactivated_at
RFC3339 string
(optional)
custom_properties
dictionary
See .
tags
array
Specify tags with a key and optional value (optional)
"local_groups": [
{
"name": "US Contractors",
"unique_id": "us-contractors",
"identities": ["[email protected]"],
"groups": [
"all-contractors",
"all-workers"
],
"tags": []
}
]
If the application has any groups, describe each one in the local_groups
array.
Group assignments for entities are defined in
identity_to_permissions
.
name
string
Name of the local group. Primary ID for mapping group to permissions.
unique_id
string
Optional identifier to use for permissions mapping
custom_properties
dictionary
See .
identities
array
If IdP users are members of the local group, or if the local group directly maps to an IdP group, list them here.*
groups
array
List of local group this group is a member of (for applications that support adding groups to other groups)
tags
array
Specify tags with a key and optional value (optional)
*Must match a discovered Okta or Azure entity Name, PrincipalName, or Identity
"local_roles": [
{
"name": "administrator",
"unique_id": "0001",
"permissions": ["create","destroy"]
},
{
"name": "operator",
"permissions": ["pull", "read"],
"tags": []
}
]
Local roles define collections of local permissions that can be assigned to multiple resources. In the applications
section, roles are named and mapped to permissions. Role assignments are defined in identity_to_permissions
.
name
string
Name of the local role. Primary ID for mapping role to permissions.
unique_id
string
Optional identifier to use for permissions mapping
permissions
array
Permissions associated with the role. Must exist in permissions
tags
array
Specify tags with a key and optional value (optional)
permissions
"permissions": [
{
"name": "Admin",
"permission_type": [
"DataRead",
"DataWrite",
"MetadataRead",
"MetadataWrite"
]
},
{
"name": "Operator",
"permission_type": [
"MetadataRead",
"DataRead"
]
},
{
"name": "Inactive",
"permission_type": [
"NonData"
]
}
]
Bind local permissions to the corresponding Veza canonical permission(s). Each native application permission should be included as an object, mapped to the corresponding data/non-data actions it allows.
Canonical permission types are:
DataRead
DataWrite
MetadataRead
MetadataWrite
NonData
DataCreate
DataDelete
MetadataCreate
MetadataDelete
Uncategorized
name
string
Native permission name, such as “Push” (used to bind local and IdP identities to native permissions).
permission_type
enum
List of canonical privilege(s) the permission represents.
application_type
enum
Optional list of custom application application_type
the permission applies to.
apply_to_sub_resources
bool
To more accurately model applications where permissions should apply to any children of a resource, set TRUE to define the permission as inheritable. This eliminates the need to include the permission at each sub level.
To better model systems where roles can contain different permissions to different types of resources, permissions
can apply to individual resource_types
.
When the payload is parsed, individual permissions are created for each type of resource the permission applies to.
Without resource_types
specified, the permission will function normally. When directly connecting principals and resources, resource_type
is ignored.
identity_to_permissions
"identity_to_permissions": [
{
"identity": "Evan Gray",
"identity_type": "local_user",
"application_permissions": [
{
"application": "Veza AI",
"resources": ["terraform-dev", "prod"],
"permission": "pull"
},
{
"application": "Veza AI",
"resources": ["terraform-dev", "prod"],
"permission": "push"
}
]
}
]
Contains an object for each local and IdP identity, and the individual permissions to applications and resources.
You can bind permissions to federated users and groups by providing the principal’s IDP login email or group name as the identity
, and setting the identity_type
to idp
.
Permissions and role assignments can apply to the entire application or scoped to specific resources.
For each identity (matching a local user, group, or IdP identity), state the identity type and add the assigned permissions/roles:
identity
string
Principal name or email address. Maps to IdP login email or group name.
identity_type
string
Sets whether the identity corresponds to an IdP identity, or is local to the application
application_permissions
array
List each local permission available to the identity (must be a valid permission name
from the previous section).
role_assignments
array
Any roles assigned to the identity, and the resources they apply to (role/resource must exist in applications
).
Each identity
can be either a local_user
, local_role
, or local_group
name, or the identifier of an IdP user, group, or role (email address or group name).
identity_type
must be one of (idp
(default), local_group
, local_role
, or local_user
.
application_permissions
{
"identity": "[email protected]",
"identity_type": "idp",
"application_permissions":
[
{
"application": "source control",
"resources": ["util-tools", "terraform"],
"apply_to_application": false,
"permission": "write"
},
{
"application": "source control",
"resources": [],
"apply_to_application": true,
"permission": "read"
}
]
}
Binds the identity
(IdP entity, local user, or local group) to local permission, by application
and resources
.
application
string
Maps to an application name
from the first section. Must exist in applications
resources
array
List of application resource or sub-resource name
s to apply the permission. Must exist in applications
apply_to_application
boolean
Set to true
to model environments where permissions apply to the top-level application as well as its resources.
permission
string
Maps to a permission name
from the second section. Must exist in permissions
role_assignments
Local roles are assigned to identities in the role_assignments
array. Roles can apply to the entire application or only to specific (sub) resources
.
{
"identity": "john_smith",
"identity_type": "local_user",
"role_assignments":[
{
"application": "custom application",
"role": "administrator",
"apply_to_application": true,
"resources": []
},
{
"application": "custom application",
"role": "ops",
"apply_to_application": false,
"resources": ["oaa-vm-1"]
}
]
}
application
string
The application where the role applies. Must exist in applications
role
string
The role name. Must exist in local_roles
apply_to_application
boolean
Set to true
to model environments where the role applies to the top-level application and all its resources.
resources
array
List of resources and sub-resources where the role applies. Must exist in applications
"identity_to_permissions": [
{
"identity": "0000000001",
"identity_type": "local_user",
"role_assignments": [
{
"application": "Sample App",
"role": "user",
"apply_to_application": true,
"resources": []
}
]
},
{
"identity": "0000000002",
"identity_type": "local_user",
"role_assignments": [
{
"application": "Sample App",
"role": "user",
"apply_to_application": true,
"resources": []
},
{
"application": "Sample App",
"role": "admin",
"apply_to_application": true,
"resources": []
}
]
}
]
Classes for calling Veza APIs and managing OAA providers and data sources.
Copyright 2022 Veza Technologies Inc.
Use of this source code is governed by the MIT license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
Global Variables
OAACLIENT_VERSION
PROVIDER_ICON_MAX_SIZE
report_builder_entrypoint
report_builder_entrypoint() → None
Entry point for oaaclient-report-builder
command
Reads a JSON file and passes it to the oaaclient.utils.build_report
method
main
main()
OAAClientError
Error raised by OAAClient.
Raised for issues connecting to the OAA API and when the API returns an error.
Args:
error
(str): short string for error message
message
(str): detailed error message
status_code
(int, optional): status code for HTTP related errors. Defaults to None.
details
(list, optional): list of additional details for error. Defaults to None.
__init__
__init__(
error: 'str',
message: 'str',
status_code: 'int' = None,
details: 'list' = None
) → None
OAAResponseError
Error returned from API Call
__init__
__init__(*args, **kwargs)
OAAConnectionError
Error with API Connection
__init__
__init__(
error: 'str',
message: 'str',
status_code: 'int' = None,
details: 'list' = None
) → None
OAAClient
Class for OAA API Connection and Management.
Utilities for OAA-related operations with Veza API calls. Manages custom providers and data sources, and can push OAA payloads from JSON or template objects.
Connection url and API key can be automatically loaded from OS environment values if set. To utilize environment variables initialize OAAClient without providing a URL or API key value and set the VEZA_URL
and VEZA_API_KEY
OS environment variables.
Args:
url
(str, optional): URL for Veza instance.
api_key
(str, optional): Veza API key.
username
(str, optional): Not used (legacy). Defaults to None.
token
(str, optional): Legacy parameter name for API key. Defaults to None.
Attributes:
url
(str): URL of the Veza instance to connect to
api_key
(str): Veza API key
enable_compression
(bool): Enable or disable compression of the OAA payload during push, defaults to enabled (True)
Raises:
OAAClientError
: For errors connecting to API and if API returns errors
__init__
__init__(
url: 'str' = None,
api_key: 'str' = None,
username: 'str' = None,
token: 'str' = None
)
add_query_report
add_query_report(report_id: 'str', query_id: 'str') → dict
Add a Query to a Report
Adds a Query to an existing Report by ID
Args:
report_id
(str): Report UUID to add Query to
query_id
(str): Query UUID to add
Returns:
dict
: API Response
api_delete
api_delete(api_path: 'str', params: 'dict' = None) → dict
Perform REST API DELETE operation.
Args:
api_path
(str): API Path API path relative to Veza URL
Raises:
OAAResponseError
: API returned an error
OAAConnectionError
: Connection error during HTTP operation
Returns:
dict
: API response from call
api_get
api_get(api_path: 'str', params: 'dict' = None) → list | dict
Perform a Veza API GET operation.
Makes the GET API call to the given path and processes the API response. Returns the value
or values
result returned from the API.
For API endpoints that return a list like /api/v1/providers/custom
function will return a list of entities or an empty list if the API returns no results.
For API endpoints that are a specific ID such as /api/v1/providers/custom/<uuid>
function will return the dictionary result of the JSON returned by the API.
Args:
api_path
(str): API path relative to Veza URL (example /api/v1/providers
).
Raises:
OAAResponseError
: API returned an error
OAAConnectionError
: Connection error during HTTP operation
Returns:
list|dict
: Returns list or dict based on API destination
api_patch
api_patch(api_path: 'str', data: 'dict', params: 'dict' = None) → dict
Perform REST API PATCH operation.
Args:
api_path
(str): API Path API path relative to Veza URL
Raises:
OAAResponseError
: API returned an error
OAAConnectionError
: Connection error during HTTP operation
Returns:
dict
: API response from call
api_post
api_post(api_path: 'str', data: 'dict', params: 'dict' = None) → list | dict
Perform a Veza API POST operation.
Call POST on the supplied Veza instance API path, submitting a data payload.
Returns value
or values
response from API result. Paginated responses are automatically processed to collect all responses a single list.
Args:
api_path
(str): API path relative to Veza URL example /api/v1/providers
data
(dict): dictionary object included as JSON in body of POST operation
params
(dict, optional): Optional HTTP query parameters. Defaults to empty dictionary.
Raises:
OAAResponseError
: API returned an error
OAAConnectionError
: Connection error during HTTP operation
Returns:
dict
: API response as dictionary
api_put
api_put(api_path: 'str', data: 'dict', params: 'dict' = None) → list | dict
Perform Veza API PUT operation.
Call PUT on the supplied Veza instance API path, including the data payload.
Returns value
or values
response from API result. Paginated responses are automatically processed to collect all responses a single list.
Args:
api_path
(str): API path relative to Veza URL example /api/v1/providers
data
(dict): dictionary object included as JSON in body of PUT operation
params
(dict, optional): Optional HTTP query parameters. Defaults to empty dictionary.
Raises:
OAAResponseError
: API returned an error
OAAConnectionError
: Connection error during HTTP operation
Returns:
dict
: API response as dictionary
create_data_source
create_data_source(
name: 'str',
provider_id: 'str',
options: 'dict | None' = None
) → dict
Create a new Data Source for the given Provider ID.
Args:
name
(str): Name for new Data Source
provider_id
(str): Unique identifier for the Provider
options
: (dict, optional): Additional arguments to be included with data source create call to Veza. Defaults to None.
Raises:
ValueError
: Data source name contains invalid characters
Returns:
dict
: dictionary of new Data Source
create_datasource
create_datasource(name, provider_id)
Deprecated Legacy function for backward-compatibility.
create_provider
create_provider(
name: 'str',
custom_template: 'str',
base64_icon: 'str' = '',
options: 'dict | None' = None
) → dict
Create a new Provider.
Creates a new Provider with the given name. An error will be raised for naming conflicts.
Args:
name
(str): new Provider name
custom_template
(str): the OAA template to use for the Provider ("application" or "identity_provider")
base64_icon
(str, optional): Base64 encoded string of icon to set for Provider. Defaults to "".
options
: (dict, optional): Additional arguments to be included with provider create call to Veza. Defaults to None.
Raises:
ValueError
: Provider name contains invalid characters
Returns:
dict
: dictionary representing the created Provider
create_query
create_query(query: 'dict') → dict
Create a new Assessment Query
For details on how to define an Assessment Query see the Veza docs.
Args:
query
(dict): Query definition
Returns:
dict
: API response including ID of created Query
create_report
create_report(report: 'dict') → dict
Create a new Report
For details on how to define a new Report see the Veza docs.
Args:
report
(dict): Report definition
Returns:
dict
: API response including ID of created Report
delete_data_source
delete_data_source(data_source_id: 'str', provider_id: 'str') → dict
Delete an existing Data Source by ID.
Removes a Data Source and all entity data.
Args:
data_source_id
(str): ID of Data Source to delete
provider_id
(str): ID of Data Source Provider
Returns:
dict
: API response
delete_provider
delete_provider(provider_id: 'str') → dict
Delete an existing provider by ID.
Deleting a provider removes all datasources and historical data. Fully deleting the provider is a background operation that will complete after API response is returned.
Args:
provider_id
(str): ID of provider to delete
Returns:
dict
: API response
delete_query
delete_query(id: 'str', force: 'bool' = False) → dict
Delete an Assessment Query by ID
Args:
id
(str): UUID of Query to delete
force
(bool): Force deletion of query that may be part of a report. Defaults to False
Returns:
dict
: API response from delete
delete_report
delete_report(id: 'str') → dict
Delete Report by ID
Args:
id
(str): UUID of Report to delete
Returns:
dict
: API response
get_data_source
get_data_source(name: 'str', provider_id: 'str') → dict
Get Provider's Data Source by name.
Find a Data Source from a specific provider based on the name of the Data Source
Args:
name
(str): Data Source name
provider_id
(str): Provider unique ID
Returns:
dict
: Data Source as dict or None
get_data_sources
get_data_sources(provider_id: 'str') → list[dict]
Get Data Sources for Provider by ID.
Get the list of existing Data Sources, filtered by Provider UUID.
Args:
provider_id
(str): ID of Provider
Returns:
list[dict]
: List of Data Sources as dictionaries
get_provider
get_provider(name: 'str') → dict
Get Provider by name.
Args:
name
(str): name of Provider
Returns:
dict
: dictionary representing Provider or None
get_provider_by_id
get_provider_by_id(provider_id: 'str') → dict
Get Provider by UUID identifier.
Args:
provider_id
(str): Unique global identifier for provider
Returns:
dict
: dictionary representation of Provider or None
get_provider_list
get_provider_list() → list[dict]
Return list of Providers.
Returns:
list[dict]
: Returns a list of existing Providers as dictionaries
get_queries
get_queries(include_inactive_queries: 'bool' = True) → list[dict]
Get all saved Assessment Queries
Veza can filter out queries that include inactive entity types (e.g. Okta Users on a system without Okta configured). To only retrieve queries that include active entity types set include_inactive_queries
to False.
Args:
include_inactive_queries
(bool): Set False to exclude inactive queries from result. Defaults to True.
Returns:
list
: List of assessment Queries as dictionaries
get_query_by_id
get_query_by_id(id: 'str') → dict
Get Assessment Query by ID
Args:
id
(str): UUID identifier for Query
Returns:
dict
: Query definition
get_report_by_id
get_report_by_id(id: 'str', include_inactive_queries: 'bool' = True) → dict
Get Report by ID
Veza can filter out queries from reports that only contain entity types that are not configured (e.g. Okta Users on a system without Okta configured). To only return queries configured on the report that match entity types configured on the system set include_inactive_queries
to False
Args:
id
(str): UUID of Report to get
include_inactive_queries
(bool): Set True to include inactive queries. Default True.
Returns:
dict
: Report definition
get_reports
get_reports(
include_inactive_reports: 'bool' = True,
include_inactive_queries: 'bool' = True
) → list[dict]
Get all Reports
Gets Reports created on the system. To get all reports include_inactive_reports
and include_inactive_queries
must be set to True.
Args:
include_inactive_reports
(bool, Optional): Set to True to include reports that contain no active providers, defaults to True.
include_inactive_queries
(bool, Optional): Set to True to include reports that contain only inactive queries, defaults to True.
Returns:
list[dict]
: List of Reports as dictionary objects
push_application
push_application(
provider_name: 'str',
data_source_name: 'str',
application_object: 'CustomApplication | CustomIdPProvider',
save_json: 'bool' = False,
create_provider: 'bool' = False,
options: 'dict | None' = None
) → dict
Push an OAA Application Object (such as CustomApplication).
Extracts the OAA JSON payload from the supplied OAA class (e.g. CustomApplication, CustomIdPProvider, etc) and push to the supplied Data Source.
The Provider with provider_name
must be a valid existing Provider or create_provider
must be set to True
. A new data source will be created automatically by default if it does not already exist.
For logging, and debugging, the optional save_json
flag writes the payload to a local file (before push). Output file name is formatted with a timestamp: {data source name}-{%Y%m%d-%H%M%S}.json
Args:
provider_name
(str): Name of an existing Provider.
data_source_name
(str): Name for Data Source (will be created if it doesn't exist).
application_object
(Class): OAA object to extract the payload from
save_json
(bool, optional): Save the JSON payload to a local file before push. Defaults to False.
create_provider
(bool, optional): Create a new Provider if Provider does not already exists. Defaults to False.
options
(dict, optional): Additional dictionary of key/values to be included in push API call. Defaults to None.
Raises:
OAAClientError
: If any API call returns an error (including errors processing the OAA payload).
Returns:
dict
: API response from push, including any warnings that are returned.
push_metadata
push_metadata(
provider_name: 'str',
data_source_name: 'str',
metadata: 'dict',
save_json: 'bool' = False,
options: 'dict | None' = None
) → dict
Push an OAA payload dictionary to Veza.
Publishes the supplied metadata
dictionary representing an OAA payload to the specified provider and data source. The function will create a new data source if it does not already exist, but requires the Provider be created ahead of time.
Args:
provider_name
(str): Name of existing Provider
data_source_name
(str): Name for Data Source, will be created if doesn't exist.
metadata
(dict): Dictionary of OAA payload to push.
save_json
(bool, optional): Save the OAA JSON payload to a local file before push. Defaults to False.
options
(dict, optional): Additional dictionary of key/values to be included in push API call. Defaults to None.
Raises:
OAAClientError
: If any API call returns an error including errors processing the OAA payload.
Returns:
dict
: API response to the push request (including any warnings).
update_provider_icon
update_provider_icon(provider_id: 'str', base64_icon: 'str') → None
Update an existing custom provider icon from base64 encoded string.
To load an icon from file, use utils.encode_icon_file
to get the base64 encoding of the file first
Args:
provider_id
(str): unique ID of existing provider
base64_icon
(str): base64 encoded string of new icon
Raises:
ValueError
: If icon size exceeds maximum allowed size
update_report
update_report(report_id: 'str', report: 'dict') → dict
Update an existing report
Args:
report_id
(str): UUID of Report to updated
report
(dict): Updated Report definition
Returns:
dict
: API response
update_user_agent
update_user_agent(extra: 'str' = '') → None
Updates the User-Agent string passed with all API calls
Generates a User-Agent with the oaaclient version, Python version and platform information.
The optional extra
string will be appended if provided.
Args:
extra
(str, optional): Additional information to append to User-Agent string. Defaults to "".
OAARetry
Super class for urllib3.util.retry
Super class to allow modifying the default max backoff time from 120 seconds to our own value
Args:
Retry
(type): description
__init__
__init__(backoff_max=30, **kwargs) → None
Classes for constructing an OAA JSON payload (Custom "Application" or "IdP").
Copyright 2022 Veza Technologies Inc.
Use of this source code is governed by the MIT license that can be found in the LICENSE file or at https://opensource.org/licenses/MIT.
Global Variables
PROPERTY_NAME_REGEX
append_helper
append_helper(base, addition)
Helper function to simplify appending.
Handles multiple cases:
base is None: starts a list
addition is list: extends base with list
addition is anything else: append element to list
Args:
base
(List or None): base list to append to, can be None
addition
(*): What to append to the list
Returns:
list
: will always return a list
unique_strs
unique_strs(input: 'list') → list
Returns a list of unique strings from input list case insensitive
Returns the unique list of strings from input list in a case insensitive manner. For duplicate strings with different cast (e.g. "STRING" and "string") the case of the first occurrence is returned.
Args:
input
(list): list of strings
Returns:
list
: list of unique strings
OAATemplateException
General exception used for violations of the template schema.
__init__
__init__(message)
OAAPermission
Canonical permissions used by Veza Authorization Framework.
Used to describe the raw data or metadata permissions granted by CustomPermission
OAAIdentityType
Types of identities for permission mapping.
Provider
Base class for CustomProvider.
__init__
__init__(name, custom_template)
serialize
serialize()
Application
Base class for CustomApplication.
__init__
__init__(name, application_type, description=None)
CustomApplication
Class for modeling application authorization using the OAA Application template.
CustomApplication class consists of identities, resources and permissions and produces the OAA JSON payload for the custom application template.
Class uses dictionaries to track most entities that can be referenced after creation. Dictionaries keys are case insensitive of the entity identifier (name or id). This applies to local_users
, local_groups
, local_roles
, idp_identities
, resources
and custom_permissions
.
Args:
name
(str): Name of custom application
application_type
(str): Searchable property, can be unique or shared across multiple applications
description
(str, optional): Description for application. Defaults to None.
Attributes:
application_type
(str): Searchable application type
custom_permissions
(dict[OAAPermission]): Dictionary of class instances
description
(str): Description for application
identity_to_permissions
(dict): Mapping of authorizations for identities to resources
idp_identities
(dict[IdPIdentity]): Contains federated identities without a corresponding local account
local_groups
(dict[LocalGroup]): Contains application groups (collections of users)
local_roles
(dict[LocalRole]): Contains application roles (collections of permissions)
local_users
(dict[LocalUser]): Contains users local to the application and their properties
name
(str): Name of custom application
properties
(dict): key value pairs of property values, property keys must be defined as part of the property_definitions
property_definitions
(ApplicationPropertyDefinitions): Custom property names and types for the application
resources
(dict[CustomResource]): Contains data resources and subresources within the application
__init__
__init__(name: 'str', application_type: 'str', description: 'str' = None) → None
add_access
add_access(identity, identity_type, permission, resource=None)
Legacy method for backwards compatibility.
.. deprecated:
---
### <kbd>method</kbd> `add_access_cred`
```python
add_access_cred(unique_id: 'str', name: 'str') → AccessCred
Create an Access Credential
Access creds can be used to represent alternative access methods such as API keys or application integrations.
Access creds can be assigned roles and permissions similar to local users. Access credentials can exist independently for use cases such as administratively created integrations or can be assigned to a local user for use cases like personal access tokens.
Args:
unique_id
(str): unique identifier for access cred
name
(str): name for access cred
Raises:
OAATemplateException
: Access credential with unique ID already exists
Returns:
AccessCred
: New access cred
add_custom_permission
add_custom_permission(
name: 'str',
permissions: 'List[OAAPermission]',
apply_to_sub_resources: 'bool' = False,
resource_types: 'List[str]' = None
) → CustomPermission
Create a new custom permission.
Creates a new CustomPermission
object for the application that can be used to authorize identities to the application, resources/sub-resource or as part of a role.
Args:
name
(str): Name of the permission
permissions
(list[OAAPermission]): Canonical permissions the custom permission represents
apply_to_sub_resources
(bool, optional): If true, when permission is applied to the application or resource, identity also has permission to all children of application/resource. Defaults to False.
resource_types
(list, optional): List of resource types as strings that the permission relates to. Defaults to empty list.
Returns: CustomPermission
add_idp_identity
add_idp_identity(name: 'str') → IdPIdentity
Create an IdP principal identity.
IdP users and groups can be authorized directly to applications and resources by associating custom application permissions and roles with an IdP identity's name or email.
Args:
name
(str): IdP unique identifier for user or group.
Returns: IdPIdentity
add_local_group
add_local_group(
name: 'str',
identities: 'List[str]' = None,
unique_id: 'str' = None
) → LocalGroup
Create a new local group.
Groups can be associated to resources via permissions or roles. All users in the local group are granted the group's authorization.
Local groups will be identified by name
by default, if unique_id
is provided it will be used as the identifier instead
Local groups can be referenced after creation using .local_groups
dictionary attribute. Dictionary is case insensitive keyed by unique_id or name if not using unique_id.
Args:
name
(str): Display name for group
identities
(list): List of IdP identities to associate group with.
unique_id
(str, optional): Unique identifier for group for reference by ID
Returns: LocalGroup
add_local_role
add_local_role(
name: 'str',
permissions: 'List[str]' = None,
unique_id: 'str' = None
) → LocalRole
Create a new local role.
A local role represents a collection of permissions.
Identities (local user, group, idp user) can be assigned a role to the application or resource, granting the role's permissions.
Local roles will be identified by name
by default, if unique_id
is provided it will be used as the identifier instead.
Local roles can be referenced after creation if needed through .local_roles
case insensitive dictionary attribute.
When a permission that has resource_types
is added to a role, it will only apply to resources with a matching resource_type
Args:
name
(str): Display name for role
permissions
(list): List of Custom Permission names to include in role. CustomPermission
must be created separately.
unique_id
(str, optional): Unique identifier for role for reference by ID
Returns: LocalRole
add_local_user
add_local_user(
name: 'str',
identities: 'List[str]' = None,
groups: 'List[str]' = None,
unique_id: 'str' = None
) → LocalUser
Create a new local user for application.
Local users can be assigned to groups and associated with resources via permissions or roles. Groups and identities can be provided at creation or added later. See Identity
and LocalUser
class for operations.
Local users will be identified by name
by default, if unique_id
is provided it will be used as the identifier instead.
Local users can be referenced after creation using the .local_users
dictionary attribute. Dictionary is case insensitivekeyed by unique_id or name if not using unique_id.
Use unique_id
when name is not guaranteed to be unique. All permission, group and role assignments will be referenced by unique_id.
Args:
name
(str): Display name for user
identities
(list): List of identities as strings (usually email) for local user. Used to map local user to discovered IdP identities.
groups
(list[LocalGroup]): List of group names (as string) to add user to
unique_id
(str, optional): Unique identifier for user for reference by ID
Returns: LocalUser
add_resource
add_resource(
name: 'str',
resource_type: 'str',
description: 'str' = None,
unique_id: 'str' = None
) → CustomResource
Create a new resource under the application.
Resource type is used to group and filter application resources. It should be consistent for all common resources of an application.
Returns new resource object.
Resource is identified by name
by default unless unique_id
is provided. name
must be unique if not using unique_id
.
Resources can be referenced after creation using the .resources
dictionary attribute. Dictionary is keyed by unique_id or name if not using unique_id. Use unique_id
when name is not guaranteed to be unique.
Args:
name
(str): Name of resources
resource_type
(str): Type for resource
description
(str, optional): Description of resources. Defaults to None.
unique_id
(str, optional): Unique identifier for resource. defaults to None.
Returns: CustomResource
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a tag to the Application
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
app_dict
app_dict() → dict
Return the 'applications' section of the payload as serializable dictionary.
define_custom_permission
define_custom_permission(
custom_permission: 'CustomPermission'
) → CustomPermission
Add a custom permission to the application.
.. deprecated: ``` See CustomApplication.add_custom_permission()
**Args:**
- <b>`custom_permission`</b> (CustomPermission): CustomPermission class
**Raises:**
- <b>`Exception`</b>: Duplicate Keys
**Returns:**
- <b>`CustomPermission`</b>: The defined custom Permission
---
### <kbd>method</kbd> `get_identity_to_permissions`
```python
get_identity_to_permissions() → list
Collect authorizations for all identities into a single list.
get_payload
get_payload() → dict
Get the OAA payload.
Returns the complete OAA template payload for application as serializable dictionary
Returns:
dict
: OAA payload as dictionary
permissions_dict
permissions_dict() → dict
Return the 'permissions' section of the payload as serializable dictionary.
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set a custom property value for the application.
Property name must be defined for CustomApplication
before calling set_property. See example below and ApplicationPropertyDefinitions.define_application_property
for more information on defining properties.
Args:
property_name
(str): Name of property to set value for, property names must be defined as part of the application property_definitions
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property name is not defined
Example: app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_application_property(name="my_property", property_type=OAAPropertyType.STRING) >>> app.set_property("my_property", "property value")
CustomResource
Class for resources and sub-resources.
Should be used for representing components of the application to which authorization is granted. Each resource has a name and a type. The type can be used for grouping and filtering.
Arguments:
name
(str): display name for resource, must be unique to parent application or resource unless using unique_id
resource_type
(str): type for resource
description
(str): description for resource
application_name
(str): name of parent application
resource_key
(str, optional): for sub-resources the full unique identifier required for identity_to_permissions
section. Defaults to name or unique_id if not provided.
property_definitions
(ApplicationPropertyDefinitions, optional): Property definitions structure for the resource
unique_id
(str, optional): Optional unique identifier for the resource. Defaults to None.
Attributes:
name
(str): display name for resource, must be unique to parent application or resource
unique_id
(str): resource's unique identifier if provided.
resource_type
(str): type for resource
application_name
(str): name of parent application
resource_key
(str): for sub-resources represents the sub-resource's parent path
sub_resources
(dict): dictionary of sub-resources keyed by name
properties
(dict): dictionary of properties set for resource
tags
(list[Tag]): list of tags
__init__
__init__(
name: 'str',
resource_type: 'str',
description: 'str',
application_name: 'str',
resource_key: 'str' = None,
property_definitions: 'ApplicationPropertyDefinitions' = None,
unique_id: 'str' = None
) → None
add_access
add_access(identity, identity_type, permission)
No longer supported, access should be added through identity (local_user, local_group, idp)
add_resource_connection
add_resource_connection(id: 'str', node_type: 'str') → None
Add an external connection to the resource.
Used to add a relationship to another entity discovered by Veza such as a service account or AWS IAM role.
Args:
id
(str): Unique identifier for connection entity
node_type
(str): Veza type for connecting node
add_sub_resource
add_sub_resource(
name: 'str',
resource_type: 'str',
description: 'str' = None,
unique_id: 'str' = None
) → CustomResource
Create a new sub-resource under current resource
Args:
name
(str): display name for resource
resource_type
(str): type for resource
description
(str, optional): String description. Defaults to None.
unique_id
(str, optional): Unique identifier for new subresource, Defaults to name
.
Returns: CustomResource
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to resource.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set the value for a custom property on a resource or sub-resource.
Property name must be defined for resource type before calling set_property()
. See example below and ApplicationPropertyDefinitions.define_resource_property
for more information on defining properties.
Args:
property_name
(str): Name of property to set value for
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property_name
is not defined
Example: app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_resource_property(resource_type="cog", name="my_property", property_type=OAAPropertyType.STRING) >>> cog1 = app.add_resource(name="cog1", resource_type="cog") >>> cog1.set_property("my_property", "this value")
to_dict
to_dict() → dict
Return the dictionary representation of resource.
Identity
Base class for deriving all identity types (should not be used directly).
Args:
name
(str): name of identity
identity_type
(OAAIdentityType): Veza Identity Type (local_user, local_group, idp)
unique_id
(str, optional): ID of entity for reference by ID
Attributes:
name
(str): name of identity
identity_type
(OAAIdentityType): Veza Identity Type (local_user, local_group, idp)
application_permissions
(list[CustomPermission]): List of permissions identity has directly to custom application
resource_permissions
(dict): Dictionary of custom permissions associated with resources and sub-resources. Key is permission, value is list of resource keys
application_roles
(LocalRole): List of roles identity has directly to custom application
resource_roles
(dict): Dictionary of local_roles for resources and sub-resources. Key is roles, value is list of resource keys
properties
(dict): Dictionary of properties for identity, allowed values will vary by identity type
tags
(list[Tag]): List of tags
__init__
__init__(
name: 'str',
identity_type: 'OAAIdentityType',
unique_id: 'str' = None,
property_definitions: 'ApplicationPropertyDefinitions' = None
) → None
add_permission
add_permission(
permission: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'bool' = False
) → None
Add a permission to an identity.
Permission can apply to either the application or application resource/sub-resources
Args:
permissions
([str]): List of strings representing the permission names
resource
(CustomResource, optional): Custom resource, if None permission is applied to application. Defaults to None.
apply_to_application
(bool): Apply permission to application when True, defaults to False
add_role
add_role(
role: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'Optional[bool]' = None,
assignment_properties: 'Optional[dict]' = None
) → None
Add a role to an identity.
Role to authorize identity to either the application or application resource/sub-resource based on role's permissions.
Role assignment properties can be set with the assignment_properties
dictionary parameter with property names as the keys. Role assignment properties types must be defined on the application prior to setting.
Args:
role
(str): Name of role as string
resources
(List[CustomResource], optional): Custom resource, if None role is applied to application. Defaults to None.
apply_to_application
(bool, optional): Apply permission to application when True, False will replace existing value, None will leave previous setting if any
assignment_properties
(dict, optional): Custom properties for the role assignment. Defaults to no properties.
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to identity.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
get_identity_to_permissions
get_identity_to_permissions(application_name: 'str') → dict
Get a JSON serializable dictionary of all the identity's permissions and roles.
Formats the identity's permissions and roles for the Custom Application template payload
Returns:
dict
: JSON serializable dictionary of all the identity's permissions and roles
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set a custom defined property to a specific value on an identity.
Property name must be defined for identity type before calling set_property()
. See example below for LocalUser
and ApplicationPropertyDefinitions.define_local_user_property
for more information on defining properties. Property must be defined for the correct Identity
type (LocalUser
or LocalGroup
, IdPIdentity
does not support custom properties).
Args:
property_name
(str): Name of property to set value for
property_value
(Any): Value for property, type should match OAAPropertyType
for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example:
app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_local_user_property(name="my_property", property_type=OAAPropertyType.STRING) >>> user1 = app.add_local_user(name="user1") >>> user1.set_property("my_property", "value for user1")
LocalUserType
Enum for
LocalUser
LocalUser identity, derived from Identity base class.
Used to model an application user. Can be associated with an external IdP user, or represent a local account.
Args:
name
(str): name of identity
identities
(list): list of strings for IdP identity association
groups
(list[LocalGroup]): list of group names as strings to add user too
unique_id
(string, optional): For reference by ID
Attributes:
name
(str): name of identity
id
(str): ID of entity for ID based reference
email
(string): Users email address
identities
(list): list of strings for IdP identity association
groups
(list[LocalGroup]): list of group names as strings to add user too
identity_type
(OAAIdentityType): Veza Identity Type (local_user)
application_permissions
(list[CustomPermission]): Permissions identity has directly to custom application
resource_permissions
(dict): Dictionary of custom permissions associated with resources and sub-resources. Key is permission, value is list of resource keys
application_roles
(list[LocalRole]): Custom application roles assigned directly to the identity
resource_roles
(dict): Dictionary of local_roles for resources and sub-resources. Key is roles, value is list of resource keys
properties
(dict): Dictionary of properties for identity, allowed values will vary by identity type
tags
(list[Tag]): List of tags
is_active
(bool): Defaults to None for unset
created_at
(str): RFC3339 time stamp for user creation
last_login_at
(str): RFC3339 time stamp for last login
deactivated_at
(str): RFC3339 for user deactivate time
password_last_changed_at
(str): RFC3339 time stamp for last password change
user_type
(LocalUserType): Set the local user account type
__init__
__init__(
name: 'str',
identities: 'List[str]' = None,
groups: 'List[str]' = None,
unique_id: 'str' = None,
property_definitions: 'ApplicationPropertyDefinitions' = None
) → None
add_access_cred
add_access_cred(access_cred: 'str') → None
Add access cred to user (access cred must be created separately)
Args:
access_cred
(str): unique identifier of access cred
add_group
add_group(group: 'str') → None
Add user to local group (group must be created separately).
Args:
group
(str): identifier of local group
add_identities
add_identities(identities: 'List[str]') → None
Add multiple identities to a local user from a list.
Args:
identities
(list[str]): list of identities to add to user
add_identity
add_identity(identity: 'str') → None
Add an identity to user.
Identity should match the email address or another principal identifier for an IdP user (Okta, Azure, ect). Veza will create a connection from the application local user to IdP identity.
Args:
identity
(str): email or identifier for IdP user
add_permission
add_permission(
permission: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'bool' = False
) → None
Add a permission to an identity.
Permission can apply to either the application or application resource/sub-resources
Args:
permissions
([str]): List of strings representing the permission names
resource
(CustomResource, optional): Custom resource, if None permission is applied to application. Defaults to None.
apply_to_application
(bool): Apply permission to application when True, defaults to False
add_role
add_role(
role: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'Optional[bool]' = None,
assignment_properties: 'Optional[dict]' = None
) → None
Add a role to an identity.
Role to authorize identity to either the application or application resource/sub-resource based on role's permissions.
Role assignment properties can be set with the assignment_properties
dictionary parameter with property names as the keys. Role assignment properties types must be defined on the application prior to setting.
Args:
role
(str): Name of role as string
resources
(List[CustomResource], optional): Custom resource, if None role is applied to application. Defaults to None.
apply_to_application
(bool, optional): Apply permission to application when True, False will replace existing value, None will leave previous setting if any
assignment_properties
(dict, optional): Custom properties for the role assignment. Defaults to no properties.
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to identity.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
get_identity_to_permissions
get_identity_to_permissions(application_name: 'str') → dict
Get a JSON serializable dictionary of all the identity's permissions and roles.
Formats the identity's permissions and roles for the Custom Application template payload
Returns:
dict
: JSON serializable dictionary of all the identity's permissions and roles
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set a custom defined property to a specific value on an identity.
Property name must be defined for identity type before calling set_property()
. See example below for LocalUser
and ApplicationPropertyDefinitions.define_local_user_property
for more information on defining properties. Property must be defined for the correct Identity
type (LocalUser
or LocalGroup
, IdPIdentity
does not support custom properties).
Args:
property_name
(str): Name of property to set value for
property_value
(Any): Value for property, type should match OAAPropertyType
for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example:
app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_local_user_property(name="my_property", property_type=OAAPropertyType.STRING) >>> user1 = app.add_local_user(name="user1") >>> user1.set_property("my_property", "value for user1")
to_dict
to_dict() → dict
Output user to dictionary for payload.
LocalGroup
LocalGroup identity.
Derived from Identity base class. Used to represent groups of local users for application.
Args:
name
(str): name of group
identities
(list): list of strings for IdP identity association
unique_id
(string, optional): Unique identifier for group
Attributes:
name
(str): name of identity
identities
(list): list of strings for IdP identity association
groups
(list[LocalGroup]): list of group names as strings that group is member of for nested groups
identity_type
(OAAIdentityType): Veza Identity Type, local_group
application_permissions
(list[CustomPermission]): permissions identity has directly to custom application
resource_permissions
(dict): Dictionary of custom permissions associated with resources and sub-resources. Key is permission, value is list of resource keys
application_roles
(list[LocalRole]): list of roles identity has directly to custom application
resource_roles
(dict): Dictionary of local_roles for resources and sub-resources. Key is roles, value is list of resource keys
properties
(dict): Dictionary of properties for identity, allowed values will vary by identity type
tags
(list[Tag]): List of tags
created_at
(str): RFC3339 time stamp for group creation time
__init__
__init__(
name,
identities=None,
unique_id: 'str' = None,
property_definitions: 'ApplicationPropertyDefinitions' = None
)
add_group
add_group(group: 'str') → None
Add a nested group to local group (group must be created separately).
Args:
group
(str): identifier of local group
add_identity
add_identity(identity: 'str') → None
Add an identity to group.
The email address or another valid identifier should match that of an IdP principal (Okta, Azure, ect). Veza will create a connection from the application local group to IdP identity.
Args:
identity
(str): primary IdP identifier for group to associate
add_permission
add_permission(
permission: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'bool' = False
) → None
Add a permission to an identity.
Permission can apply to either the application or application resource/sub-resources
Args:
permissions
([str]): List of strings representing the permission names
resource
(CustomResource, optional): Custom resource, if None permission is applied to application. Defaults to None.
apply_to_application
(bool): Apply permission to application when True, defaults to False
add_role
add_role(
role: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'Optional[bool]' = None,
assignment_properties: 'Optional[dict]' = None
) → None
Add a role to an identity.
Role to authorize identity to either the application or application resource/sub-resource based on role's permissions.
Role assignment properties can be set with the assignment_properties
dictionary parameter with property names as the keys. Role assignment properties types must be defined on the application prior to setting.
Args:
role
(str): Name of role as string
resources
(List[CustomResource], optional): Custom resource, if None role is applied to application. Defaults to None.
apply_to_application
(bool, optional): Apply permission to application when True, False will replace existing value, None will leave previous setting if any
assignment_properties
(dict, optional): Custom properties for the role assignment. Defaults to no properties.
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to identity.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
get_identity_to_permissions
get_identity_to_permissions(application_name: 'str') → dict
Get a JSON serializable dictionary of all the identity's permissions and roles.
Formats the identity's permissions and roles for the Custom Application template payload
Returns:
dict
: JSON serializable dictionary of all the identity's permissions and roles
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set a custom defined property to a specific value on an identity.
Property name must be defined for identity type before calling set_property()
. See example below for LocalUser
and ApplicationPropertyDefinitions.define_local_user_property
for more information on defining properties. Property must be defined for the correct Identity
type (LocalUser
or LocalGroup
, IdPIdentity
does not support custom properties).
Args:
property_name
(str): Name of property to set value for
property_value
(Any): Value for property, type should match OAAPropertyType
for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example:
app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_local_user_property(name="my_property", property_type=OAAPropertyType.STRING) >>> user1 = app.add_local_user(name="user1") >>> user1.set_property("my_property", "value for user1")
to_dict
to_dict() → dict
Output group to dictionary for payload.
IdPIdentity
IdP identity derived from Identity base class.
Used to associate IdP identities (users or groups) directly to resource where concept of local users/groups doesn't apply to application.
Args:
name
(str): Primary IdP identifier for identity (email, group name, etc)
Attributes:
name
(str): name of identity
identity_type
(OAAIdentityType): Veza Identity Type, (idp)
application_permissions
(list[CustomPermission]): permissions identity has directly to custom application
resource_permissions
(dict): Dictionary of custom permissions associated with resources and sub-resources. Key is permission, value is list of resource keys
application_roles
(list[LocalRole]): roles identity has directly to custom application
resource_roles
(dict): Dictionary of local_roles for resources and sub-resources. Key is roles, value is list of resource keys
properties
(dict): Dictionary of properties for identity, allowed values will vary by identity type
tags
(list[Tag]): List of tags
__init__
__init__(name: 'str') → None
add_permission
add_permission(
permission: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'bool' = False
) → None
Add a permission to an identity.
Permission can apply to either the application or application resource/sub-resources
Args:
permissions
([str]): List of strings representing the permission names
resource
(CustomResource, optional): Custom resource, if None permission is applied to application. Defaults to None.
apply_to_application
(bool): Apply permission to application when True, defaults to False
add_role
add_role(
role: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'Optional[bool]' = None,
assignment_properties: 'Optional[dict]' = None
) → None
Add a role to an identity.
Role to authorize identity to either the application or application resource/sub-resource based on role's permissions.
Role assignment properties can be set with the assignment_properties
dictionary parameter with property names as the keys. Role assignment properties types must be defined on the application prior to setting.
Args:
role
(str): Name of role as string
resources
(List[CustomResource], optional): Custom resource, if None role is applied to application. Defaults to None.
apply_to_application
(bool, optional): Apply permission to application when True, False will replace existing value, None will leave previous setting if any
assignment_properties
(dict, optional): Custom properties for the role assignment. Defaults to no properties.
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to identity.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
get_identity_to_permissions
get_identity_to_permissions(application_name: 'str') → dict
Get a JSON serializable dictionary of all the identity's permissions and roles.
Formats the identity's permissions and roles for the Custom Application template payload
Returns:
dict
: JSON serializable dictionary of all the identity's permissions and roles
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set custom IdP property (no functionality).
IdP identities do not support custom properties since the identity is discovered through the provider (Okta, Azure, etc)
AccessCred
Access Credential derived from Identity base class.
Access Creds can be used to represent non-user based methods that grant access such as API keys or integrations.
AccessCreds can be assigned roles or permissions to an application or resource. An AccessCred can stand-alone or be associated to a local user.
Args:
unique_id
(str): Unique identifier for access cred
name
(str): Name for access cred, does not need to be unique
Attributes:
unique_id
(str): Unique identifier for access cred
name
(str): Name for access cred, does not need to be unique
is_active
(bool): Indicate if credential is active, defaults to True
created_at
(str): Time access cred was created at as RFC3339 timestampe, defaults to empty
expires_at
(str): Time access cred was created at as RFC3339 timestampe, defaults to empty
last_used_at
(str): Time access cred was created at as RFC3339 timestampe, defaults to empty
can_expire
(bool): Boolean to indicate if credential type can exipre, defaults to unset
__init__
__init__(
unique_id: 'str',
name: 'str',
property_definitions: 'ApplicationPropertyDefinitions' = None
) → None
add_permission
add_permission(
permission: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'bool' = False
) → None
Add a permission to an identity.
Permission can apply to either the application or application resource/sub-resources
Args:
permissions
([str]): List of strings representing the permission names
resource
(CustomResource, optional): Custom resource, if None permission is applied to application. Defaults to None.
apply_to_application
(bool): Apply permission to application when True, defaults to False
add_role
add_role(
role: 'str',
resources: 'List[CustomResource]' = None,
apply_to_application: 'Optional[bool]' = None,
assignment_properties: 'Optional[dict]' = None
) → None
Add a role to an identity.
Role to authorize identity to either the application or application resource/sub-resource based on role's permissions.
Role assignment properties can be set with the assignment_properties
dictionary parameter with property names as the keys. Role assignment properties types must be defined on the application prior to setting.
Args:
role
(str): Name of role as string
resources
(List[CustomResource], optional): Custom resource, if None role is applied to application. Defaults to None.
apply_to_application
(bool, optional): Apply permission to application when True, False will replace existing value, None will leave previous setting if any
assignment_properties
(dict, optional): Custom properties for the role assignment. Defaults to no properties.
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to identity.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
get_identity_to_permissions
get_identity_to_permissions(application_name: 'str') → dict
Get a JSON serializable dictionary of all the identity's permissions and roles.
Formats the identity's permissions and roles for the Custom Application template payload
Returns:
dict
: JSON serializable dictionary of all the identity's permissions and roles
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set a custom defined property to a specific value on an access credential.
Property name must be defined for access credentials before calling set_property()
. See example below and ApplicationPropertyDefinitions.define_access_cred_property
for more information on defining properties.
Args:
property_name
(str): Name of property to set value for
property_value
(Any): Value for property, type should match OAAPropertyType
for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example:
app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_access_cred_property(name="my_property", property_type=OAAPropertyType.STRING) >>> cred1 = app.add_access_cred(unique_id="cred001", name="Cred 001") >>> cred1.set_property("my_property", "value for cred001")
to_dict
to_dict() → dict
Output Access credential dictionary for payload
LocalRole
Represent a Custom Application Local Role.
Local Roles are a collection of permissions (as CustomPermission
). Roles can be used to associate a local user, group or IdP identity to an application, resource or sub-resource.
Permissions can either be assigned at creation and/or added later.
If the CustomPermission
definition includes resource types in the resource_types
list, the permission will only be assigned to resources/sub-resources that match that type as part of an assignment.
Args:
name
(str): name of local role
permissions
(list[CustomPermission], optional): List of custom permission names (strings) to associate with the role. Defaults to empty list.
unique_id
(string, optional): Unique identifier for role for identification by ID
Attributes:
name
(str): name of local role
unique_id
(str): Unique identifier for role for identification by ID
permissions
(list[CustomPermission]): list of custom permission names (strings) to associate with the role
roles
(list[LocalRole]): list of roles nested inside the role
tags
(list[Tag]): list of Tags instances
__init__
__init__(
name: 'str',
permissions: 'List[str]' = None,
unique_id: 'str' = None,
property_definitions: 'ApplicationPropertyDefinitions' = None
) → None
add_permissions
add_permissions(permissions: 'List[str]') → None
Add a permission to the role.
Args:
permissions
(list): List of permission names (strings) to add to role
add_role
add_role(role: 'str') → None
Add a nested sub-role to the role (nested role must be created separately)
Args:
role
(str): identifier of the local role to nest inside this role
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to role.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set the value for custom property on a local role.
Property name must be defined for local roles before calling set_property()
. See example below and ApplicationPropertyDefinitions.define_local_role_property
for more information on defining properties.
Args:
property_name
(str): Name of property to set value for
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property name is not defined.
Example: app = CustomApplication("App", application_type="example")
>>> app.property_definitions.define_local_role_property(name="my_property", property_type=OAAPropertyType.STRING) >>> role1 = app.add_local_role(name="role1") >>> role1.set_property(property_name="my_property", property_value="role1s value")
to_dict
to_dict() → dict
Convert role to dictionary for inclusion in JSON payload.
Returns:
dict
: serializable dictionary of role
CustomPermission
CustomPermission class for defining CustomApplication
permissions.
Custom permissions represent the named permissions for the application in its terms (e.g. "Admin" or "PUSH") and define the Veza canonical mapping (e.g. DataRead, MetadataRead, DataWrite).
A permission can either be applied directly to an application or resource or assigned as part of a role.
Optionally, when permissions are used as part of a role, if the resource_types
list is populated the permission will only be applied to resources who's type is in the resource_types
list when the role is applied to a resource.
Args:
name
(str): Display name for permission
permissions
(list): List of OAAPermission enums that represent the canonical permissions
apply_to_sub_resources
(bool, optional): If true, when permission is applied to the application or resource, identity also has permission to all children of application/resource. Defaults to False
.
resource_types
(list, optional): List of resource types as strings that the permission relates to. Defaults to empty list.
Attributes:
name
(str): Display name for permission
permissions
(list[OAAPermission]): List of OAAPermission enums that represent the canonical permissions
apply_to_sub_resources
(bool): If true, when permission is applied to the application or resource, identity also has permission to all children of application/resource.
resource_types
(list): List of resource types as strings that the permission relates to.
__init__
__init__(
name: 'str',
permissions: 'List[OAAPermission]',
apply_to_sub_resources: 'bool' = False,
resource_types: 'list' = None
) → None
add_resource_type
add_resource_type(resource_type: 'str') → None
Add a resource type to the resource_types list.
Extends the list of resource types permission applies to when used in role assignment.
Args:
resource_type
(str): The resource type string value
to_dict
to_dict() → dict
Returns dictionary representation for payload.
OAAPropertyType
Supported types for custom properties on OAA entities such as application, resource, and identity.
ApplicationPropertyDefinitions
Model for defining custom properties for application and its entities (users, groups, roles, resources).
Property definitions define the names for additional entity properties and the expected type.
Args:
application_type
(str): type of custom application property definitions apply to
Attributes:
application_properties
(dict): property definitions for application
local_user_properties
(dict): property definitions for local users
local_group_properties
(dict): property definitions for local groups
local_role_properties
(dict): property definitions for local roles
resources
(dict): property definitions for resources keyed by resource type
__init__
__init__(application_type: 'str') → None
define_access_cred_property
define_access_cred_property(
name: 'str',
property_type: 'OAAPropertyType'
) → None
Define an access cred property.
Args:
name
(str): name for property
property_type
(OAAPropertyType): type for property
define_application_property
define_application_property(
name: 'str',
property_type: 'OAAPropertyType'
) → None
Define an application property.
Args:
name
(str): name for property
property_type
(OAAPropertyType): type for property
define_local_group_property
define_local_group_property(
name: 'str',
property_type: 'OAAPropertyType'
) → None
Define a local group property.
Args:
name
(str): name for property
property_type
(OAAPropertyType): type for property
define_local_role_property
define_local_role_property(name: 'str', property_type: 'OAAPropertyType') → None
Define a local role property.
Args:
name
(str): name for property
property_type
(OAAPropertyType): type for property
define_local_user_property
define_local_user_property(name: 'str', property_type: 'OAAPropertyType') → None
Define a local user property.
Args:
name
(str): name for property
property_type
(OAAPropertyType): type for property
define_resource_property
define_resource_property(
resource_type: 'str',
name: 'str',
property_type: 'OAAPropertyType'
) → None
Define a property for a resource by type of resource.
Args:
resource_type
(str): type of resource property definition is for
name
(str): property name
property_type
(OAAPropertyType): type for property
define_role_assignment_property
define_role_assignment_property(
name: 'str',
property_type: 'OAAPropertyType'
) → None
to_dict
to_dict() → dict
Return property definitions as dictionary ready for OAA payload
validate_name
validate_name(name: 'str') → None
Check property name for valid characters
Raises an exception if the name string does not match required pattern. Name must start with a character and can only contain letters and _ character.
Args:
name
(str): name of property to validate
Raises:
OAATemplateException
: Name is not a string
OAATemplateException
: Name contains invalid characters or does not start with a letter
validate_property_name
validate_property_name(
property_name: 'str',
entity_type: 'str',
resource_type: 'str' = None
) → bool
Validate that a property name has been defined for given resource type.
Args:
property_name
(str): name of property to validate
entity_type
(str): type of entity custom property is for (application, local_user, local_group, local_role, resource)
resource_type
(str): (optional) type for validating resource property names, only applicable to entity_type resource
Raises:
OAATemplateException
: If property name has not been previously defined for entity
IdPEntityType
IdP entity types.
IdPProviderType
Veza supported IdP provider types.
CustomIdPProvider
CustomIdPProvider class for modeling Identity Providers (IdP) using OAA Custom Identity Provider Template.
CustomIdPProvider class consists of IdP domain information, user, group and external associations for identities like AWS Roles.
Classes uses dictionaries to track most components, dictionaries are all keyed by string of the entity name
Args:
name
(str): Name of IdP
idp_type
(str): Type descriptor for IdP, can be unique or share across multiple IdP e.g. ldap, IPA
domain
(str): IdP domain name
description
(str, optional): Description for IdP. Defaults to None.
Attributes:
name
(str): Name of custom IdP
idp_type
(str): Type for IdP
description
(str): Description for IdP
domain
(CustomIdPDomain): Domain model, created with domain name at init
users
(dict[CustomIdPUser]): Dictionary of CustomIdPUser class instances
groups
(dict[CustomIdPGroup]): Dictionary of CustomIdPGroup class instances
property_definitions
(IdPPropertyDefinitions): Custom Property definitions for IdP instance
__init__
__init__(
name: 'str',
idp_type: 'str',
domain: 'str',
description: 'str' = None
) → None
add_app
add_app(id: 'str', name: 'str') → CustomIdPApp
summary
Args:
id
(str): description
name
(str): description
Raises:
Returns:
CustomIdPApp
: description
add_group
add_group(
name: 'str',
full_name: 'str' = None,
identity: 'str' = None
) → CustomIdPGroup
Add group to IdP.
Arguments:
name
(str): primary ID for group
full_name
(str): optional display name for group
identity
(str): optional unique identifier for group, if None name is used as identity
add_user
add_user(
name: 'str',
full_name: 'str' = None,
email: 'str' = None,
identity: 'str' = None
) → CustomIdPUser
Add user to IdP
if no identity is set name will be used as identity
Arguments:
name
(str): primary ID for user
full_name
(str): optional full name for display
email
(str): optional email for user
identity
(str): optional unique identifier for user, if None name is used as identity
Returns: CustomIdPUser
get_payload
get_payload() → dict
Return formatted payload as dictionary for JSON conversion and upload
CustomIdPDomain
Domain model for Custom IdP provider.
Args:
name
(str): domain name
Attributes:
name
(str): domain name
__init__
__init__(
name: 'str',
property_definitions: 'IdPPropertyDefinitions' = None
) → None
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to IdP Domain.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set custom property value for domain.
Property name must be defined for domain before calling set_property()
. See example below and IdPPropertyDefinitions.define_domain_property
for more information.
Args:
property_name
(str): Name of property
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example: idp = CustomIdPProvider(name="Example IdP", idp_type="example", domain="example.com")
>>> idp.property_definitions.define_domain_property(name="my_property", property_type=OAAPropertyType.STRING) >>> idp.domain.set_property(property_name="my_property", property_value="domain property value")
to_dict
to_dict() → dict
Output function for payload.
CustomIdPUser
User model for CustomIdPProvider.
Args:
name
(str): username for identity
email
(str): primary email for user
full_name
(str): Display name for user
identity
(str): unique identifier for user (may be same as username or email, or another unique ID like employee number)
Attributes:
name
(str): username for identity
email
(str): primary email for user
full_name
(str): display name for user
identity
(str): unique identifier for user (may be same as username or email, or another unique ID like employee number)
department
(str): department name for user
is_active
(bool): if user is active, defaults to None
is_guest
(bool): if user is a guest type user, defaults to None
manager_id
(str, optional): CustomIdPUser.identity of manager, defaults to None
__init__
__init__(
name: 'str',
email: 'str' = None,
full_name: 'str' = None,
identity: 'str' = None,
property_definitions: 'IdPPropertyDefinitions' = None
) → None
add_app_assignment
add_app_assignment(
id: 'str',
name: 'str',
app_id: 'str',
assignment_properties: 'Optional[dict]' = None
) → None
Create App assignment for user
Args:
id
(str): ID of App assignment, must be unique for user
name
(str): Name of assignment
app_id
(str): App ID, must exist in list of Apps for IDP
assignment_properties
(Optional[dict], optional): Optional custom properties to set. Property names must be defined first. Defaults to None.
Raises:
OAATemplateException
: Duplicate assignment ID
OAATemplateException
: Unknown assignment property name
add_assumed_role_arns
add_assumed_role_arns(arns: 'List[str]') → None
Add AWS Roles to list of roles user can assume by ARN.
Args:
arns
(list): list of role ARNs as strings that the user is allowed to assume
add_groups
add_groups(group_identities: 'List[str]') → None
Add user to group(s) by group name
Args:
group_identities
(list): list of strings for group identities to add user to
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to IdP User.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set custom property value for user.
Property name must be defined for users before calling set_property()
. See example below and IdPPropertyDefinitions.define_user_property
for more information.
Args:
property_name
(str): Name of property
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example: idp = CustomIdPProvider(name="Example IdP", idp_type="example", domain="example.com")
>>> idp.property_definitions.define_user_property(name="my_property", property_type=OAAPropertyType.STRING) >>> user1 = idp.add_user(name="User 1") >>> user1.set_property("my_property", "user1 value")
set_source_identity
set_source_identity(identity: 'str', provider_type: 'IdPProviderType') → None
Set an source external identity for user.
source_identity
will connect CustomIdP user to a Veza graph IdP user.
provider_type
limits scope for finding matching IdP identities
search all providers with IdPProviderType.ANY
.
Args:
identity
(str): Unique Identity of the source identity
provider_type
(IdPProviderType): Type for provider to match source identity from
to_dict
to_dict() → dict
Function to prepare user entity for payload
CustomIdPGroup
Group model for CustomIdPProvider.
Args:
name
(str): name of group
full_name
(str): optional full name for group
identity
(str): optional identifier for group if name is not reference identifier
Parameters:
name
(str): name of group
full_name
(str): optional full name for group
identity
(str): optional identifier for group, if None name is used as identity
is_security_group
(bool): Property for group, defaults to None (unset)
__init__
__init__(
name: 'str',
full_name: 'str' = None,
identity: 'str' = None,
property_definitions: 'IdPPropertyDefinitions' = None
) → None
add_app_assignment
add_app_assignment(
id: 'str',
name: 'str',
app_id: 'str',
assignment_properties: 'Optional[dict]' = None
) → None
Create App assignment for group
Args:
id
(str): ID of App assignment, must be unique for group
name
(str): Name of assignment
app_id
(str): App ID, must exist in list of Apps for IDP
assignment_properties
(Optional[dict], optional): Optional custom properties to set. Property names must be defined first. Defaults to None.
Raises:
OAATemplateException
: Duplicate assignment ID
OAATemplateException
: Unknown assignment property name
add_assumed_role_arns
add_assumed_role_arns(arns: 'List[str]') → None
Add AWS Roles to list of roles group members can assume by ARN.
Args:
arns
(list): list of role ARNs as strings that the group members are allowed to assume
add_groups
add_groups(group_identities: 'List[str]') → None
Add group to group(s) by group name
Adds current group to another parent group by the group identifier
Args:
group_identities
(list): list of strings for group identities to add group to
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to IdP Group.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set custom property value for group.
Property name must be defined for groups before calling set_property()
. See example below and IdPPropertyDefinitions.define_group_property
for more information.
Args:
property_name
(str): Name of property
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example: idp = CustomIdPProvider(name="Example IdP", idp_type="example", domain="example.com")
>>> idp.property_definitions.define_group_property(name="my_property", property_type=OAAPropertyType.STRING) >>> group1 = idp.add_group(name="Group 1") >>> group1.set_property("my_property", "group1 value")
to_dict
to_dict() → None
Function to prepare user entity for payload.
CustomIdPApp
App model for CustomIdPProvider
Args:
id
(str): ID for App, must be unique
name
(str): Name for App
property_definitions
(IdPPropertyDefinitions, optional): Custom property definitions, required to set custom properties. Defaults to None.
Attributes:
id
(str): ID for App, must be unique
name
(str): Name for App
description
(str): Description property for App
__init__
__init__(
id: 'str',
name: 'str',
property_definitions: 'IdPPropertyDefinitions' = None
) → None
add_assumed_role_arns
add_assumed_role_arns(arns: 'List[str]') → None
Add AWS Roles to list of roles App can assume by ARN. Any Users or Groups assigned to the App are represented as being able to assume the roles.
Args:
arns
(list): list of role ARNs as strings that the user is allowed to assume
add_tag
add_tag(key: 'str', value: 'str' = '') → None
Add a new tag to IdP User.
Args:
key
(str): Key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for Tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only. Defaults to "".
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set custom property value for app.
Property name must be defined for app before calling set_property()
. See example below and IdPPropertyDefinitions.define_app_property
for more information.
Args:
property_name
(str): Name of property
property_value
(Any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
Example: idp = CustomIdPProvider(name="Example IdP", idp_type="example", domain="example.com")
>>> idp.property_definitions.define_app_property(name="my_property", property_type=OAAPropertyType.STRING) >>> app1 = idp.add_app(id="app1", ="App 1") >>> app1.set_property("my_property", "app1 value")
to_dict
to_dict() → dict
IdPPropertyDefinitions
Model for defining custom properties for CustomIdPProvider and its entities (users, groups, domain).
Property definitions define the names for additional entity properties and the expected type.
Attributes:
domain_properties
(dict): property definitions for IdP Domain
user_properties
(dict): property definitions for IdP users
group_properties
(dict): property definitions for IdP groups
__init__
__init__() → None
define_app_assignment_property
define_app_assignment_property(
name: 'str',
property_type: 'OAAPropertyType'
) → None
Define an app assignment custom property
Args:
name
(str): name of property
property_type
(OAAPropertyType): type for property
define_app_property
define_app_property(name: 'str', property_type: 'OAAPropertyType') → None
Define an app custom property
Args:
name
(str): name of property
property_type
(OAAPropertyType): type for property
define_domain_property
define_domain_property(name: 'str', property_type: 'OAAPropertyType') → None
Define a domain custom property.
Args:
name
(str): name of property
property_type
(OAAPropertyType): type for property
define_group_property
define_group_property(name: 'str', property_type: 'OAAPropertyType') → None
Define a group custom property.
Args:
name
(str): name of property
property_type
(OAAPropertyType): type for property
define_user_property
define_user_property(name: 'str', property_type: 'OAAPropertyType') → None
Define a user custom property.
Args:
name
(str): name of property
property_type
(OAAPropertyType): type for property
to_dict
to_dict() → dict
Returns custom IdP property definitions.
validate_property_name
validate_property_name(property_name: 'str', entity_type: 'str') → None
Validate that a property name has been defined for a given IdP entity.
Raises exception if property name has not been previously defined for entity
Args:
property_name
(str): name of property to validate
entity_type
(str): type of entity custom property is for (domain, users, groups)
Raises:
OAATemplateException
: If property name is not defined
Tag
Veza Tag data model.
Args:
key
(str): key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str, optional): Value for tag, will appear in Veza as key:value
. Must be letters, numbers, whitespace and the special characters @,._- only.
Attributes:
key
(str): key for tag, aka name. Must be present and must be letters, numbers or _ (underscore) only.
value
(str): Value for tag, will appear in Veza as key:value
. Must be letters, numbers and the special characters @,._ only.
__init__
__init__(key: 'str', value: 'str' = '') → None
HRISProvider
Class for modeling Human Resource Information Systems (HRIS) Template
HRIS template consists of base information about the HRIS instance, Employees and Groups.
Employees and Groups are tracked in case insensitive dictionaries that can be used to reference entities after creation.
Args:
name
(str): Name for HRIS Instance
hris_type
(str): Type for HRIS. Typically the vendor or product name.
url
(str): Instance URL for HRIS.
Attributes:
employees
(dict[string]): Dictionary of HRISEmployee instances keyed by Employee ID
groups
(dict[string]): Dictionary of HRISGroup instances keyed by Group ID
__init__
__init__(name: 'str', hris_type: 'str', url: 'str')
add_employee
add_employee(
unique_id: 'str',
name: 'str',
employee_number: 'str',
first_name: 'str',
last_name: 'str',
is_active: 'bool',
employment_status: 'str'
) → HRISEmployee
Add a new Employee
Function creates a new HRISEmployee instance and adds it to the HRISProvider.employees
keyed by the unique_id
Args:
unique_id
(str): Unique Identifier for Employee
name
(str): Display name for employee
employee_number
(str): The employee's number that appears in the third-party integration.
first_name
(str): Employee first name
last_name
(str): Employee last name (family name)
is_active
(bool): Boolean for employee active status
employment_status
(str): String representation of employee status, e.g. "ACTIVE", "TERMINATE", "PENDING"
Raises:
OAATemplateException
: Employee with ID already exists
Returns:
HRISEmployee
: Entity for new employee
add_group
add_group(unique_id: 'str', name: 'str', group_type: 'str') → HRISGroup
Add a new Group
Used to represent any subset of employees, such as PayGroup or Team. Employees can be in multiple Groups. Groups can also be members of other groups to create hierarchy.
Some properties of HRISEmployee such as department
must reference an existing HRISGroup by its ID.
Args:
unique_id
(str): Unique ID for group
name
(str): Display name
group_type
(str): Type for group such as "Team", "Department", "Cost Center"
Returns:
HRISGroup
: Entity for new group
get_payload
get_payload() → dict
Get the OAA payload.
Returns the complete OAA template payload for HRIS as serializable dictionary
Returns:
dict
: OAA payload as dictionary
HRISSystem
HRISSystem information
Representation for HRISSystem information. The system information is used to represent additional details for the HRIS Instance.
Args:
name
(str): Name for system Instance
url
(str, optional): URL for instance . Defaults to "". TODO: Is this right?
__init__
__init__(name: 'str', url: 'str' = '')
add_idp_type
add_idp_type(provider_type: 'IdPProviderType') → list[IdPProviderType]
Link HRIS to External IdP of given type
Sets the IdP types (Okta, AzureAD, ect) for Veza to link employee identities too.
Args:
provider_type
(IdPProviderType): Type of IdP for source identities
Raises:
ValueError
: provider_type must be IdPProviderType enum
Returns:
list[IdPProviderType]
: List of configured IdP types
to_dict
to_dict() → dict
HRISEmployee
HRIS Employee Entity
Represents an employee record in the HRIS system. Each employee must have a unique ID to identify it in the payload. This ID is also used to reference one employee to the other for manager hierarchy.
Init variables are all required and must not be empty such as ""
Args:
unique_id
(str): Unique Identifier for Employee
name
(str): Name for employee record.
employee_number
(str): The employee's number that appears in the third-party integration.
first_name
(str): Employee first name
last_name
(str): Employee last name (family name)
is_active
(bool): Boolean for employee active status
employment_status
(str): String representation of employee status, e.g. "ACTIVE", "TERMINATE", "PENDING"
Parameters:
company
(str): The company (or subsidiary) the employee works for.
preferred_name
(str): The employee's preferred first name.
display_full_name
(str): The employee's full name, to use for display purposes.
canonical_name
(str): The employee's canonical name.
username
(str): The employee's username that appears in the integration UI.
email
(str): The employee's work email.
idpid
(str): The ID for this employee on the destination IDP provider used to automatically connect to it, if not supplied email is used.
personal_email
(str): The employee's personal email.
home_location
(str): The employee's home location.
work_location
(str): The employee's work location.
cost_center
(str): The cost center ID (Group ID) that the employee is in.
department
(str): The department ID (Group ID) that the employee is in.
managers
(str): The employee IDs of the employee's managers.
groups
(str): The IDs of groups this user is in
start_date
(str): The date that the employee started working. RFC3339 timestamp.
termination_date
(str): The employee's termination date. RFC3339 timestamp.
job_title
(str): The title of the employee.
employment_typ
(str): The employee's type of employment. For example: FULL_TIME, PART_TIME, INTERN, CONTRACTOR, FREELANCE.
primary_time_zone
(str): The time zone which the employee primarily lives.
Raises:
ValueError
: Any of the required arguments are empty.
__init__
__init__(
unique_id: 'str',
name: 'str',
employee_number: 'str',
first_name: 'str',
last_name: 'str',
is_active: 'bool',
employment_status: 'str'
)
add_group
add_group(group_id: 'str') → None
Add employee to group
Adds employee to a group by the group ID. Group must also be defined for HRISInstance with HRISProvider.add_group()
Args:
group_id
(str): Unique ID of HRISGroup to add employee too
add_manager
add_manager(manager_id: 'str') → None
Add manager to Employee
Adds a manager to the employee by the manager's HRISEmployee instance unique ID. Manger employee record must also exist.
Args:
manager_id
(str): Unique ID for manager HRISEmployee instance
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set Employee custom property value
Property name must be defined for employee before calling set_property
Args:
property_name
(str): Name of property
property_value
(any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
to_dict
to_dict() → dict
Output employee to dictionary for payload.
HRISGroup
HRIS Group
Represents any group of employees in the HRIS system. HRISGroups can be used to represent teams, departments, cost centers or any organizational unit. Each group has a type to make searching and grouping easier.
Group's Unique ID must be unique across all group types.
Args:
unique_id
(str): Unique ID for group
name
(str): Display name
group_type
(str): Type for group such as "Team", "Department", "Cost Center"
__init__
__init__(unique_id: 'str', name: 'str', group_type: 'str')
set_property
set_property(
property_name: 'str',
property_value: 'any',
ignore_none: 'bool' = False
) → None
Set HRIS Group custom property value
Property name must be defined for group before calling set_property
Args:
property_name
(str): Name of property
property_value
(any): Value for property, type should match OAAPropertyType for property definition
ignore_none
(bool, optional): Do not set property if value is None. Defaults to False.. Defaults to False.
Raises:
OAATemplateException
: If property with property_name
is not defined.
to_dict
to_dict() → dict
Dictionary output for inclusion in payload
HRISPropertyDefinitions
__init__
__init__()
define_employee_property
define_employee_property(name: 'str', property_type: 'OAAPropertyType') → None
define_group_property
define_group_property(name: 'str', property_type: 'OAAPropertyType') → None
define_system_property
define_system_property(name: 'str', property_type: 'OAAPropertyType') → None
to_dict
to_dict() → dict
validate_name
validate_name(name: 'str') → None
Check property name for valid characters
Raises an exception if the name string does not match required pattern. Name must start with a character and can only contain letters and _ character.
Args:
name
(str): name of property to validate
Raises:
OAATemplateException
: Name is not a string
OAATemplateException
: Name contains invalid characters or does not start with a letter