mirror of
https://github.com/ansible/awx.git
synced 2026-03-22 11:25:08 -02:30
feat: support insights service account credentials for project update (AAP-37464)
This commit is contained in:
@@ -1279,6 +1279,7 @@ class RunProjectUpdate(BaseTask):
|
|||||||
'local_path': os.path.basename(project_update.project.local_path),
|
'local_path': os.path.basename(project_update.project.local_path),
|
||||||
'project_path': project_update.get_project_path(check_if_exists=False), # deprecated
|
'project_path': project_update.get_project_path(check_if_exists=False), # deprecated
|
||||||
'insights_url': settings.INSIGHTS_URL_BASE,
|
'insights_url': settings.INSIGHTS_URL_BASE,
|
||||||
|
'oidc_endpoint': settings.INSIGHTS_OIDC_ENDPOINT,
|
||||||
'awx_license_type': get_license().get('license_type', 'UNLICENSED'),
|
'awx_license_type': get_license().get('license_type', 'UNLICENSED'),
|
||||||
'awx_version': get_awx_version(),
|
'awx_version': get_awx_version(),
|
||||||
'scm_url': scm_url,
|
'scm_url': scm_url,
|
||||||
@@ -1445,6 +1446,11 @@ class RunProjectUpdate(BaseTask):
|
|||||||
)
|
)
|
||||||
return params
|
return params
|
||||||
|
|
||||||
|
def build_credentials_list(self, project_update):
|
||||||
|
if project_update.scm_type == 'insights' and project_update.credential:
|
||||||
|
return [project_update.credential]
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
@task(queue=get_task_queuename)
|
@task(queue=get_task_queuename)
|
||||||
class RunInventoryUpdate(SourceControlMixin, BaseTask):
|
class RunInventoryUpdate(SourceControlMixin, BaseTask):
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import requests
|
|||||||
|
|
||||||
from ansible.plugins.action import ActionBase
|
from ansible.plugins.action import ActionBase
|
||||||
|
|
||||||
|
DEFAULT_OIDC_ENDPOINT = 'https://sso.redhat.com/auth/realms/redhat-external'
|
||||||
|
|
||||||
|
|
||||||
class ActionModule(ActionBase):
|
class ActionModule(ActionBase):
|
||||||
def save_playbook(self, proj_path, remediation, content):
|
def save_playbook(self, proj_path, remediation, content):
|
||||||
@@ -34,27 +36,75 @@ class ActionModule(ActionBase):
|
|||||||
with open(file_path, 'w') as f:
|
with open(file_path, 'w') as f:
|
||||||
f.write(etag)
|
f.write(etag)
|
||||||
|
|
||||||
|
def _obtain_auth_token(self, oidc_endpoint, client_id, client_secret):
|
||||||
|
if oidc_endpoint.endswith('/'):
|
||||||
|
oidc_endpoint = oidc_endpoint[:-1]
|
||||||
|
main_url = oidc_endpoint + '/.well-known/openid-configuration'
|
||||||
|
response = requests.get(url=main_url, headers={'Accept': 'application/json'})
|
||||||
|
data = {}
|
||||||
|
if response.status_code != 200:
|
||||||
|
data['failed'] = True
|
||||||
|
data['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format(
|
||||||
|
main_url, response.status_code, response.content
|
||||||
|
)
|
||||||
|
return data
|
||||||
|
|
||||||
|
auth_url = response.json().get('token_endpoint', None)
|
||||||
|
data = {
|
||||||
|
'grant_type': 'client_credentials',
|
||||||
|
'scope': 'api.console',
|
||||||
|
'client_id': client_id,
|
||||||
|
'client_secret': client_secret,
|
||||||
|
}
|
||||||
|
response = requests.post(url=auth_url, data=data)
|
||||||
|
|
||||||
|
if response.status_code != 200:
|
||||||
|
data['failed'] = True
|
||||||
|
data['msg'] = 'Expected {} to return a status code of 200 but returned status code "{}" instead with content "{}".'.format(
|
||||||
|
auth_url, response.status_code, response.content
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
data['token'] = response.json().get('access_token', None)
|
||||||
|
data['token_type'] = response.json().get('token_type', None)
|
||||||
|
return data
|
||||||
|
|
||||||
def run(self, tmp=None, task_vars=None):
|
def run(self, tmp=None, task_vars=None):
|
||||||
self._supports_check_mode = False
|
self._supports_check_mode = False
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
result = super(ActionModule, self).run(tmp, task_vars)
|
result = super(ActionModule, self).run(tmp, task_vars)
|
||||||
|
|
||||||
insights_url = self._task.args.get('insights_url', None)
|
insights_url = self._task.args.get('insights_url', None)
|
||||||
username = self._task.args.get('username', None)
|
|
||||||
password = self._task.args.get('password', None)
|
|
||||||
proj_path = self._task.args.get('project_path', None)
|
proj_path = self._task.args.get('project_path', None)
|
||||||
license = self._task.args.get('awx_license_type', None)
|
license = self._task.args.get('awx_license_type', None)
|
||||||
awx_version = self._task.args.get('awx_version', None)
|
awx_version = self._task.args.get('awx_version', None)
|
||||||
|
authentication = self._task.args.get('authentication', None)
|
||||||
|
username = self._task.args.get('username', None)
|
||||||
|
password = self._task.args.get('password', None)
|
||||||
|
client_id = self._task.args.get('client_id', None)
|
||||||
|
client_secret = self._task.args.get('client_secret', None)
|
||||||
|
oidc_endpoint = self._task.args.get('oidc_endpoint', DEFAULT_OIDC_ENDPOINT)
|
||||||
|
|
||||||
|
session.headers.update(
|
||||||
|
{
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'User-Agent': '{} {} ({})'.format('AWX' if license == 'open' else 'Red Hat Ansible Automation Platform', awx_version, license),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
if authentication == 'service_account' or (client_id and client_secret):
|
||||||
|
data = self._obtain_auth_token(oidc_endpoint, client_id, client_secret)
|
||||||
|
if 'token' not in data:
|
||||||
|
result['failed'] = data['failed']
|
||||||
|
result['msg'] = data['msg']
|
||||||
|
return result
|
||||||
|
session.headers.update({'Authorization': f'{data["token_type"]} {data["token"]}'})
|
||||||
|
elif authentication == 'basic' or (username and password):
|
||||||
|
session.auth = requests.auth.HTTPBasicAuth(username, password)
|
||||||
|
|
||||||
session = requests.Session()
|
|
||||||
session.auth = requests.auth.HTTPBasicAuth(username, password)
|
|
||||||
headers = {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
'User-Agent': '{} {} ({})'.format('AWX' if license == 'open' else 'Red Hat Ansible Automation Platform', awx_version, license),
|
|
||||||
}
|
|
||||||
url = '/api/remediations/v1/remediations'
|
url = '/api/remediations/v1/remediations'
|
||||||
while url:
|
while url:
|
||||||
res = session.get('{}{}'.format(insights_url, url), headers=headers, timeout=120)
|
res = session.get('{}{}'.format(insights_url, url), timeout=120)
|
||||||
|
|
||||||
if res.status_code != 200:
|
if res.status_code != 200:
|
||||||
result['failed'] = True
|
result['failed'] = True
|
||||||
|
|||||||
@@ -19,6 +19,11 @@
|
|||||||
# awx_version: Current running version of the awx or tower as a string
|
# awx_version: Current running version of the awx or tower as a string
|
||||||
# awx_license_type: "open" for AWX; else presume Tower
|
# awx_license_type: "open" for AWX; else presume Tower
|
||||||
# gpg_pubkey: the GPG public key to use for validation, when enabled
|
# gpg_pubkey: the GPG public key to use for validation, when enabled
|
||||||
|
# client_id: Red Hat service account client ID; required for the 'service_account' authentication method used against the Insights API
|
||||||
|
# client_secret: Red Hat service account client secret; required for the 'service_account' authentication method used against the Insights API
|
||||||
|
# authentication: The authentication method to use against the Insights API
|
||||||
|
# client_id and client_secret are required for the 'service_account' authentication method
|
||||||
|
# scm_username and scm_password are required for the 'basic' authentication method
|
||||||
|
|
||||||
- hosts: localhost
|
- hosts: localhost
|
||||||
gather_facts: false
|
gather_facts: false
|
||||||
@@ -95,11 +100,14 @@
|
|||||||
- name: Fetch Insights Playbook(s)
|
- name: Fetch Insights Playbook(s)
|
||||||
insights:
|
insights:
|
||||||
insights_url: "{{ insights_url }}"
|
insights_url: "{{ insights_url }}"
|
||||||
username: "{{ scm_username }}"
|
username: "{{ scm_username | default(omit) }}"
|
||||||
password: "{{ scm_password }}"
|
password: "{{ scm_password | default(omit) }}"
|
||||||
project_path: "{{ project_path }}"
|
project_path: "{{ project_path }}"
|
||||||
awx_license_type: "{{ awx_license_type }}"
|
awx_license_type: "{{ awx_license_type }}"
|
||||||
awx_version: "{{ awx_version }}"
|
awx_version: "{{ awx_version }}"
|
||||||
|
client_id: "{{ client_id | default(omit) }}"
|
||||||
|
client_secret: "{{ client_secret | default(omit) }}"
|
||||||
|
authentication: "{{ authentication | default(omit) }}"
|
||||||
register: results
|
register: results
|
||||||
|
|
||||||
- name: Save Insights Version
|
- name: Save Insights Version
|
||||||
|
|||||||
@@ -848,6 +848,7 @@ DISABLE_LOCAL_AUTH = False
|
|||||||
TOWER_URL_BASE = "https://platformhost"
|
TOWER_URL_BASE = "https://platformhost"
|
||||||
|
|
||||||
INSIGHTS_URL_BASE = "https://example.org"
|
INSIGHTS_URL_BASE = "https://example.org"
|
||||||
|
INSIGHTS_OIDC_ENDPOINT = "https://sso.example.org/"
|
||||||
INSIGHTS_AGENT_MIME = 'application/example'
|
INSIGHTS_AGENT_MIME = 'application/example'
|
||||||
# See https://github.com/ansible/awx-facts-playbooks
|
# See https://github.com/ansible/awx-facts-playbooks
|
||||||
INSIGHTS_SYSTEM_ID_FILE = '/etc/redhat-access-insights/machine-id'
|
INSIGHTS_SYSTEM_ID_FILE = '/etc/redhat-access-insights/machine-id'
|
||||||
|
|||||||
Reference in New Issue
Block a user