Skip to content

Support fetching tokens from an external command - #469

Open
ammachado wants to merge 8 commits into
psss:mainfrom
ammachado:did-pwmgr
Open

Support fetching tokens from an external command#469
ammachado wants to merge 8 commits into
psss:mainfrom
ammachado:did-pwmgr

Conversation

@ammachado

@ammachado ammachado commented May 15, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add token_command as a third token source alongside token / token_file, so plugins can pull secrets from password managers such as BitWarden (bw get password did-jira) or 1Password (op read op://Personal/Jira/token). The command is parsed with shlex (no shell) and its stripped stdout is used as the token; failures raise ConfigError. Results are memoized per process via functools.lru_cache so multiple config sections sharing a command only invoke the external tool once.
  • Precedence when more than one of token, token_file, token_command is set in a section: token > token_file > token_command, matching the existing token vs. token_file precedence — lower-precedence keys are silently ignored, same as before.
  • Updated the jira, confluence, github, and gitlab plugin docstrings to document token_command and the precedence rule. Other plugins automatically gain the feature through the shared did.base.get_token helper.

Test plan

  • pytest tests/unit/test_base.py::TestGetToken — 13/13 passing (5 new tests for the command source, precedence, failure modes, and memoization)
  • Full pytest tests/unit -n auto clean except for pre-existing failures unrelated to token handling (nitrate/psycopg2 build needs pg_config locally; one redmine live-data test)
  • CI runs the full suite with .[all] installed
  • Manual smoke: configure a plugin with token_command = printf %s ... and confirm the report runs

🤖 Generated with Claude Code

@ammachado
ammachado marked this pull request as ready for review June 1, 2026 15:46
@ammachado

Copy link
Copy Markdown
Contributor Author

Note on integration test count updates (tests/github/issues.sh, tests/github/pulls.sh)

@psss, while investigating CI failures in Testing Farm, I found that two items are no longer being returned by the GitHub Search API for the 2022 date range:

  • teemtee/fmf#149 — missing from all "issues created/closed" queries
  • teemtee/try#002 — missing from all "pull requests created" queries

Both items are still visible by direct URL, so they have not been deleted. The root cause is GitHub Search API inconsistency: the index is eventually consistent and does not guarantee that all matching items are returned, especially for older closed issues/PRs. I confirmed the failure also reproduces on main, so this is not a regression introduced by this PR. The same behavior is also observed in PRs #468 and #470.

As a short-term fix I updated the expected counts and dropped the specific rlAssertGrep assertions for the two missing items.

I'd like your advice on how to handle this long-term. A few options I can think of:

  1. Keep exact counts (current approach) — simple to read, but requires manual updates whenever the Search index drifts. Will happen again.
  2. Drop count assertions entirely — only assert that specific known items are present/absent. More resilient to index drift, but loses the sanity check that ensures we're not silently fetching zero results.
  3. Use a lower-bound check — e.g. assert the count is >= N rather than == N. Beakerlib doesn't support this natively but it could be done with a small shell snippet.
  4. Pin the test to a narrower, stable date range where the result set is small enough that index drift is unlikely to drop items.

What is the preferred approach for this project?

@psss

psss commented Jul 10, 2026

Copy link
Copy Markdown
Owner

@ammachado, thanks for looking into the test issues. Yes, it really seems that some of the expected issues are not included in the search results anymore. Interestingly, for pull requests your changes seem not needed anymore. Something weird is happening on the GitHub side. Here's a slightly modified version of your adjustments:

@psss psss left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks much for implementing this! Looks good, just two minor comments. Also the following statement from the pull request description is not valid, right?

  • Breaking change: setting more than one of token, token_file, token_command in a section is now a hard ConfigError. Previously the lower-precedence keys were silently ignored.

Comment thread CLAUDE.md Outdated
Comment thread did/plugins/jira.py
ammachado added a commit to ammachado/did that referenced this pull request Jul 14, 2026
…re widely

- Remove CLAUDE.md from this branch per review request; the repo
  guidance content is fine but belongs in its own PR.
- Document token_command (alongside token/token_file) in the github
  and gitlab plugin docstrings, matching the jira/confluence notes.
psss pushed a commit to ammachado/did that referenced this pull request Jul 14, 2026
…re widely

- Remove CLAUDE.md from this branch per review request; the repo
  guidance content is fine but belongs in its own PR.
- Document token_command (alongside token/token_file) in the github
  and gitlab plugin docstrings, matching the jira/confluence notes.
@psss psss added the base label Jul 14, 2026
@psss psss added this to the 0.24 milestone Jul 14, 2026
psss pushed a commit to ammachado/did that referenced this pull request Jul 14, 2026
…re widely

- Remove CLAUDE.md from this branch per review request; the repo
  guidance content is fine but belongs in its own PR.
- Document token_command (alongside token/token_file) in the github
  and gitlab plugin docstrings, matching the jira/confluence notes.

@psss psss left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks for the changes. Added one more missing pre-commit dependency in 4cd37b4. Should be good to go.

@lukaszachy lukaszachy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, finally a safer way to store tokens

@kwk kwk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I do like the idea of using Bitwarden. But I'm sorry but I have a few questions and annotations.

Comment thread did/plugins/gitlab.py
Comment thread did/plugins/jira.py
Comment thread did/plugins/jira.py Outdated
Comment thread did/base.py
Comment thread did/base.py
Comment thread tests/unit/test_base.py Outdated
Comment thread tests/unit/test_utils.py Outdated
Comment thread tests/unit/test_utils.py Outdated
Comment thread tests/unit/test_utils.py Outdated
Comment thread .pre-commit-config.yaml Outdated
ammachado added a commit to ammachado/did that referenced this pull request Sep 6, 2026
- List `token_command` in the gitlab and jira config examples, next
  to the `token` and `token_file` entries that were already there
- Clarify that the token command inherits did's environment (so
  `BW_SESSION` and friends work) and only loses shell expansion
- Include the command line in the `FileNotFoundError` and
  `CalledProcessError` messages, matching the timeout message
- Document why caching the token for the process lifetime does not
  widen exposure of the secret
- Reword a test comment that read as a typo

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ammachado and others added 7 commits September 6, 2026 12:24
Add `token_command` as a third token source alongside `token` and
`token_file`, so secrets can be pulled from password managers such as
BitWarden (`bw get password ...`) or 1Password (`op read op://...`).
The command is parsed with shlex (no shell) and its stdout is used as
the token; failures raise `ConfigError`. Results are memoized per
process so multiple sections sharing a command only invoke the tool
once.

Setting more than one of `token`, `token_file`, `token_command` is now
a hard `ConfigError` (previously the lower-precedence keys were
silently ignored).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Fix terminal color reset sequence (\033[1;m -> \033[0m) so
  background color does not bleed into subsequent output
- Extract the human-readable 'message' field from GitHub API JSON
  error responses instead of printing the raw JSON blob
- Drop the Future object reference from ReportError log lines so
  only the error text is shown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Mirror the dependency list from the mypy hook so pylint can resolve
all project imports, substituting requests/python-dateutil for their
type-stub equivalents.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Adriano Machado <60320+ammachado@users.noreply.github.com>
Update expected ANSI escape sequences from \033[1;m to \033[0m
following the fix in cbef398.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

rh-pre-commit.version: 2.4.0
rh-pre-commit.check-secrets: ENABLED
…re widely

- Remove CLAUDE.md from this branch per review request; the repo
  guidance content is fine but belongs in its own PR.
- Document token_command (alongside token/token_file) in the github
  and gitlab plugin docstrings, matching the jira/confluence notes.
- List `token_command` in the gitlab and jira config examples, next
  to the `token` and `token_file` entries that were already there
- Clarify that the token command inherits did's environment (so
  `BW_SESSION` and friends work) and only loses shell expansion
- Include the command line in the `FileNotFoundError` and
  `CalledProcessError` messages, matching the timeout message
- Document why caching the token for the process lifetime does not
  widen exposure of the secret
- Reword a test comment that read as a typo

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ammachado

Copy link
Copy Markdown
Contributor Author

@kwk, all fourteen threads from your review are answered, and the branch is rebased on current main. Could you take another look when you have a moment?

Changed in 179036d:

  • token_command added to the gitlab and jira config examples, next to the token / token_file entries that were already listed
  • Clarified "without a shell": the command inherits did's environment, so BW_SESSION and friends are visible. Only shell expansion is missing (no pipes, redirects, $VAR, globs). Documented in both the jira plugin docstring and _run_token_command
  • The command line is now included in all three error paths, not just the timeout one
  • Documented why caching the token for the process lifetime does not widen exposure of the secret
  • Reworded the "memoized" comment that read as a typo

Answered without a code change, reasoning in each thread:

  • Optional[str] stays, since setup.py still declares Python 3.9 and PEP 604 is 3.10+. from __future__ import annotations would let us switch, but repo-wide rather than in this PR
  • did/stats.py: the old line printed a Future repr, and a failing token_command surfaces through exactly that line
  • did/utils.py colour reset and the three test assertions that follow from it: I overstated this as a bug fix in the commit message, it is a cleanup. Corrected in the thread
  • .pre-commit-config.yaml: pylint was hitting import-error, mirroring the list the mypy hook already carries

I offered in several threads to split the colour-reset and pre-commit changes into separate PRs if you would still prefer this one narrower. Happy to do that, just say which.

One note on CI: the four Fedora testing-farm jobs are red, but not because of this branch. pagure.io's REST API is currently returning 404 for every path, including https://pagure.io/api/0/version, while the web UI is up. That fails the five tests/unit/plugins/test_pagure.py tests. The same five fail on unmodified main, and this PR touches no pagure code. The CentOS Stream jobs pass because they do not run the unit plan.

@ammachado
ammachado requested a review from kwk September 6, 2026 16:55
@kwk

kwk commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Okay, let me make one thing clear, I like the feature. But I also like Big Buts (the song is cool as well but written differently).

Can we please get a policy for AI contributions? I simply do not buy that my last review was answered by a human. And I refuse to work this way.

I'm getting really angry about such comments. Not because they are incorrect or inaccurate but because I feel like someone is operating a machine instead of thinking for him or herself. This is disrespectful.

@psss please look at the responses and tell me this was written by a person.

@ammachado

Copy link
Copy Markdown
Contributor Author

@kwk I use Claude Code to assist me, but I review all the work it does. This PR is not on autopilot. I'll move the color reset and log improvements to a different PR.

The color reset in did/utils.py (with the three test assertions that
follow from it), the stat failure log message in did/stats.py, and the
pylint additional_dependencies in .pre-commit-config.yaml are not part
of this feature. They go to separate pull requests instead, per review
feedback on psss#469.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ammachado

ammachado commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Two new PR open: #481 (missing runtime dependencies for macOS) and #482 (log improvements)

@ammachado
ammachado force-pushed the did-pwmgr branch 3 times, most recently from 01833ef to 538af51 Compare September 7, 2026 16:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants