Repository: ansible/tower-cli
Pull request #483 (2018-03-02 19:11:46)

Make demo script run again
This flag was removed, it never worked.


Patch:
diff --git a/docs/source/api_ref/conf.rst b/docs/source/api_ref/conf.rst
index 1bd5adb..4bb900f 100644
--- a/docs/source/api_ref/conf.rst
+++ b/docs/source/api_ref/conf.rst
@@ -29,7 +29,7 @@ available Tower CLI settings:
 +------------------+-------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
 |``certificate``   | String/''                                             | Path to a custom certificate file that will be used throughout the command. Ignored if `--insecure` flag if set in command or `verify_ssl` is set to false |
 +------------------+-------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
-|``use_token``     | Boolean/'false'                                       | Whether to use token-based authentication.  No longer supported in Tower 3.3 and above                                                                     |
+|``use_token``     | Boolean/'false'                                       | Whether to use token-based authentication.                                                                                                                 |
 +------------------+-------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------+
 
 **Note:** Some settings are marked as 'CLI use only', this means although users are free to set values to those
diff --git a/docs/source/cli_ref/index.rst b/docs/source/cli_ref/index.rst
index 19797c6..ab0f667 100644
--- a/docs/source/cli_ref/index.rst
+++ b/docs/source/cli_ref/index.rst
@@ -166,6 +166,8 @@ and ``tower-cli {resource} {action} --help`` shows details of the usage of this
                          False]
 
     Global Options:
+      --use-token                     Turn on Tower's token-based authentication.
+                                      Set config use_token to make this permanent.
       --certificate TEXT              Path to a custom certificate file that will
                                       be used throughout the command. Overwritten
                                       by --insecure flag if set.
@@ -190,8 +192,6 @@ and ``tower-cli {resource} {action} --help`` shows details of the usage of this
                                       "http://" is explicitly provided. This will
                                       take precedence over a host provided to
                                       `tower config`, if any.
-      --use-token                     Turn on Tower's token-based authentication.
-                                      No longer supported in Tower 3.3 and above
 
     Other Options:
       --help  Show this message and exit.
diff --git a/docs_deprecated/examples/fake_data_creator.sh b/docs_deprecated/examples/fake_data_creator.sh
index ae90591..d9a5284 100644
--- a/docs_deprecated/examples/fake_data_creator.sh
+++ b/docs_deprecated/examples/fake_data_creator.sh
@@ -177,7 +177,7 @@ tower-cli job_template create --name=Apache --description="Confgure Apache serve
 
 echo "Tower-CLI DATA FAKER: run a job, check status, cancel, and run with monitoring"
 # Launch job without monitoring
-tower-cli job launch --job-template="Hello World Debug" --job-explanation="launched by example script"
+tower-cli job launch --job-template="Hello World Debug"
 # Note that these only work because there are no other completed jobs from that template
 # If that is not true, you need to run "job list" and then cancel with the ID
 tower-cli job status --job-template="Hello World Debug" --status="running"
@@ -186,7 +186,7 @@ tower-cli job cancel --job-template="Hello World Debug" --status="running"
 # With delete, we remove the record of this job's run. For instance:
 # tower-cli job delete {pk}
 # launch a job with monitoring turned on
-tower-cli job launch --job-template="Hello World Debug" --monitor --job-explanation="launched by example script"
+tower-cli job launch --job-template="Hello World Debug" --monitor
 
 echo "Tower-CLI DATA FAKER: displaying jobs that have run via the fake data script"
 tower-cli job list --job-template="Hello World Debug"
diff --git a/tower_cli/api.py b/tower_cli/api.py
index 37e3630..dfe4eab 100644
--- a/tower_cli/api.py
+++ b/tower_cli/api.py
@@ -18,7 +18,6 @@ import contextlib
 import copy
 import functools
 import json
-import stat
 import warnings
 from datetime import datetime as dt
 
@@ -26,7 +25,7 @@ from requests.exceptions import ConnectionError, SSLError
 from requests.sessions import Session
 from requests.models import Response
 from requests.packages import urllib3
-from requests.auth import AuthBase, HTTPBasicAuth
+from requests.auth import AuthBase
 
 from tower_cli import exceptions as exc
 from tower_cli.conf import settings
@@ -37,13 +36,12 @@ from tower_cli.constants import CUR_API_VERSION
 TOWER_DATETIME_FMT = r'%Y-%m-%dT%H:%M:%S.%fZ'
 
 
-class BasicTowerAuth(AuthBase):
+class TowerTokenAuth(AuthBase):
 
     def __init__(self, username, password, cli_client):
         self.username = username
         self.password = password
         self.cli_client = cli_client
-        self.use_legacy_token = settings.use_token
 
     def _acquire_token(self):
         return self.cli_client._make_request(
@@ -68,30 +66,10 @@ class BasicTowerAuth(AuthBase):
                 raise exc.AuthError('Invalid Tower auth token format: %s' % json.dumps(token_json))
             with open(filename, 'w') as f:
                 json.dump(token_json, f)
-            try:
-                os.chmod(filename, stat.S_IRUSR | stat.S_IWUSR)
-            except Exception as e:
-                warnings.warn(
-                    'Unable to set permissions on {0} - {1} '.format(filename, e),
-                    UserWarning
-                )
             return 'Token ' + token_json['token']
 
     def __call__(self, r):
-        if self.use_legacy_token:
-            resp = self.cli_client._make_request(
-                'OPTIONS', self.cli_client.prefix + 'authtoken/', [], {}
-            )
-            if resp.ok:
-                r.headers['Authorization'] = self._get_auth_token()
-            else:
-                warnings.warn(
-                    'use_token is not supported in this version of Tower '
-                    '(the Auth Token API has been replaced with OAuth2.0 support)',
-                )
-                HTTPBasicAuth(self.username, self.password)(r)
-        else:
-            HTTPBasicAuth(self.username, self.password)(r)
+        r.headers['Authorization'] = self._get_auth_token()
         return r
 
 
@@ -183,11 +161,12 @@ class Client(Session):
         # from settings if it's provided.
         kwargs.setdefault(
             'auth',
-            BasicTowerAuth(
+            TowerTokenAuth(
                 settings.username,
                 settings.password,
                 self
-            )
+            ) if settings.use_token else (settings.username,
+                                          settings.password)
         )
 
         # POST and PUT requests will send JSON by default; make this
diff --git a/tower_cli/cli/base.py b/tower_cli/cli/base.py
index 569391e..440030a 100644
--- a/tower_cli/cli/base.py
+++ b/tower_cli/cli/base.py
@@ -38,60 +38,16 @@ class TowerCLI(click.MultiCommand):
     def _get_all_misc_cmds(self):
         pass
 
-    def format_command_subsection(self, ctx, formatter, commands, header):
-        """Writes help text for a sub-section of commands,
-        specifically to be reused for resource commands
-        and system/configuration commands.
-        """
-        rows = []
-        for subcommand in commands:
-            cmd = self.get_command(ctx, subcommand)
-            # What is this, the tool lied about a command.  Ignore it
-            if cmd is None:
-                continue
-
-            help = cmd.short_help or ''
-            rows.append((subcommand, help))
-
-        if rows:
-            with formatter.section(header):
-                formatter.write_dl(rows)
-
-    def format_commands(self, ctx, formatter):
-        """Extra format methods for multi methods that adds all the commands
-        after the options.
-        """
-        self.format_command_subsection(
-            ctx, formatter, self.list_misc_commands(), 'Commands'
-        )
-        self.format_command_subsection(
-            ctx, formatter, self.list_resource_commands(), 'Resources'
-        )
-
     def list_commands(self, ctx):
         """Return a list of commands present in the commands and resources
         folders, but not subcommands.
         """
-        commands = set(self.list_resource_commands())
-        commands.union(set(self.list_misc_commands()))
-        return sorted(commands)
-
-    def list_resource_commands(self):
-        """Returns a list of multi-commands for each resource type.
-        """
         resource_path = os.path.abspath(os.path.join(
             os.path.dirname(__file__),
             os.pardir,
             'resources'
         ))
         answer = set([name for _, name, _ in pkgutil.iter_modules([resource_path])])
-        return sorted(answer)
-
-    def list_misc_commands(self):
-        """Returns a list of global commands, realted to CLI
-        configuration or system management in general.
-        """
-        answer = set([])
         for cmd_name in misc.__all__:
             answer.add(cmd_name)
         return sorted(answer)
diff --git a/tower_cli/conf.py b/tower_cli/conf.py
index 22e8a5a..1a7df2b 100644
--- a/tower_cli/conf.py
+++ b/tower_cli/conf.py
@@ -59,7 +59,7 @@ class Parser(configparser.ConfigParser):
         # other users, raise a warning
         if os.path.isfile(fpname):
             file_permission = os.stat(fpname)
-            if fpname != os.path.join(tower_dir, 'tower_cli.cfg') and (
+            if fpname != os.path.join(tower_dir, CONFIG_FILENAME) and (
                 (file_permission.st_mode & stat.S_IRGRP) or
                 (file_permission.st_mode & stat.S_IROTH)
             ):
@@ -162,7 +162,7 @@ class Settings(object):
 
             # If there is a global settings file for Tower CLI, read in its
             # contents.
-            self._global.read(os.path.join(tower_dir, 'tower_cli.cfg'))
+            self._global.read(os.path.join(tower_dir, CONFIG_FILENAME))
 
         # Initialize a parser for the user settings file.
         self._user = self._new_parser()
@@ -446,8 +446,8 @@ def with_global_options(method):
     method = click.option(
         '--use-token',
         default=None,
-        help='Turn on Tower\'s token-based authentication. No longer supported '
-             'in Tower 3.3 and above.',
+        help='Turn on Tower\'s token-based authentication. Set config'
+             ' use_token to make this permanent.',
         is_flag=True,
         required=False, callback=_apply_runtime_setting,
         is_eager=True
diff --git a/tower_cli/resources/role.py b/tower_cli/resources/role.py
index 9f26216..39171be 100644
--- a/tower_cli/resources/role.py
+++ b/tower_cli/resources/role.py
@@ -191,13 +191,10 @@ class Resource(models.Resource):
             item_dict['resource_name'] = None
             item_dict['resource_type'] = None
         else:
-            sf = item_dict['summary_fields']
-            # Explination of fallback state:
-            # The situation where resource_name or resource_type is not present
-            # should not be seen for singleton roles, and where it is seen,
-            # there may be a problem with data quality on the server
-            item_dict['resource_name'] = sf.get('resource_name', '[unknown]')
-            item_dict['resource_type'] = sf.get('resource_type', '[unknown]')
+            item_dict['resource_name'] = item_dict[
+                'summary_fields']['resource_name']
+            item_dict['resource_type'] = item_dict[
+                'summary_fields']['resource_type']
 
     def set_display_columns(self, set_true=[], set_false=[]):
         """Add or remove columns from the output."""


Test patch:
diff --git a/tests/test_api.py b/tests/test_api.py
index 5d4b8aa..a962041 100644
--- a/tests/test_api.py
+++ b/tests/test_api.py
@@ -19,7 +19,7 @@ from datetime import datetime as dt, timedelta
 import requests
 from requests.sessions import Session
 
-from tower_cli.api import APIResponse, client, BasicTowerAuth,\
+from tower_cli.api import APIResponse, client, TowerTokenAuth,\
         TOWER_DATETIME_FMT
 from tower_cli import exceptions as exc
 from tower_cli.conf import settings
@@ -180,9 +180,9 @@ class ClientTests(unittest.TestCase):
                 with settings.runtime_values(verify_ssl=False):
                     client.get('/ping/')
                     g.assert_called_once_with(
-                        # The point is to assure verify=False below
+                        # The pont is to assure verify=False below
                         'GET', mock.ANY, allow_redirects=True,
-                        auth=mock.ANY, verify=False
+                        auth=(mock.ANY, mock.ANY), verify=False
                     )
 
     def test_http_contradiction_error(self):
@@ -205,15 +205,14 @@ class ClientTests(unittest.TestCase):
                     self.assertTrue(secho.called)
 
 
-class TowerAuthTokenTests(unittest.TestCase):
+class TowerTokenAuthTests(unittest.TestCase):
     def setUp(self):
 
         class Req(object):
             def __init__(self):
                 self.headers = {}
 
-        with settings.runtime_values(use_token=True):
-            self.auth = BasicTowerAuth('alice', 'pass', client)
+        self.auth = TowerTokenAuth('alice', 'pass', client)
         self.req = Req()
         self.expires = dt.utcnow()
 
@@ -222,10 +221,8 @@ class TowerAuthTokenTests(unittest.TestCase):
         expires = self.expires.strftime(TOWER_DATETIME_FMT)
         with mock.patch('six.moves.builtins.open', new_callable=mock.mock_open()):
             with mock.patch('tower_cli.api.json.load', return_value={'token': 'foobar', 'expires': expires}):
-                with client.test_mode as t:
-                    t.register('/authtoken/', json.dumps({}), status_code=200, method='OPTIONS')
-                    self.auth(self.req)
-                    self.assertEqual(self.req.headers['Authorization'], 'Token foobar')
+                self.auth(self.req)
+                self.assertEqual(self.req.headers['Authorization'], 'Token foobar')
 
     def test_reading_invalid_token(self):
         self.expires += timedelta(hours=1)
@@ -233,7 +230,6 @@ class TowerAuthTokenTests(unittest.TestCase):
         with mock.patch('six.moves.builtins.open', new_callable=mock.mock_open()):
             with mock.patch('tower_cli.api.json.load', return_value="invalid"):
                 with client.test_mode as t:
-                    t.register('/authtoken/', json.dumps({}), status_code=200, method='OPTIONS')
                     t.register('/authtoken/', json.dumps({'token': 'barfoo', 'expires': expires}), status_code=200,
                                method='POST')
                     self.auth(self.req)
@@ -245,7 +241,6 @@ class TowerAuthTokenTests(unittest.TestCase):
         with mock.patch('six.moves.builtins.open', new_callable=mock.mock_open()):
             with mock.patch('tower_cli.api.json.load', return_value={'token': 'foobar', 'expires': expires}):
                 with client.test_mode as t:
-                    t.register('/authtoken/', json.dumps({}), status_code=200, method='OPTIONS')
                     t.register('/authtoken/', json.dumps({'token': 'barfoo', 'expires': expires}), status_code=200,
                                method='POST')
                     self.auth(self.req)
@@ -258,16 +253,5 @@ class TowerAuthTokenTests(unittest.TestCase):
             with mock.patch('tower_cli.api.json.load', return_value={'token': 'foobar', 'expires': expires}):
                 with client.test_mode as t:
                     with self.assertRaises(exc.AuthError):
-                        t.register('/authtoken/', json.dumps({}), status_code=200, method='OPTIONS')
                         t.register('/authtoken/', json.dumps({'invalid': 'invalid'}), status_code=200, method='POST')
                         self.auth(self.req)
-
-    def test_auth_token_unsupported(self):
-        # If the user specifies `use_token=True`, but `/authtoken/` doesn't
-        # exist (in Tower 3.3 and beyond), just fall back to basic auth
-        with client.test_mode as t:
-            with settings.runtime_values(use_token=True):
-                t.register('/authtoken/', json.dumps({}), status_code=404, method='OPTIONS')
-                auth = BasicTowerAuth('alice', 'pass', client)
-                auth(self.req)
-                assert self.req.headers == {'Authorization': 'Basic YWxpY2U6cGFzcw=='}