summaryrefslogtreecommitdiffstats
path: root/awxkit
diff options
context:
space:
mode:
authorAlan Rominger <arominge@redhat.com>2024-11-04 22:21:36 +0100
committerAlan Rominger <arominge@redhat.com>2024-11-20 17:18:52 +0100
commit6599f3f827f4a5eef9a0b2bb965b7087e71d404a (patch)
tree318336f3bce0be7f6a582d1849c197a8acfc7a67 /awxkit
parentFix server error from system job detail view (#15640) (diff)
downloadawx-6599f3f827f4a5eef9a0b2bb965b7087e71d404a.tar.xz
awx-6599f3f827f4a5eef9a0b2bb965b7087e71d404a.zip
Removal of OAuth2 stuff from CLI
also from awxkit generally Remove login command
Diffstat (limited to 'awxkit')
-rw-r--r--awxkit/awxkit/api/client.py13
-rw-r--r--awxkit/awxkit/api/pages/__init__.py2
-rw-r--r--awxkit/awxkit/api/pages/api.py1
-rw-r--r--awxkit/awxkit/api/pages/applications.py82
-rw-r--r--awxkit/awxkit/api/pages/authtoken.py10
-rw-r--r--awxkit/awxkit/api/pages/base.py72
-rw-r--r--awxkit/awxkit/api/resources.py12
-rw-r--r--awxkit/awxkit/awx/utils.py13
-rwxr-xr-xawxkit/awxkit/cli/client.py16
-rw-r--r--awxkit/awxkit/cli/docs/source/usage.rst3
-rw-r--r--awxkit/awxkit/cli/format.py6
-rw-r--r--awxkit/awxkit/cli/resource.py47
-rw-r--r--awxkit/awxkit/ws.py8
-rw-r--r--awxkit/test/api/pages/test_base.py38
-rw-r--r--awxkit/test/test_ws.py5
15 files changed, 15 insertions, 313 deletions
diff --git a/awxkit/awxkit/api/client.py b/awxkit/awxkit/api/client.py
index cca1bba51d..ef18055f2a 100644
--- a/awxkit/awxkit/api/client.py
+++ b/awxkit/awxkit/api/client.py
@@ -13,15 +13,6 @@ class ConnectionException(exc.Common):
pass
-class Token_Auth(requests.auth.AuthBase):
- def __init__(self, token):
- self.token = token
-
- def __call__(self, request):
- request.headers['Authorization'] = 'Bearer {0.token}'.format(self)
- return request
-
-
def log_elapsed(r, *args, **kwargs): # requests hook to display API elapsed time
log.debug('"{0.request.method} {0.url}" elapsed: {0.elapsed}'.format(r))
@@ -47,7 +38,7 @@ class Connection(object):
self.get(config.api_base_path) # this causes a cookie w/ the CSRF token to be set
return dict(next=next)
- def login(self, username=None, password=None, token=None, **kwargs):
+ def login(self, username=None, password=None, **kwargs):
if username and password:
_next = kwargs.get('next')
if _next:
@@ -62,8 +53,6 @@ class Connection(object):
self.uses_session_cookie = True
else:
self.session.auth = (username, password)
- elif token:
- self.session.auth = Token_Auth(token)
else:
self.session.auth = None
diff --git a/awxkit/awxkit/api/pages/__init__.py b/awxkit/awxkit/api/pages/__init__.py
index 7fbdbcdb0d..0ede3ee2fe 100644
--- a/awxkit/awxkit/api/pages/__init__.py
+++ b/awxkit/awxkit/api/pages/__init__.py
@@ -4,13 +4,11 @@ from .base import * # NOQA
from .bulk import * # NOQA
from .access_list import * # NOQA
from .api import * # NOQA
-from .authtoken import * # NOQA
from .roles import * # NOQA
from .organizations import * # NOQA
from .notifications import * # NOQA
from .notification_templates import * # NOQA
from .users import * # NOQA
-from .applications import * # NOQA
from .teams import * # NOQA
from .credentials import * # NOQA
from .unified_jobs import * # NOQA
diff --git a/awxkit/awxkit/api/pages/api.py b/awxkit/awxkit/api/pages/api.py
index 2283f10c96..7c91e38e96 100644
--- a/awxkit/awxkit/api/pages/api.py
+++ b/awxkit/awxkit/api/pages/api.py
@@ -25,7 +25,6 @@ EXPORTABLE_RESOURCES = [
'job_templates',
'workflow_job_templates',
'execution_environments',
- 'applications',
'schedules',
]
diff --git a/awxkit/awxkit/api/pages/applications.py b/awxkit/awxkit/api/pages/applications.py
deleted file mode 100644
index 3c9e4a8d76..0000000000
--- a/awxkit/awxkit/api/pages/applications.py
+++ /dev/null
@@ -1,82 +0,0 @@
-from awxkit.utils import random_title, update_payload, filter_by_class, PseudoNamespace
-from awxkit.api.resources import resources
-from awxkit.api.pages import Organization
-from awxkit.api.mixins import HasCreate, DSAdapter
-
-from . import page
-from . import base
-
-
-class OAuth2Application(HasCreate, base.Base):
- dependencies = [Organization]
- NATURAL_KEY = ('organization', 'name')
-
- def payload(self, **kwargs):
- payload = PseudoNamespace(
- name=kwargs.get('name') or 'OAuth2Application - {}'.format(random_title()),
- description=kwargs.get('description') or random_title(10),
- client_type=kwargs.get('client_type', 'public'),
- authorization_grant_type=kwargs.get('authorization_grant_type', 'password'),
- )
- if kwargs.get('organization'):
- payload.organization = kwargs['organization'].id
-
- optional_fields = ('redirect_uris', 'skip_authorization')
- update_payload(payload, optional_fields, kwargs)
- return payload
-
- def create_payload(self, organization=Organization, **kwargs):
- self.create_and_update_dependencies(*filter_by_class((organization, Organization)))
- organization = self.ds.organization if organization else None
- payload = self.payload(organization=organization, **kwargs)
- payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
- return payload
-
- def create(self, organization=Organization, **kwargs):
- payload = self.create_payload(organization=organization, **kwargs)
- return self.update_identity(OAuth2Applications(self.connection).post(payload))
-
-
-page.register_page((resources.application, (resources.applications, 'post')), OAuth2Application)
-
-
-class OAuth2Applications(page.PageList, OAuth2Application):
- pass
-
-
-page.register_page(resources.applications, OAuth2Applications)
-
-
-class OAuth2AccessToken(HasCreate, base.Base):
- optional_dependencies = [OAuth2Application]
-
- def payload(self, **kwargs):
- payload = PseudoNamespace(description=kwargs.get('description') or random_title(10), scope=kwargs.get('scope', 'write'))
-
- if kwargs.get('oauth_2_application'):
- payload.application = kwargs['oauth_2_application'].id
-
- optional_fields = ('expires',)
- update_payload(payload, optional_fields, kwargs)
- return payload
-
- def create_payload(self, oauth_2_application=None, **kwargs):
- self.create_and_update_dependencies(*filter_by_class((oauth_2_application, OAuth2Application)))
- oauth_2_application = self.ds.oauth_2_application if oauth_2_application else None
- payload = self.payload(oauth_2_application=oauth_2_application, **kwargs)
- payload.ds = DSAdapter(self.__class__.__name__, self._dependency_store)
- return payload
-
- def create(self, oauth_2_application=None, **kwargs):
- payload = self.create_payload(oauth_2_application=oauth_2_application, **kwargs)
- return self.update_identity(OAuth2AccessTokens(self.connection).post(payload))
-
-
-page.register_page((resources.token, (resources.tokens, 'post')), OAuth2AccessToken)
-
-
-class OAuth2AccessTokens(page.PageList, OAuth2AccessToken):
- pass
-
-
-page.register_page(resources.tokens, OAuth2AccessTokens)
diff --git a/awxkit/awxkit/api/pages/authtoken.py b/awxkit/awxkit/api/pages/authtoken.py
deleted file mode 100644
index 36ec0b9546..0000000000
--- a/awxkit/awxkit/api/pages/authtoken.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from awxkit.api.resources import resources
-from . import base
-from . import page
-
-
-class AuthToken(base.Base):
- pass
-
-
-page.register_page(resources.authtoken, AuthToken)
diff --git a/awxkit/awxkit/api/pages/base.py b/awxkit/awxkit/api/pages/base.py
index 8730b9be48..99fb08f5f7 100644
--- a/awxkit/awxkit/api/pages/base.py
+++ b/awxkit/awxkit/api/pages/base.py
@@ -1,23 +1,13 @@
-import collections
import logging
-import typing
-from requests.auth import HTTPBasicAuth
-
-from awxkit.api.pages import Page, get_registered_page, exception_from_status_code
+from awxkit.api.pages import Page
from awxkit.config import config
-from awxkit.api.resources import resources
import awxkit.exceptions as exc
log = logging.getLogger(__name__)
-class AuthUrls(typing.TypedDict):
- access_token: str
- personal_token: str
-
-
class Base(Page):
def silent_delete(self):
"""Delete the object. If it's already deleted, ignore the error"""
@@ -135,66 +125,6 @@ class Base(Page):
for obj_role in Roles(self.connection, endpoint=url).get().json.results:
yield Role(self.connection, endpoint=obj_role.url).get()
- def get_authtoken(self, username='', password=''):
- default_cred = config.credentials.default
- payload = dict(username=username or default_cred.username, password=password or default_cred.password)
- auth_url = resources.authtoken
- return get_registered_page(auth_url)(self.connection, endpoint=auth_url).post(payload).token
-
- def load_authtoken(self, username='', password=''):
- self.connection.login(token=self.get_authtoken(username, password))
- return self
-
- load_default_authtoken = load_authtoken
-
- def _request_token(self, auth_urls, username, password, client_id, description, client_secret, scope):
- req = collections.namedtuple('req', 'headers')({})
- if client_id and client_secret:
- HTTPBasicAuth(client_id, client_secret)(req)
- req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
- resp = self.connection.post(
- auth_urls["access_token"],
- data={"grant_type": "password", "username": username, "password": password, "scope": scope},
- headers=req.headers,
- )
- elif client_id:
- req.headers['Content-Type'] = 'application/x-www-form-urlencoded'
- resp = self.connection.post(
- auth_urls["access_token"],
- data={"grant_type": "password", "username": username, "password": password, "client_id": client_id, "scope": scope},
- headers=req.headers,
- )
- else:
- HTTPBasicAuth(username, password)(req)
- resp = self.connection.post(
- auth_urls['personal_token'],
- json={"description": description, "application": None, "scope": scope},
- headers=req.headers,
- )
- if resp.ok:
- result = resp.json()
- if client_id:
- return result.pop('access_token', None)
- else:
- return result.pop('token', None)
- else:
- raise exception_from_status_code(resp.status_code)
-
- def get_oauth2_token(self, username='', password='', client_id=None, description='AWX CLI', client_secret=None, scope='write'):
- default_cred = config.credentials.default
- username = username or default_cred.username
- password = password or default_cred.password
- # Try gateway first, fallback to controller
- urls: AuthUrls = {"access_token": "/o/token/", "personal_token": f"{config.gateway_base_path}v1/tokens/"}
- try:
- return self._request_token(urls, username, password, client_id, description, client_secret, scope)
- except exc.NotFound:
- urls = {
- "access_token": f"{config.api_base_path}o/token/",
- "personal_token": f"{config.api_base_path}v2/users/{username}/personal_tokens/",
- }
- return self._request_token(urls, username, password, client_id, description, client_secret, scope)
-
def load_session(self, username='', password=''):
default_cred = config.credentials.default
self.connection.login(
diff --git a/awxkit/awxkit/api/resources.py b/awxkit/awxkit/api/resources.py
index 57bf845f86..7b2d1003d3 100644
--- a/awxkit/awxkit/api/resources.py
+++ b/awxkit/awxkit/api/resources.py
@@ -12,10 +12,7 @@ class Resources(object):
_ad_hoc_related_cancel = r'ad_hoc_commands/\d+/cancel/'
_ad_hoc_relaunch = r'ad_hoc_commands/\d+/relaunch/'
_ansible_facts = r'hosts/\d+/ansible_facts/'
- _application = r'applications/\d+/'
- _applications = 'applications/'
_auth = 'auth/'
- _authtoken = 'authtoken/'
_bulk = 'bulk/'
_bulk_job_launch = 'bulk/job_launch/'
_config = 'config/'
@@ -233,8 +230,6 @@ class Resources(object):
_team_permissions = r'teams/\d+/permissions/'
_team_users = r'teams/\d+/users/'
_teams = 'teams/'
- _token = r'tokens/\d+/'
- _tokens = 'tokens/'
_unified_job_template = r'unified_job_templates/\d+/'
_unified_job_templates = 'unified_job_templates/'
_unified_jobs = 'unified_jobs/'
@@ -282,12 +277,7 @@ class Resources(object):
def __getattr__(self, resource):
if resource[:3] == '___':
raise AttributeError('No existing resource: {}'.format(resource))
- # Currently we don't handle anything under:
- # /api/o/
- # /api/login/
- # /api/logout/
- # If/when we do we will probably need to modify this __getattr__ method
- # Also, if we add another API version, this would be handled here
+ # If/when we add another API version, this would be handled here
prefix = 'v2'
resource = '_' + resource
return '{0}{1}'.format(getattr(self, prefix), getattr(self, resource))
diff --git a/awxkit/awxkit/awx/utils.py b/awxkit/awxkit/awx/utils.py
index c84a58b3b2..0b56fc22cb 100644
--- a/awxkit/awxkit/awx/utils.py
+++ b/awxkit/awxkit/awx/utils.py
@@ -73,7 +73,6 @@ def check_related(resource):
@contextmanager
def as_user(v, username, password=None):
"""Context manager to allow running tests as an alternative login user."""
- access_token = False
if not isinstance(v, api.client.Connection):
connection = v.connection
else:
@@ -83,11 +82,6 @@ def as_user(v, username, password=None):
password = username.password
username = username.username
- if isinstance(username, api.OAuth2AccessToken):
- access_token = username.token
- username = None
- password = None
-
try:
if config.use_sessions:
session_id = None
@@ -101,10 +95,7 @@ def as_user(v, username, password=None):
break
if session_id:
del connection.session.cookies[connection.session_cookie_name]
- if access_token:
- kwargs = dict(token=access_token)
- else:
- kwargs = connection.get_session_requirements()
+ kwargs = connection.get_session_requirements()
else:
previous_auth = connection.session.auth
kwargs = dict()
@@ -112,8 +103,6 @@ def as_user(v, username, password=None):
yield
finally:
if config.use_sessions:
- if access_token:
- connection.session.auth = None
del connection.session.cookies[connection.session_cookie_name]
if session_id:
connection.session.cookies.set(connection.session_cookie_name, session_id, domain=domain)
diff --git a/awxkit/awxkit/cli/client.py b/awxkit/awxkit/cli/client.py
index de790e184e..c06188c123 100755
--- a/awxkit/awxkit/cli/client.py
+++ b/awxkit/awxkit/cli/client.py
@@ -82,17 +82,9 @@ class CLI(object):
return '--help' in self.argv or '-h' in self.argv
def authenticate(self):
- """Configure the current session (or OAuth2.0 token)"""
- token = self.get_config('token')
- if token:
- self.root.connection.login(
- None,
- None,
- token=token,
- )
- else:
- config.use_sessions = True
- self.root.load_session().get()
+ """Configure the current session for basic auth"""
+ config.use_sessions = True
+ self.root.load_session().get()
def connect(self):
"""Fetch top-level resources from /api/v2"""
@@ -141,7 +133,7 @@ class CLI(object):
"""Attempt to parse the <resource> (e.g., jobs) specified on the CLI
If a valid resource is discovered, the user will be authenticated
- (either via an OAuth2.0 token or session-based auth) and the remaining
+ (via session-based auth) and the remaining
CLI arguments will be processed (to determine the requested action
e.g., list, create, delete)
diff --git a/awxkit/awxkit/cli/docs/source/usage.rst b/awxkit/awxkit/cli/docs/source/usage.rst
index c877019bc6..13d36d0c1d 100644
--- a/awxkit/awxkit/cli/docs/source/usage.rst
+++ b/awxkit/awxkit/cli/docs/source/usage.rst
@@ -88,6 +88,3 @@ A few of the most important ones are:
``--conf.password, CONTROLLER_PASSWORD``
the AWX password to use for authentication
-
-``--conf.token, CONTROLLER_OAUTH_TOKEN``
- an OAuth2.0 token to use for authentication
diff --git a/awxkit/awxkit/cli/format.py b/awxkit/awxkit/cli/format.py
index de2de47262..f294a9e045 100644
--- a/awxkit/awxkit/cli/format.py
+++ b/awxkit/awxkit/cli/format.py
@@ -30,12 +30,6 @@ def add_authentication_arguments(parser, env):
default=env.get('CONTROLLER_HOST', env.get('TOWER_HOST', 'https://127.0.0.1:443')),
metavar='https://example.awx.org',
)
- auth.add_argument(
- '--conf.token',
- default=env.get('CONTROLLER_OAUTH_TOKEN', env.get('CONTROLLER_TOKEN', env.get('TOWER_OAUTH_TOKEN', env.get('TOWER_TOKEN', '')))),
- help='an OAuth2.0 token (get one by using `awx login`)',
- metavar='TEXT',
- )
config_username, config_password = get_config_credentials()
# options configured via cli args take higher precedence than those from the config
diff --git a/awxkit/awxkit/cli/resource.py b/awxkit/awxkit/cli/resource.py
index 05e8163059..621c76798a 100644
--- a/awxkit/awxkit/cli/resource.py
+++ b/awxkit/awxkit/cli/resource.py
@@ -1,14 +1,12 @@
import yaml
import json
-import os
-from awxkit import api, config, yaml_file
+from awxkit import config, yaml_file
from awxkit.exceptions import ImportExportError
-from awxkit.utils import to_str
from awxkit.api.pages import Page
from awxkit.api.pages.api import EXPORTABLE_RESOURCES
-from awxkit.cli.format import FORMATTERS, format_response, add_authentication_arguments, add_formatting_import_export
-from awxkit.cli.utils import CustomRegistryMeta, cprint
+from awxkit.cli.format import format_response, add_formatting_import_export
+from awxkit.cli.utils import CustomRegistryMeta
CONTROL_RESOURCES = ['ping', 'config', 'me', 'metrics', 'mesh_visualizer']
@@ -66,44 +64,6 @@ class CustomCommand(metaclass=CustomRegistryMeta):
raise NotImplementedError()
-class Login(CustomCommand):
- name = 'login'
- help_text = 'authenticate and retrieve an OAuth2 token'
-
- def print_help(self, parser):
- add_authentication_arguments(parser, os.environ)
- parser.print_help()
-
- def handle(self, client, parser):
- auth = parser.add_argument_group('OAuth2.0 Options')
- auth.add_argument('--description', help='description of the generated OAuth2.0 token', metavar='TEXT')
- auth.add_argument('--conf.client_id', metavar='TEXT')
- auth.add_argument('--conf.client_secret', metavar='TEXT')
- auth.add_argument('--conf.scope', choices=['read', 'write'], default='write')
- if client.help:
- self.print_help(parser)
- raise SystemExit()
- parsed = parser.parse_known_args()[0]
- kwargs = {
- 'client_id': getattr(parsed, 'conf.client_id', None),
- 'client_secret': getattr(parsed, 'conf.client_secret', None),
- 'scope': getattr(parsed, 'conf.scope', None),
- }
- if getattr(parsed, 'description', None):
- kwargs['description'] = parsed.description
- try:
- token = api.Api().get_oauth2_token(**kwargs)
- except Exception as e:
- self.print_help(parser)
- cprint('Error retrieving an OAuth2.0 token ({}).'.format(e.__class__), 'red')
- else:
- fmt = client.get_config('format')
- if fmt == 'human':
- print('export CONTROLLER_OAUTH_TOKEN={}'.format(token))
- else:
- print(to_str(FORMATTERS[fmt]({'token': token}, '.')).strip())
-
-
class Config(CustomCommand):
name = 'config'
help_text = 'print current configuration values'
@@ -114,7 +74,6 @@ class Config(CustomCommand):
raise SystemExit()
return {
'base_url': config.base_url,
- 'token': client.get_config('token'),
'use_sessions': config.use_sessions,
'credentials': config.credentials,
}
diff --git a/awxkit/awxkit/ws.py b/awxkit/awxkit/ws.py
index ac4b37453a..78c58bf2d2 100644
--- a/awxkit/awxkit/ws.py
+++ b/awxkit/awxkit/ws.py
@@ -37,7 +37,7 @@ class WSClient(object):
'control': ['limit_reached']}
e.x:
```
- ws = WSClient(token, port=8013, secure=False).connect()
+ ws = WSClient(port=8013, secure=False, session_id='xyz', csrftoken='abc').connect()
ws.job_details()
... # launch job
job_messages = [msg for msg in ws]
@@ -52,7 +52,6 @@ class WSClient(object):
def __init__(
self,
- token=None,
hostname='',
port=443,
secure=True,
@@ -80,15 +79,12 @@ class WSClient(object):
self.suffix = ws_suffix
self._use_ssl = secure
self.hostname = hostname
- self.token = token
self.session_id = session_id
self.csrftoken = csrftoken
self._recv_queue = Queue()
self._ws_closed = False
self._ws_connected_flag = threading.Event()
- if self.token is not None:
- auth_cookie = 'token="{0.token}";'.format(self)
- elif self.session_id is not None:
+ if self.session_id is not None:
auth_cookie = '{1}="{0.session_id}"'.format(self, session_cookie_name)
if self.csrftoken:
auth_cookie += ';csrftoken={0.csrftoken}'.format(self)
diff --git a/awxkit/test/api/pages/test_base.py b/awxkit/test/api/pages/test_base.py
index 6706d950d4..3126e333d8 100644
--- a/awxkit/test/api/pages/test_base.py
+++ b/awxkit/test/api/pages/test_base.py
@@ -1,9 +1,6 @@
from http.client import NOT_FOUND
import pytest
-from pytest_mock import MockerFixture
-from requests import Response
-from awxkit.api.pages import Base
from awxkit.config import config
@@ -22,38 +19,3 @@ def response(mocker):
"access_token": "my_token",
}
return r
-
-
-@pytest.mark.parametrize(
- ("auth_creds", "url", "token"),
- [
- ({"client_id": "foo", "client_secret": "bar"}, "/o/token/", "my_token"),
- ({"client_id": "foo"}, "/o/token/", "my_token"),
- ({}, "/api/gateway/v1/tokens/", "my_personal_token"),
- ],
-)
-def test_get_oauth2_token_from_gateway(mocker: MockerFixture, response: Response, auth_creds, url, token):
- post = mocker.patch("requests.Session.post", return_value=response)
- base = Base()
- ret = base.get_oauth2_token(**auth_creds)
- assert post.call_count == 1
- assert post.call_args.args[0] == url
- assert ret == token
-
-
-@pytest.mark.parametrize(
- ("auth_creds", "url", "token"),
- [
- ({"client_id": "foo", "client_secret": "bar"}, "/api/o/token/", "my_token"),
- ({"client_id": "foo"}, "/api/o/token/", "my_token"),
- ({}, "/api/v2/users/foo/personal_tokens/", "my_personal_token"),
- ],
-)
-def test_get_oauth2_token_from_controller(mocker: MockerFixture, response: Response, auth_creds, url, token):
- type(response).ok = mocker.PropertyMock(side_effect=[False, True])
- post = mocker.patch("requests.Session.post", return_value=response)
- base = Base()
- ret = base.get_oauth2_token(**auth_creds)
- assert post.call_count == 2
- assert post.call_args.args[0] == url
- assert ret == token
diff --git a/awxkit/test/test_ws.py b/awxkit/test/test_ws.py
index c2e69fbc51..f40767b357 100644
--- a/awxkit/test/test_ws.py
+++ b/awxkit/test/test_ws.py
@@ -10,11 +10,10 @@ ParseResult = namedtuple("ParseResult", ["port", "hostname", "secure"])
def test_explicit_hostname():
- client = WSClient("token", "some-hostname", 556, False)
+ client = WSClient(hostname="some-hostname", port=556, secure=False)
assert client.port == 556
assert client.hostname == "some-hostname"
assert client._use_ssl == False
- assert client.token == "token"
def test_websocket_suffix():
@@ -35,7 +34,7 @@ def test_urlparsing(url, result):
with patch("awxkit.ws.config") as mock_config:
mock_config.base_url = url
- client = WSClient("token")
+ client = WSClient(hostname=None)
assert client.port == result.port
assert client.hostname == result.hostname
assert client._use_ssl == result.secure