fix: use plain open() when creating empty config files#260
Merged
baxeaz merged 1 commit intoMay 25, 2026
Merged
Conversation
The configuration manager creates an empty <AdaptorName>.json on first access if one doesn't exist. This previously went through secure_open(), which on Windows replaces the file's DACL with a non-inheriting ACE that grants only the current owner SID FILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE. That overrides the ACLs a user would otherwise expect to be inherited from the parent directory and can produce surprising access errors when the same config file is accessed by a different account than the one that created it. There's no clear reason adaptor config files specifically need owner-only DACLs; the parent directory's ACLs already control access in the locations these files are written to. Switch the create path to plain open() and let normal filesystem permission rules apply. Signed-off-by: Brian Axelson <86568017+baxeaz@users.noreply.github.com>
mwiebe
approved these changes
May 22, 2026
Cherie-Chen
approved these changes
May 22, 2026
epmog
approved these changes
May 25, 2026
Merged
Cherie-Chen
added a commit
to Cherie-Chen/deadline-cloud-for-unreal-engine-cheriech
that referenced
this pull request
May 26, 2026
….9.0 Bump minimum required versions of OpenJobDescription dependencies to the latest releases available on PyPI: openjd-adaptor-runtime: 0.8.1 -> 0.9.4 openjd-model: 0.8.1 -> 0.9.0 The adaptor-runtime bump picks up the Windows DACL fix from OpenJobDescription/openjd-adaptor-runtime-for-python#260, which prevents the per-adaptor JSON config file from being permanently locked to the SID of whichever account first created it. This avoids PermissionError on multi-user Unreal worker hosts. openjd-model 0.9.1 has a GitHub release tag but was never published to PyPI, so 0.9.0 is the highest installable floor. The existing '< 0.10' upper bound is preserved.
Cherie-Chen
added a commit
to Cherie-Chen/deadline-cloud-for-unreal-engine-cheriech
that referenced
this pull request
May 26, 2026
….9.0 Bump minimum required versions of OpenJobDescription dependencies to the latest releases available on PyPI: openjd-adaptor-runtime: 0.8.1 -> 0.9.4 openjd-model: 0.8.1 -> 0.9.0 The adaptor-runtime bump picks up the Windows DACL fix from OpenJobDescription/openjd-adaptor-runtime-for-python#260, which prevents the per-adaptor JSON config file from being permanently locked to the SID of whichever account first created it. This avoids PermissionError on multi-user Unreal worker hosts. openjd-model 0.9.1 has a GitHub release tag but was never published to PyPI, so 0.9.0 is the highest installable floor. The existing '< 0.10' upper bound is preserved.
Cherie-Chen
added a commit
to Cherie-Chen/deadline-cloud-for-unreal-engine-cheriech
that referenced
this pull request
May 26, 2026
….9.0 Bump minimum required versions of OpenJobDescription dependencies to the latest releases available on PyPI: openjd-adaptor-runtime: 0.8.1 -> 0.9.4 openjd-model: 0.8.1 -> 0.9.0 The adaptor-runtime bump picks up the Windows DACL fix from OpenJobDescription/openjd-adaptor-runtime-for-python#260, which prevents the per-adaptor JSON config file from being permanently locked to the SID of whichever account first created it. This avoids PermissionError on multi-user Unreal worker hosts. openjd-model 0.9.1 has a GitHub release tag but was never published to PyPI, so 0.9.0 is the highest installable floor. The existing '< 0.10' upper bound is preserved. Signed-off-by: Cherie-Chen <58997764+Cherie-Chen@users.noreply.github.com>
Cherie-Chen
added a commit
to aws-deadline/deadline-cloud-for-unreal-engine
that referenced
this pull request
May 26, 2026
….9.0 (#302) Bump minimum required versions of OpenJobDescription dependencies to the latest releases available on PyPI: openjd-adaptor-runtime: 0.8.1 -> 0.9.4 openjd-model: 0.8.1 -> 0.9.0 The adaptor-runtime bump picks up the Windows DACL fix from OpenJobDescription/openjd-adaptor-runtime-for-python#260, which prevents the per-adaptor JSON config file from being permanently locked to the SID of whichever account first created it. This avoids PermissionError on multi-user Unreal worker hosts. openjd-model 0.9.1 has a GitHub release tag but was never published to PyPI, so 0.9.0 is the highest installable floor. The existing '< 0.10' upper bound is preserved. Signed-off-by: Cherie-Chen <58997764+Cherie-Chen@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes: N/A — no tracking issue
What was the problem/requirement? (What/Why)
ConfigurationManager._ensure_config_filecreates an empty<AdaptorName>.jsonon first access if one does not yet exist. That create path goes throughsecure_open(), which on Windows replaces the file's DACL with a single non-inheriting ACE granting only the current owner SIDFILE_GENERIC_READ | FILE_GENERIC_WRITE | DELETE(seeset_file_permissions_in_windowsin_secure_open.py).The effect is that the config file's ACL stops following the parent directory's inheritable ACLs and is permanently bound to the SID of whichever account happened to first create it. If that file is later accessed by any other account — even one that would have had inherited access from the parent directory — the open fails with
PermissionError. It also runs counter to what most users will reasonably expect from filesystem permissions on an ordinary config file.There is no documented requirement for adaptor config files to be owner-only at the file level. The directories these files live in (
<adaptor module dir>/,~/.openjd/adaptors/<name>/,%PROGRAMDATA%\openjd\adaptors\<name>\) already restrict access via their own ACLs.What was the solution? (How)
Switch the create path in
_ensure_config_fileto use plainopen(filepath, mode="w", encoding="utf-8")and let normal filesystem permission rules apply. This matches the call style used by every otheropen()write in this package (e.g.,frontend_runner.pyfor the bootstrap output file).The other
secure_opencall sites (the connection file in_background/backend_runner.pyand the log buffer in_background/log_buffers.py) are intentionally unchanged.What is the impact of this change?
On Windows, newly created adaptor config files inherit ACLs from their parent directory rather than receiving an owner-only DACL. On POSIX, behavior is unchanged in practice —
secure_openwas already only settingS_IWUSR | S_IRUSRviaos.open, whichopen()of a fresh file effectively produces under typical default umasks for the directories these files land in.No public API changes. The
secure_openhelper itself is untouched and remains available for callers (including the connection-file and log-buffer call sites) that want owner-only DACLs.How was this change tested?
hatch run test— 439 passed, 36 skipped (Posix-only tests skipped on Windows host), 91.94% coverage.hatch run lint—ruff check,black --check, andmypyall clean.Updated
test/openjd/adaptor_runtime/unit/adaptors/configuration/test_configuration_manager.py::TestEnsureConfigFile::test_createto patchbuiltins.openinstead ofsecure_openand to assert the new keyword (mode="w"rather thanopen_mode="w").Have you run the unit tests? Yes (see above).
Was this change documented?
secure_opencall; none required updates.Is this a breaking change?
No.
_ensure_config_fileis a module-private helper (leading underscore) andConfigurationManager's public API is unchanged. Callers outside the package do not depend on the file-permission side effect of the create path; the public read/write APIs (get_default_config,get_user_config,build_config, etc.) behave identically.Does this change impact security?
The change relaxes a previously-enforced owner-only DACL on Windows for a single file-create call site. The argument for doing so is that:
<AdaptorName>.jsonin the package directory or elsewhere, no DACL override was ever applied to it. So no part of the system was relying on this file's ACL as a security boundary.By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.