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.
Code goals
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.
High-level code flow
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.
Customizing the example
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.
Example: custom application
The following code provides a template and examples for creating a new application integration using the Veza.OAA SDK.
example_app.cs
usingRestSharp;usingRestSharp.Authenticators;usingVeza.OAA;usingVeza.OAA.Application;usingVeza.OAA.Client;usingNLog;namespaceExample.Integrations{ /// <summary> /// Example application group object /// </summary>publicclassAppGroup {publicstring Name { get; set; }publicstring Type { get; set; }publicstring CreatedAt { get; set; }publicList<string> Users { get; set; } } /// <summary> /// Example application resource object /// </summary>publicclassAppResource {publicstring Name { get; set; }publicint SiteId { get; set; }publicstring ResourceType { get; set; } } /// <summary> /// Example application role object /// </summary>publicclassAppRole {publicstring Name { get; set; }publicint SystemId { get; set; }publicstring CreatedAt { get; set; }publicList<string> Users { get; set; }publicList<string> Resources { get; set; } }publicclassExampleApp { // Optional NLog logger; can be replaced with any standard logging interfaceprivatestaticLogger logger =LogManager.GetCurrentClassLogger(); // The base64-encoded logo for the application (displayed on the Veza UI)publicstring LogoBase64; // The name of the provider for the applicationpublicstring ProviderName; // The custom application object for the example application // This object is used to build the application payload for VezapublicCustomApplication CustomApp { get; set; } // The example application client for the example application API (not shown in this example)privateExampleAppClient AppClient { get; set; }#region"Construction"publicExampleApp() { // Instantiate the example application CustomApp =newCustomApplication( 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 informationCustomApp.AddCustomPermission( name:"Uncategorized", permissions:newList<Permission> { Permission.Uncategorized } ); // define custom properties for the example applicationDefineCustomProperties(); // 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>privatevoidDefineCustomProperties() {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>privatevoidDiscoverUsers() {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 payloadforeach (Dictionary<string,string> u in users) { // Create a new user object and add it to the application payloadUser 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>privatevoidDiscoverGroups() {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 objectsList<AppGroup> groups =AppClient.GetGroups(); // Iterate over the groups response and add them to the application payloadforeach (AppGroup g in groups) { // Create a new group object and add it to the application payloadGroup 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 groupforeach (string user ing.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>privatevoidDiscoverResources() {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 objectsList<AppResource> resources =AppClient.GetResources();foreach (AppResource r in resources) { // Create a resource object and add it to the application payloadResource 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>privatevoidDiscoverRoles() {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 objectsList<AppRole> roles =AppClient.GetRoles();foreach (Role role in roles) { // create a new role object and add it to the application payloadRole 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 applicationforeach (string user inrole.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> /// <paramname="exampleApiKey">The API key for the example application API</param> /// <paramname="exampleUrl">The URL for the example application API</param> /// <returns></returns>publicvoidDiscover(string exampleApiKey,string exampleUrl) { // instantiate an API client for the example application AppClient =newExampleAppClient(exampleApiKey, exampleUrl);Logger.Info("Beginning example application discovery"); // discover the example application componentsDiscoverUsers();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> /// <paramname="exampleApiKey">The API key for the Example application API</param> /// <paramname="exampleUrl">The URL for the Example application API</param> /// <paramname="vezaApiKey">The API key for the Veza tenant</param> /// <paramname="vezaUrl">The URL for the Veza tenant</param> /// <returns></returns>publicasyncTaskRun(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 applicationDiscover(exampleApiKey, exampleUrl); // Get or create the Veza provider for the payloadLogger.Debug("Checking Veza for an existing provider for the example application");Sdk.Client.ApiClient.VezaApiResponse providerResponse =awaitoaaClient.GetProvider(ProviderName); // Create the provider if none existsif (providerResponse isnull) {Logger.Info("No Veza provider exists; creating provider");awaitoaaClient.CreateProvider(ProviderName,"application", base64_icon: LogoBase64); } // Push OAA payload to VezaLogger.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 }}