Skip to content

plugin sdk restricted configuration

Andre Lafleur edited this page Feb 3, 2026 · 2 revisions

About plugin 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.

Overview

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

When to Use

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

AdminConfigXml

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.

Access Control

Reading AdminConfigXml:

  • Plugin itself (running in plugin role context)
  • Administrator users

Writing AdminConfigXml:

  • Plugin itself (running in plugin role context)
  • Administrator users

Reading AdminConfigXml

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);
    }
}

Writing AdminConfigXml

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");
}

AdminConfigXml Example

[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 (Secure Credentials)

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.

Access Control

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.

Storing Credentials

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);
    }
}

Setting Credentials from Config Page

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;
}

Deleting Credentials

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");
}

Complete Example: External System Integration

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);
    }
}

Request Handler Example: Credential Management

[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;
}

Error Handling

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");
}

Graceful Degradation

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();
}

Comparison with SpecificConfiguration

Example Configuration Strategy

// 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);
}

See also

Security Center SDK

  • Security Center SDK Developer Guide Overview of the SDK framework and how to build integrations with Security Center.

    • Platform SDK

      • Overview Introduction to the Platform SDK and core concepts.
      • Connecting to Security Center Step-by-step guide for connecting and authenticating with the SDK.
      • SDK Certificates Details certificates, licensing, and connection validation.
      • Referencing SDK Assemblies Best practices for referencing assemblies and resolving them at runtime.
      • SDK Compatibility Guide Understanding backward compatibility and versioning in the SDK.
      • 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.
      • Transactions Covers batching operations for performance and consistency.
      • Events Subscribing to real-time system events.
      • Actions Sending actions to Security Center.
      • Security Desk Displaying content on monitors, reading tiles, sending tasks, and messaging operators.
      • Custom Events Defining, raising, and subscribing to custom events.
      • ReportManager Querying entities and activity data from Security Center.
      • ReportManager Query Reference Complete reference of query types, parameters, and response formats.
      • Privileges Checking, querying, and setting user privileges.
      • Partitions Entity organization and access control through partitions.
      • Logging How to configure logging, diagnostics, and debug methods.
    • Plugin SDK

    • Workspace SDK

    • Macro SDK

      • Overview How macros work, creating and configuring macro entities, automation, and monitoring.
      • Developer Guide Developing macro code with the UserMacro class and Security Center SDK.

Web SDK Developer Guide

  • 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.
  • About access control in the Web SDK Concepts, relationships, and common access-control operations.
  • About video in the Web SDK Concepts, relationships, configuration, and common video operations.
  • Users and user groups Creating users, managing group membership, and assigning privileges.
  • 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.
  • Tasks Listing and executing saved report tasks.
  • Macros Monitoring currently running macros.
  • Custom Entity Types Listing, retrieving, and deleting custom entity type descriptors.
  • System Endpoints License usage, web tokens, and exception handling.
  • 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 Developer Guide


Web Player Developer Guide

  • Developer Guide Complete guide to integrating GWP for live and playback video streaming.
  • API Reference Full API documentation with interfaces, methods, properties, and events.
  • Sample Application Comprehensive demo showcasing all GWP features with timeline and PTZ controls.
  • Multiplexing Sample Multi-camera grid demo using a shared WebSocket connection.

Clone this wiki locally