1
0
mirror of https://github.com/httpie/cli.git synced 2024-11-24 08:22:22 +02:00

Added support for auth plugins.

This commit is contained in:
Jakub Roztocil 2013-09-21 23:46:15 +02:00
parent f7b703b4bf
commit 2acb303552
11 changed files with 159 additions and 25 deletions

View File

@ -514,6 +514,13 @@ Authorization information from your ``.netrc`` file is honored as well:
[...]
-------
Plugins
-------
* `httpie-ntlm <https://github.com/jkbr/httpie-ntlm>`_
=======
Proxies
=======
@ -1206,6 +1213,7 @@ Changelog
* `0.7.0-dev`_
* Added ``--ignore-stdin``.
* Added support for auth plugins.
* `0.6.0`_ (2013-06-03)
* XML data is now formatted.
* ``--session`` and ``--session-read-only`` now also accept paths to

View File

@ -3,7 +3,7 @@ HTTPie - a CLI, cURL-like tool for humans.
"""
__author__ = 'Jakub Roztocil'
__version__ = '0.6.0'
__version__ = '0.7.0'
__licence__ = 'BSD'

View File

@ -10,6 +10,8 @@ from argparse import (RawDescriptionHelpFormatter, FileType,
from . import __doc__
from . import __version__
from .plugins.builtin import BuiltinAuthPlugin
from .plugins import plugin_manager
from .sessions import DEFAULT_SESSIONS_DIR
from .output import AVAILABLE_STYLES, DEFAULT_STYLE
from .input import (Parser, AuthCredentialsArgType, KeyValueArgType,
@ -381,14 +383,32 @@ auth.add_argument(
""",
)
_auth_plugins = plugin_manager.get_auth_plugins()
auth.add_argument(
'--auth-type',
choices=['basic', 'digest'],
default='basic',
choices=[plugin.auth_type for plugin in _auth_plugins],
default=_auth_plugins[0].auth_type,
help="""
The authentication mechanism to be used. Defaults to "basic".
The authentication mechanism to be used. Defaults to "{default}".
{types}
"""
.format(default=_auth_plugins[0].auth_type, types='\n '.join(
'"{type}": {name}{package}{description}'.format(
type=plugin.auth_type,
name=plugin.name,
package=(
'' if issubclass(plugin, BuiltinAuthPlugin)
else ' (provided by %s)' % plugin.package_name
),
description=(
'' if not plugin.description else
'\n ' + ('\n '.join(wrap(plugin.description)))
)
)
for plugin in _auth_plugins
)),
)

View File

@ -7,6 +7,7 @@ import requests.auth
from . import sessions
from . import __version__
from .plugins import plugin_manager
FORM = 'application/x-www-form-urlencoded; charset=utf-8'
@ -14,6 +15,12 @@ JSON = 'application/json; charset=utf-8'
DEFAULT_UA = 'HTTPie/%s' % __version__
class HTTPie(object):
def __init__(self, env, plugin_manager):
pass
def get_response(args, config_dir):
"""Send the request and return a `request.Response`."""
@ -27,6 +34,7 @@ def get_response(args, config_dir):
response = requests.request(**requests_kwargs)
else:
response = sessions.get_response(
args=args,
config_dir=config_dir,
session_name=args.session or args.session_read_only,
requests_kwargs=requests_kwargs,
@ -69,10 +77,8 @@ def get_requests_kwargs(args):
credentials = None
if args.auth:
credentials = {
'basic': requests.auth.HTTPBasicAuth,
'digest': requests.auth.HTTPDigestAuth,
}[args.auth_type](args.auth.key, args.auth.value)
auth_plugin = plugin_manager.get_auth_plugin(args.auth_type)()
credentials = auth_plugin.get_auth(args.auth.key, args.auth.value)
kwargs = {
'stream': True,

View File

@ -18,13 +18,13 @@ from httpie import __version__ as httpie_version
from requests import __version__ as requests_version
from pygments import __version__ as pygments_version
from .cli import parser
from .compat import str, is_py3
from .client import get_response
from .downloads import Download
from .models import Environment
from .output import build_output_stream, write, write_with_colors_win_py3
from . import ExitStatus
from .plugins import plugin_manager
def get_exit_status(http_status, follow=False):
@ -58,6 +58,9 @@ def main(args=sys.argv[1:], env=Environment()):
Return exit status code.
"""
plugin_manager.load_installed_plugins()
from .cli import parser
if env.config.default_options:
args = env.config.default_options + args

View File

@ -6,7 +6,7 @@ import sys
import re
import json
import mimetypes
import getpass
from getpass import getpass
from io import BytesIO
#noinspection PyCompatibility
from argparse import ArgumentParser, ArgumentTypeError, ArgumentError

View File

@ -0,0 +1,9 @@
from .base import AuthPlugin
from .manager import PluginManager
from .builtin import BasicAuthPlugin, DigestAuthPlugin
plugin_manager = PluginManager()
plugin_manager.register(BasicAuthPlugin)
plugin_manager.register(DigestAuthPlugin)

28
httpie/plugins/base.py Normal file
View File

@ -0,0 +1,28 @@
class AuthPlugin(object):
"""
Base auth plugin class.
See <https://github.com/jkbr/httpie-ntlm> for an example auth plugin.
"""
# The value that should be passed to --auth-type
# to use this auth plugin. Eg. "my-auth"
auth_type = None
# The name of the plugin, eg. "My auth".
name = None
# Optional short description. Will be be shown in the help
# under --auth-type.
description = None
# This be set automatically once the plugin has been loaded.
package_name = None
def get_auth(self, username, password):
"""
Return a ``requests.auth.AuthBase`` subclass instance.
"""
raise NotImplementedError()

26
httpie/plugins/builtin.py Normal file
View File

@ -0,0 +1,26 @@
import requests.auth
from .base import AuthPlugin
class BuiltinAuthPlugin(AuthPlugin):
package_name = '(builtin)'
class BasicAuthPlugin(BuiltinAuthPlugin):
name = 'Basic HTTP auth'
auth_type = 'basic'
def get_auth(self, username, password):
return requests.auth.HTTPBasicAuth(username, password)
class DigestAuthPlugin(BuiltinAuthPlugin):
name = 'Digest HTTP auth'
auth_type = 'digest'
def get_auth(self, username, password):
return requests.auth.HTTPDigestAuth(username, password)

35
httpie/plugins/manager.py Normal file
View File

@ -0,0 +1,35 @@
from pkg_resources import iter_entry_points
ENTRY_POINT_NAMES = [
'httpie.plugins.auth.v1'
]
class PluginManager(object):
def __init__(self):
self._plugins = []
def __iter__(self):
return iter(self._plugins)
def register(self, plugin):
self._plugins.append(plugin)
def get_auth_plugins(self):
return list(self._plugins)
def get_auth_plugin_mapping(self):
return dict((plugin.auth_type, plugin) for plugin in self)
def get_auth_plugin(self, auth_type):
return self.get_auth_plugin_mapping()[auth_type]
def load_installed_plugins(self):
for entry_point_name in ENTRY_POINT_NAMES:
for entry_point in iter_entry_points(entry_point_name):
plugin = entry_point.load()
plugin.package_name = entry_point.dist.key
self.register(entry_point.load())

View File

@ -6,10 +6,10 @@ import os
import requests
from requests.cookies import RequestsCookieJar, create_cookie
from requests.auth import HTTPBasicAuth, HTTPDigestAuth
from .compat import urlsplit
from .config import BaseConfigDict, DEFAULT_CONFIG_DIR
from httpie.plugins import plugin_manager
SESSIONS_DIR_NAME = 'sessions'
@ -21,7 +21,8 @@ VALID_SESSION_NAME_PATTERN = re.compile('^[a-zA-Z0-9_.-]+$')
SESSION_IGNORED_HEADER_PREFIXES = ['Content-', 'If-']
def get_response(session_name, requests_kwargs, config_dir, read_only=False):
def get_response(session_name, requests_kwargs, config_dir, args,
read_only=False):
"""Like `client.get_response`, but applies permanent
aspects of the session to the request.
@ -50,9 +51,12 @@ def get_response(session_name, requests_kwargs, config_dir, read_only=False):
requests_kwargs['headers'] = dict(session.headers, **request_headers)
session.update_headers(request_headers)
auth = requests_kwargs.get('auth', None)
if auth:
session.auth = auth
if args.auth:
session.auth = {
'type': args.auth_type,
'username': args.auth.key,
'password': args.auth.value,
}
elif session.auth:
requests_kwargs['auth'] = session.auth
@ -140,15 +144,10 @@ class Session(BaseConfigDict):
auth = self.get('auth', None)
if not auth or not auth['type']:
return
Auth = {'basic': HTTPBasicAuth,
'digest': HTTPDigestAuth}[auth['type']]
return Auth(auth['username'], auth['password'])
auth_plugin = plugin_manager.get_auth_plugin(auth['type'])()
return auth_plugin.get_auth(auth['username'], auth['password'])
@auth.setter
def auth(self, cred):
self['auth'] = {
'type': {HTTPBasicAuth: 'basic',
HTTPDigestAuth: 'digest'}[type(cred)],
'username': cred.username,
'password': cred.password,
}
def auth(self, auth):
assert set(['type', 'username', 'password']) == set(auth.keys())
self['auth'] = auth