Ok, it turns out, Foreman supports the OAuth1 kind of access, which is not much (or even any) better than simply a static username/password. Worse, unless you take special measures by mapping such access to an unprivileged account, connections using OAuth1 will have admin-privileges.
Visit the settings page of your Foreman-installation to turn on the mapping and/or learn/set the OAuth1 "key" and "secret" strings.
While the real users may be authenticating via LDAP (such as against your corporate Active Directory) or other means, the OAuth1-using script can use the same "key" and "secret" values for ever -- they don't even expire.
Edit: enabling user-mapping does not appear to improve security at all -- any existing account-name can be specified and no knowledge of the account's password or any other secrets is necessary.
My simple Foreman-search script implemented in Python is thus (no support for pagination -- all my queries return well under 50 entries):
from requests_oauthlib import OAuth1Session
import json
import sys
search=sys.argv[1]
fields=sys.argv[2:]
if not fields:
fields = ['ip']
URL=("https://foreman.example.net/api/v2/hosts?search=%s" % search)
# The below highly-secret credentials come from
#
# https://foreman.example.net/settings
#
Key='mykey'
Secret='mysecret'
User='readonly'
session=OAuth1Session(Key, Secret)
# The FOREMAN-USER header is necessary, if mapping of OAuth-requests is
# turned on in Foreman's settings. The username must exist and have the
# right "roles" enabled to allow the account to perform the request.
session.headers.update({'FOREMAN-USER': User})
request=session.get(URL, verify='/path/to/your/CA/certificate.crt')
if not request.ok:
request.raise_for_status()
response=json.loads(request.content)
results=response["results"]
for i in xrange(0, response["subtotal"]):
entry=results[i]
if i > 0:
print
for field in fields:
try:
print entry[field],
except KeyError:
print '-',