Skip to content

feat(system): add secrets module#41

Merged
cesarcoatl merged 1 commit into
8.3from
feat/system/secrets
Jun 10, 2026
Merged

feat(system): add secrets module#41
cesarcoatl merged 1 commit into
8.3from
feat/system/secrets

Conversation

@cesarcoatl

@cesarcoatl cesarcoatl commented Jun 10, 2026

Copy link
Copy Markdown
Member

remove runUpdateQuery from db module

Summary by Sourcery

Introduce a new system secrets API and supporting types while removing an obsolete database update helper.

New Features:

  • Add a system.secrets module that exposes functions to encrypt, decrypt, list providers and secrets, and read secret values.
  • Add common and gateway-side secrets support classes, including PyPlaintext, SecretMeta, SecretProviderMeta, and Plaintext for handling secret data.
  • Introduce a generic ContextManager base class in the scripting ABC package for use with context-managed objects like PyPlaintext.

Enhancements:

  • Remove the deprecated runUpdateQuery helper from the db module and its type stubs to streamline the database scripting API.

remove runUpdateQuery from db module
@sourcery-ai

sourcery-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduces a new secrets management system API (Python-level wrappers and Java interop types), adds a generic ContextManager base class for context-managed resources, wires in stubs for type checking, and removes the deprecated db.runUpdateQuery API from both implementation and stubs.

Sequence diagram for system.secrets decrypt and readSecretValue

sequenceDiagram
    participant Caller
    participant system_secrets
    participant Plaintext
    participant PyPlaintext

    Caller->>system_secrets: decrypt(json)
    system_secrets->>Plaintext: fromString(json)
    Plaintext-->>system_secrets: Plaintext
    system_secrets->>PyPlaintext: __init__(Plaintext)
    PyPlaintext-->>Caller: PyPlaintext

    Caller->>system_secrets: readSecretValue(providerName, secretName)
    system_secrets->>Plaintext: __init__()
    Plaintext-->>system_secrets: Plaintext
    system_secrets->>PyPlaintext: __init__(Plaintext)
    PyPlaintext-->>Caller: PyPlaintext
Loading

File-Level Changes

Change Details Files
Remove the legacy runUpdateQuery helper from the system.db API and its typing stubs.
  • Drop runUpdateQuery from all exports in the db module.
  • Delete the runUpdateQuery function implementation from the db runtime module.
  • Remove the runUpdateQuery signature from the db type stub file.
src/system/db.py
stubs/stubs/system/db.pyi
Introduce a reusable ContextManager base type in the scripting ABCs for use by context-managed script objects.
  • Export ContextManager from the abc package all list.
  • Implement a ContextManager PyObject subclass with enter and exit methods in the runtime module.
  • Add the corresponding ContextManager class definition to the abc type stub file.
src/com/inductiveautomation/ignition/common/script/abc/__init__.py
stubs/stubs/com/inductiveautomation/ignition/common/script/abc/__init__.pyi
Add a new system.secrets scripting module that exposes encryption/decryption and secret provider/secret listing and reading functions.
  • Create system.secrets runtime module with decrypt, encrypt, getProviders, getSecrets, and readSecretValue functions delegating to the Java Plaintext and PyPlaintext abstractions.
  • Define all in system.secrets so the new functions are exported as part of the scripting API.
  • Use placeholder implementations that return fixed structures or print parameters, suitable for stubbing/testing.
src/system/secrets.py
stubs/stubs/system/secrets.pyi
Define common-side secrets support types, including a context-managed PyPlaintext wrapper and metadata records for secrets and providers, with matching stubs.
  • Introduce PyPlaintext class that extends ContextManager and wraps a gateway Plaintext, with clear/getSecretsAsBytes/getSecretsAsString methods (currently stubbed).
  • Create SecretMeta and SecretProviderMeta record-like classes with simple field storage and accessors for name, description, and type.
  • Add corresponding type stub definitions for PyPlaintext, SecretMeta, and SecretProviderMeta.
src/com/inductiveautomation/ignition/common/secrets/__init__.py
stubs/stubs/com/inductiveautomation/ignition/common/secrets/__init__.pyi
Add gateway-side Plaintext secrets primitive and its typing stub used by higher-level secrets APIs.
  • Implement Plaintext Java-interop surrogate class with clear, close, getAsString, getBytes, and factory methods fromBytes/fromString (methods mostly stubbed).
  • Provide matching Plaintext type stub with the same constructor and method signatures.
src/com/inductiveautomation/ignition/gateway/secrets/__init__.py
stubs/stubs/com/inductiveautomation/ignition/gateway/secrets/__init__.pyi

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues, and left some high level feedback:

  • PyPlaintext currently ignores the passed Plaintext instance and leaves clear/getSecretsAsBytes/getSecretsAsString unimplemented; consider storing the Plaintext on the instance and delegating these methods to it so that usage of the secrets API behaves as expected.
  • The new ContextManager.enter/exit implementations only print and do not manage or return a context object; you may want to either remove the side-effect logging or have enter return self (and exit perform cleanup) so it behaves like a standard context manager.
  • In Plaintext.fromBytes and Plaintext.fromString, the parameter names bytes and str shadow built-in names; renaming these (e.g., to data or value) would avoid potential confusion and accidental misuse.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- PyPlaintext currently ignores the passed Plaintext instance and leaves clear/getSecretsAsBytes/getSecretsAsString unimplemented; consider storing the Plaintext on the instance and delegating these methods to it so that usage of the secrets API behaves as expected.
- The new ContextManager.__enter__/__exit__ implementations only print and do not manage or return a context object; you may want to either remove the side-effect logging or have __enter__ return self (and __exit__ perform cleanup) so it behaves like a standard context manager.
- In Plaintext.fromBytes and Plaintext.fromString, the parameter names `bytes` and `str` shadow built-in names; renaming these (e.g., to `data` or `value`) would avoid potential confusion and accidental misuse.

## Individual Comments

### Comment 1
<location path="src/com/inductiveautomation/ignition/common/script/abc/__init__.py" line_range="286-295" />
<code_context>
         pass
+
+
+class ContextManager(PyObject):
+    def __init__(self):
+        # type: () -> None
+        super(ContextManager, self).__init__()
+
+    def __enter__(self):
+        # type: () -> None
+        print("Enter")
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        # type: (object, object, object) -> None
+        print("Exit")
</code_context>
<issue_to_address>
**issue (bug_risk):** ContextManager’s __enter__/__exit__ behavior is surprising for a reusable base class.

`__enter__` currently returns `None` and both methods print on entry/exit. For a reusable base class, `with ContextManager() as cm:` (or via subclasses like `PyPlaintext`) will bind `cm` to `None`, which is likely to cause subtle bugs, and the prints will create noisy logs. Prefer returning `self` from `__enter__` and removing these prints, or make this an abstract shell that subclasses override.
</issue_to_address>

### Comment 2
<location path="src/system/secrets.py" line_range="81-90" />
<code_context>
+    ]
+
+
+def getSecrets(providerName):
+    # type: (Union[str, unicode]) -> List[SecretMeta]
+    """Returns a list of objects representing all secrets available for
+    the named Secret Provider.
+
+    Each list entry includes the name of the secret.
+
+    Args:
+        providerName: The name of the Secret Provider to fetch secrets
+            from.
+
+    Returns:
+        A list of SecretMeta instances that represent all secret names.
+    """
+    print(providerName)
+    return [SecretMeta("SecretName")]
+
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Printing providerName in getSecrets introduces unexpected side effects.

Having `getSecrets` print `providerName` forces all callers to get unexpected console output, which is unusual for an accessor and may break scripts that rely on clean stdout. If this is only for debugging, remove the `print` or replace it with a proper, configurable logging mechanism.

Suggested implementation:

```python
def getSecrets(providerName):
    # type: (Union[str, unicode]) -> List[SecretMeta]
    """Returns a list of objects representing all secrets available for
    the named Secret Provider.

    Each list entry includes the name of the secret.

    Args:
        providerName: The name of the Secret Provider to fetch secrets
            from.

    Returns:
        A list of SecretMeta instances that represent all secret names.
    """
    return [SecretMeta("SecretName")]

```

```python
__all__ = [
    "decrypt",
    "encrypt",
    "getProviders",
    "getSecrets",

```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +286 to +295
class ContextManager(PyObject):
def __init__(self):
# type: () -> None
super(ContextManager, self).__init__()

def __enter__(self):
# type: () -> None
print("Enter")

def __exit__(self, exc_type, exc_val, exc_tb):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): ContextManager’s enter/exit behavior is surprising for a reusable base class.

__enter__ currently returns None and both methods print on entry/exit. For a reusable base class, with ContextManager() as cm: (or via subclasses like PyPlaintext) will bind cm to None, which is likely to cause subtle bugs, and the prints will create noisy logs. Prefer returning self from __enter__ and removing these prints, or make this an abstract shell that subclasses override.

Comment thread src/system/secrets.py
Comment on lines +81 to +90
def getSecrets(providerName):
# type: (Union[str, unicode]) -> List[SecretMeta]
"""Returns a list of objects representing all secrets available for
the named Secret Provider.

Each list entry includes the name of the secret.

Args:
providerName: The name of the Secret Provider to fetch secrets
from.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (bug_risk): Printing providerName in getSecrets introduces unexpected side effects.

Having getSecrets print providerName forces all callers to get unexpected console output, which is unusual for an accessor and may break scripts that rely on clean stdout. If this is only for debugging, remove the print or replace it with a proper, configurable logging mechanism.

Suggested implementation:

def getSecrets(providerName):
    # type: (Union[str, unicode]) -> List[SecretMeta]
    """Returns a list of objects representing all secrets available for
    the named Secret Provider.

    Each list entry includes the name of the secret.

    Args:
        providerName: The name of the Secret Provider to fetch secrets
            from.

    Returns:
        A list of SecretMeta instances that represent all secret names.
    """
    return [SecretMeta("SecretName")]
__all__ = [
    "decrypt",
    "encrypt",
    "getProviders",
    "getSecrets",

@cesarcoatl cesarcoatl merged commit 5b567bb into 8.3 Jun 10, 2026
5 checks passed
@cesarcoatl cesarcoatl deleted the feat/system/secrets branch June 10, 2026 21:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant