-
Notifications
You must be signed in to change notification settings - Fork 4
plugin sdk restricted configuration
The RestrictedConfiguration API provides secure storage for plugin credentials and admin-only configuration. This is separate from SpecificConfiguration and has stronger access controls.
Role.RestrictedConfiguration
├── AdminConfigXml (Admin-only XML configuration)
└── Private Values (Secure credential storage - SecureString)
├── SetPrivateValue()
├── GetPrivateValue()
├── TryGetPrivateValue()
└── DeletePrivateValue()
Key differences from SpecificConfiguration:
| Feature | SpecificConfiguration | RestrictedConfiguration |
|---|---|---|
| Purpose | General plugin settings | Credentials and admin config |
| Format | String (typically JSON) | XML (AdminConfigXml), SecureString (Private Values) |
| Access | Anyone with role access | Plugin itself or administrators only |
| Storage | Plaintext in database | Plaintext (AdminConfigXml), SecureString (Private Values) |
| Use Case | URLs, timeouts, options | Admin-only settings, passwords, API keys, certificates |
Use RestrictedConfiguration for:
- ✅ Passwords and API keys
- ✅ Authentication tokens
- ✅ Certificates and private keys
- ✅ Admin-only configuration
- ✅ Sensitive connection strings
Use SpecificConfiguration for:
- ✅ Server URLs and endpoints
- ✅ Polling intervals and timeouts
- ✅ Feature flags and options
- ✅ Non-sensitive settings
- ✅ User-configurable options
Administrator-only configuration storage.
Storage format: The property stores a string value. While technically you can store any string format, the API is designed for XML and includes special handling for XML serialization (carriage return normalization for XML deserializers).
Recommended: Use XML format as intended by the API design.
Reading AdminConfigXml:
- Plugin itself (running in plugin role context)
- Administrator users
Writing AdminConfigXml:
- Plugin itself (running in plugin role context)
- Administrator users
protected override void OnPluginLoaded()
{
try
{
var role = Engine.GetEntity<Role>(PluginGuid);
string adminXml = role.RestrictedConfiguration.AdminConfigXml;
if (!string.IsNullOrEmpty(adminXml))
{
var adminConfig = DeserializeAdminConfig(adminXml);
ApplyAdminConfiguration(adminConfig);
}
}
catch (SdkException ex) when (ex.Error == SdkError.UnsufficientPrivilege)
{
// Not plugin or not administrator
Logger.TraceWarning("Insufficient privilege to read AdminConfigXml");
}
catch (SdkException ex) when (ex.Error == SdkError.NotConnected)
{
Logger.TraceError("Engine is not connected");
}
catch (SdkException ex) when (ex.Error == SdkError.InvalidOperation)
{
// Can only access AdminConfigXml of Plugin roles
Logger.TraceError("AdminConfigXml only available for Plugin roles");
}
}
private AdminConfiguration DeserializeAdminConfig(string xml)
{
using (var reader = new StringReader(xml))
{
var serializer = new XmlSerializer(typeof(AdminConfiguration));
return (AdminConfiguration)serializer.Deserialize(reader);
}
}private void UpdateAdminConfiguration(AdminConfiguration adminConfig)
{
Engine.TransactionManager.ExecuteTransaction(() =>
{
var role = Engine.GetEntity<Role>(PluginGuid);
using (var writer = new StringWriter())
{
var serializer = new XmlSerializer(typeof(AdminConfiguration));
serializer.Serialize(writer, adminConfig);
role.RestrictedConfiguration.AdminConfigXml = writer.ToString();
}
});
Logger.TraceInformation("Admin configuration updated");
}[XmlRoot("AdminConfiguration")]
public class AdminConfiguration
{
public string LicenseServer { get; set; }
public bool DebugMode { get; set; }
public int MaxConcurrentConnections { get; set; }
public List<string> AllowedIpAddresses { get; set; }
}
// Usage
var adminConfig = new AdminConfiguration
{
LicenseServer = "license.company.com",
DebugMode = false,
MaxConcurrentConnections = 100,
AllowedIpAddresses = new List<string> { "192.168.1.0/24" }
};
UpdateAdminConfiguration(adminConfig);Private values store credentials as SecureString with restricted access.
Important
The returned SecureString is read-only. Always dispose it after use to clear sensitive data from memory.
Reading Private Values:
- ✅ Only the plugin itself (verified by GUID match)
- ❌ Administrators CANNOT read
- ❌ Other plugins CANNOT read
Writing/Deleting Private Values:
- ✅ Plugin itself
- ✅ Administrators (but cannot read back)
This allows administrators to SET credentials without being able to READ them.
protected override void OnPluginLoaded()
{
// Check if credentials exist
var role = Engine.GetEntity<Role>(PluginGuid);
if (!role.RestrictedConfiguration.TryGetPrivateValue("ApiKey", out var apiKey))
{
Logger.TraceWarning("API key not configured");
ModifyPluginState(new PluginStateEntry("Configuration",
"API key not configured") { IsWarning = true });
return;
}
try
{
// Use the credential
string apiKeyValue = SecureStringToString(apiKey);
InitializeApiClient(apiKeyValue);
ModifyPluginState(new PluginStateEntry("Configuration", "API key loaded"));
}
finally
{
// Always dispose SecureString
apiKey?.Dispose();
}
}
private string SecureStringToString(SecureString secureString)
{
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.SecureStringToGlobalAllocUnicode(secureString);
return Marshal.PtrToStringUni(ptr);
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}Custom configuration page can set credentials (typically entered by administrator):
// In custom config page or request handler
private void SaveCredentials(string username, string password)
{
Engine.TransactionManager.ExecuteTransaction(() =>
{
var role = Engine.GetEntity<Role>(PluginGuid);
// Convert string to SecureString
using (var securePassword = StringToSecureString(password))
{
role.RestrictedConfiguration.SetPrivateValue("Username",
StringToSecureString(username));
role.RestrictedConfiguration.SetPrivateValue("Password",
securePassword);
}
});
Logger.TraceInformation("Credentials saved successfully");
}
private SecureString StringToSecureString(string str)
{
var secureString = new SecureString();
foreach (char c in str)
{
secureString.AppendChar(c);
}
secureString.MakeReadOnly();
return secureString;
}private void ClearCredentials()
{
Engine.TransactionManager.ExecuteTransaction(() =>
{
var role = Engine.GetEntity<Role>(PluginGuid);
role.RestrictedConfiguration.DeletePrivateValue("Username");
role.RestrictedConfiguration.DeletePrivateValue("Password");
role.RestrictedConfiguration.DeletePrivateValue("ApiKey");
});
Logger.TraceInformation("Credentials cleared");
}public class ExternalSystemPlugin : Plugin
{
private HttpClient m_httpClient;
private string m_apiKey;
protected override void OnPluginLoaded()
{
// Load configuration
if (!LoadCredentials())
{
ModifyPluginState(new PluginStateEntry("Configuration",
"Credentials not configured") { IsError = true });
return;
}
// Initialize HTTP client with credentials
m_httpClient = new HttpClient();
m_httpClient.DefaultRequestHeaders.Add("X-API-Key", m_apiKey);
ModifyPluginState(new PluginStateEntry("Configuration", "Configured"));
}
private bool LoadCredentials()
{
try
{
var role = Engine.GetEntity<Role>(PluginGuid);
if (!role.RestrictedConfiguration.TryGetPrivateValue("ApiKey", out var apiKey))
{
Logger.TraceWarning("API key not found in RestrictedConfiguration");
return false;
}
try
{
m_apiKey = SecureStringToString(apiKey);
if (string.IsNullOrWhiteSpace(m_apiKey))
{
Logger.TraceError("API key is empty");
return false;
}
return true;
}
finally
{
apiKey?.Dispose();
}
}
catch (SdkException ex) when (ex.Error == SdkError.UnsufficientPrivilege)
{
Logger.TraceError("Insufficient privilege to read credentials");
return false;
}
catch (Exception ex)
{
Logger.TraceError(ex, "Failed to load credentials");
return false;
}
}
private string SecureStringToString(SecureString secureString)
{
IntPtr ptr = IntPtr.Zero;
try
{
ptr = Marshal.SecureStringToGlobalAllocUnicode(secureString);
return Marshal.PtrToStringUni(ptr);
}
finally
{
if (ptr != IntPtr.Zero)
Marshal.ZeroFreeGlobalAllocUnicode(ptr);
}
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
// Clear sensitive data from memory
m_apiKey = null;
m_httpClient?.Dispose();
}
base.Dispose(disposing);
}
}[Serializable]
public class SetCredentialsRequest
{
public string Username { get; set; }
public string Password { get; set; }
public string ApiKey { get; set; }
}
[Serializable]
public class SetCredentialsResponse
{
public bool Success { get; set; }
public string Error { get; set; }
}
// In Plugin class
protected override void OnPluginLoaded()
{
// Use Plugin's protected helper method to register handler
AddRequestHandler<SetCredentialsRequest, SetCredentialsResponse>(HandleSetCredentials);
}
private SetCredentialsResponse HandleSetCredentials(SetCredentialsRequest request)
{
try
{
Engine.TransactionManager.ExecuteTransaction(() =>
{
var role = Engine.GetEntity<Role>(PluginGuid);
if (!string.IsNullOrEmpty(request.Username))
{
using (var secureUsername = StringToSecureString(request.Username))
{
role.RestrictedConfiguration.SetPrivateValue("Username", secureUsername);
}
}
if (!string.IsNullOrEmpty(request.Password))
{
using (var securePassword = StringToSecureString(request.Password))
{
role.RestrictedConfiguration.SetPrivateValue("Password", securePassword);
}
}
if (!string.IsNullOrEmpty(request.ApiKey))
{
using (var secureApiKey = StringToSecureString(request.ApiKey))
{
role.RestrictedConfiguration.SetPrivateValue("ApiKey", secureApiKey);
}
}
});
Logger.TraceInformation("Credentials updated successfully");
// Reload credentials and reconnect
ReloadCredentialsAndReconnect();
return new SetCredentialsResponse { Success = true };
}
catch (Exception ex)
{
Logger.TraceError(ex, "Failed to set credentials");
return new SetCredentialsResponse
{
Success = false,
Error = ex.Message
};
}
}
private SecureString StringToSecureString(string str)
{
var secureString = new SecureString();
foreach (char c in str)
{
secureString.AppendChar(c);
}
secureString.MakeReadOnly();
return secureString;
}The RestrictedConfiguration API throws SdkException for standard SDK error conditions. Common SdkError values that you should handle are:
- NotConnected: the engine or proxy is not connected.
- InvalidOperation: operation not valid for the target role type (for example, accessing plugin-only features on a non-plugin role).
- UnsufficientPrivilege: caller lacks the required privileges for the operation.
- SdkRequestError: wrapped exceptions and transaction failures.
Guidance:
- Use TryGetPrivateValue when possible to avoid exceptions on missing keys.
- Always check engine connectivity before calling APIs that require a connected engine.
- When running inside a plugin role, many privilege checks are relaxed for the owning plugin; other callers (Config Tool, clients, other roles) will encounter standard privilege errors.
- Avoid referencing internal implementation details or private method names; treat the above error codes as the contract to reason about.
A concise example of how to handle errors:
try
{
if (role.RestrictedConfiguration.TryGetPrivateValue("ApiKey", out var secure))
{
try { /* use secure */ }
finally { secure?.Dispose(); }
}
}
catch (SdkException ex) when (ex.Error == SdkError.NotConnected)
{
Logger.TraceError("RestrictedConfiguration: engine not connected");
}
catch (SdkException ex) when (ex.Error == SdkError.UnsufficientPrivilege)
{
Logger.TraceWarning("RestrictedConfiguration: insufficient privileges");
}
catch (SdkException ex)
{
Logger.TraceError(ex, "RestrictedConfiguration operation failed");
}protected override void OnPluginLoaded()
{
if (!LoadCredentials())
{
ModifyPluginState(new PluginStateEntry("Configuration",
"Credentials not configured. Please configure credentials in Config Tool.")
{ IsWarning = true });
// Continue running in limited mode
m_operatingMode = OperatingMode.Limited;
return;
}
// Full functionality available
m_operatingMode = OperatingMode.Full;
InitializeFullFunctionality();
}// SpecificConfiguration - General settings (JSON recommended)
public class PluginConfig
{
public string ServerUrl { get; set; }
public int PollingInterval { get; set; }
public bool EnableDebugLogging { get; set; }
}
// RestrictedConfiguration.AdminConfigXml - Admin-only (XML)
[XmlRoot("AdminConfig")]
public class AdminConfig
{
public List<string> AllowedIpRanges { get; set; }
public int MaxRetries { get; set; }
}
// RestrictedConfiguration.PrivateValues - Credentials (SecureString)
// - Username
// - Password
// - ApiKey
// - Certificate
protected override void OnPluginLoaded()
{
// Load all configurations
var config = LoadConfiguration(); // SpecificConfiguration (JSON)
var adminConfig = LoadAdminConfiguration(); // RestrictedConfiguration.AdminConfigXml (XML)
var credentials = LoadCredentials(); // RestrictedConfiguration.PrivateValues (SecureString)
// Apply all settings
ApplyConfiguration(config, adminConfig, credentials);
}- Plugin SDK Configuration - General plugin configuration
- Plugin SDK Overview - Plugin fundamentals
- Plugin SDK Lifecycle - Plugin initialization and disposal
- Plugin SDK Request Manager - Handling configuration requests from clients
-
Security Center SDK Developer Guide Overview of the SDK framework and how to build integrations with Security Center.
-
Platform SDK
- Platform SDK Overview Introduction to the Platform SDK and core concepts.
- SDK Certificates Details certificates, licensing, and connection validation.
- Entity Guide Explains the core entity model, inheritance, and how to work with entities.
- Entity Cache Guide Describes the engine's local entity cache and synchronization.
- SDK Transactions Covers batching operations for performance and consistency.
- ReportManager Querying entities and activity data from Security Center.
- Events and Actions Subscribing to events and handling actions.
- Logging with the Genetec SDK How to configure logging, diagnostics, and debug methods.
- Referencing SDK Assemblies Best practices for referencing assemblies and resolving them at runtime.
- SDK Compatibility Guide Understanding backward compatibility and versioning in the SDK.
-
Plugin SDK
- Plugin SDK Overview Introduction to plugin architecture and capabilities.
- Plugin SDK Certificates SDK certificate requirements for plugin roles.
- Plugin SDK Lifecycle Initialization and disposal patterns.
- Plugin SDK Threading Threading model, QueueUpdate, and async patterns.
- Plugin SDK Configuration Configuration storage and monitoring.
- Plugin SDK Restricted Configuration Secure credential storage and admin-only configuration.
- Plugin SDK Database Database integration and schema management.
- Plugin SDK Events Event subscription and handling.
- Plugin SDK Queries Query processing and response handling.
- Plugin SDK Request Manager Request/response communication with clients.
- Plugin SDK Entity Ownership Understanding plugin-owned entities, running state management, and ownership release.
- Plugin SDK Entity Mappings Using EntityMappings for plugin-specific configuration and external system integration.
- Plugin SDK State Management Reporting plugin health and diagnostics.
- Plugin SDK Server Management High availability and server failover.
- Custom Privileges Defining and enforcing custom privileges.
- Resolving Non-SDK Assemblies Handling third-party dependencies in plugins and workspace modules.
- Deploying Plugins Registering and deploying plugins and workspace modules.
-
- Macro SDK Developer Guide Complete guide to creating server-side automation scripts in Security Center using C#.
- Getting Started Setup, authentication, and basic configuration for the Web SDK.
- Referencing Entities Entity discovery, search capabilities, and parameter formats.
- Entity Operations CRUD operations, multi-value fields, and method execution.
- Partitions Managing partitions, entity membership, and user access control.
- Custom Fields Creating, reading, writing, and filtering custom entity fields.
- Custom Card Formats Managing custom credential card format definitions.
- Actions Control operations for doors, cameras, macros, and notifications.
- Events and Alarms Real-time event monitoring, alarm monitoring, and custom events.
- Incidents Incident management, creation, and attachment handling.
- Reports Activity reports, entity queries, and historical data retrieval.
- Performance Guide Optimization tips and best practices for efficient API usage.
- Reference Entity GUIDs, EntityType enumeration, and EventType enumeration.
- Under the Hood Technical architecture, query reflection, and SDK internals.
- Troubleshooting Common error resolution and debugging techniques.
- Media Gateway Guide Setup and configuration of the Media Gateway role for video streaming.
- Web Player Guide Complete guide to integrating GWP for live and playback video streaming.
- Web Player API Reference Full API documentation with interfaces, methods, properties, and events.
- Web Player Sample Application Comprehensive demo showcasing all GWP features with timeline and PTZ controls.
- Genetec Web Player Multiplexing Sample Multi-camera grid demo using a shared WebSocket connection.