interactive_get_token()   F
last analyzed

Complexity

Conditions 11

Size

Total Lines 80

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 11
c 1
b 0
f 1
dl 0
loc 80
rs 3.1764

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like interactive_get_token() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
Authenticate a user
3
"""
4
from __future__ import unicode_literals
5
6
import getpass
7
import logging
8
import platform
9
import socket
10
import sys
11
12
from six.moves.urllib.parse import urlparse
13
from six.moves import input
14
15
from binstar_client import errors
16
from binstar_client.utils import get_config, get_server_api, store_token, bool_input
17
18
19
logger = logging.getLogger('binstar.login')
20
21
22
def try_replace_token(authenticate, **kwargs):
23
    """
24
    Authenticates using the given *authenticate*, retrying if the token needs
25
    to be replaced.
26
    """
27
28
    try:
29
        return authenticate(**kwargs)
30
    except errors.BinstarError as err:
31
        if kwargs.get('fail_if_already_exists') and len(err.args) > 1 and err.args[1] == 400:
32
            logger.warning('It appears you are already logged in from host %s' % socket.gethostname())
33
            logger.warning('Logging in again will remove the previous token. (This could cause troubles with virtual '
34
                           'machines with the same hostname)')
35
            logger.warning('Otherwise you can login again and specify a different hostname with "--hostname"')
36
37
            if bool_input("Would you like to continue"):
38
                kwargs['fail_if_already_exists'] = False
39
                return authenticate(**kwargs)
40
41
        raise
42
43
44
def interactive_get_token(args, fail_if_already_exists=True):
45
    bs = get_server_api(args.token, args.site)
46
    config = get_config(site=args.site)
47
48
    token = None
49
    # This function could be called from a totally different CLI, so we don't
50
    # know if the attribute hostname exists.
51
    hostname = getattr(args, 'hostname', platform.node())
52
    site = args.site or config.get('default_site')
53
    url = config.get('url', 'https://api.anaconda.org')
54
55
    auth_name = 'binstar_client:'
56
    if site and site not in ('binstar', 'anaconda'):
57
        # For testing with binstar alpha site
58
        auth_name += '%s:' % site
59
60
    auth_name += '%s@%s' % (getpass.getuser(), hostname)
61
62
    bs.check_server()
63
    auth_type = bs.authentication_type()
64
65
    if auth_type == 'kerberos':
66
        token = try_replace_token(
67
            bs.krb_authenticate,
68
            application=auth_name,
69
            application_url=url,
70
            created_with=' '.join(sys.argv),
71
            fail_if_already_exists=fail_if_already_exists,
72
            hostname=hostname,
73
        )
74
75
        if token is None:
76
            raise errors.BinstarError(
77
                'Unable to authenticate via Kerberos. Try refreshing your '
78
                'authentication using `kinit`')
79
    else:
80
        if getattr(args, 'login_username', None):
81
            username = args.login_username
82
        else:
83
            username = input('Username: ')
84
85
        password = getattr(args, 'login_password', None)
86
87
        for _ in range(3):
88
            try:
89
                sys.stderr.write("%s's " % username)
90
91
                if password is None:
92
                    password = getpass.getpass(stream=sys.stderr)
93
94
                token = try_replace_token(
95
                    bs.authenticate,
96
                    username=username,
97
                    password=password,
98
                    application=auth_name,
99
                    application_url=url,
100
                    created_with=' '.join(sys.argv),
101
                    fail_if_already_exists=fail_if_already_exists,
102
                    hostname=hostname,
103
                )
104
                break
105
106
            except errors.Unauthorized:
107
                logger.error('Invalid Username password combination, please try again')
108
                password = None
109
                continue
110
111
        if token is None:
112
            parsed_url = urlparse(url)
113
            if parsed_url.netloc.startswith('api.anaconda.org'):
114
                netloc = 'anaconda.org'
115
            else:
116
                netloc = parsed_url.netloc
117
            hostparts = (parsed_url.scheme, netloc)
118
            msg = ('Sorry. Please try again ' + \
119
                   '(go to %s://%s/account/forgot_password ' % hostparts + \
120
                   'to reset your password)')
121
            raise errors.BinstarError(msg)
122
123
    return token
124
125
126
def interactive_login(args):
127
    token = interactive_get_token(args)
128
    store_token(token, args)
129
    logger.info('login successful')
130
131
132
def main(args):
133
    interactive_login(args)
134
135
136
def add_parser(subparsers):
137
    subparser = subparsers.add_parser('login', help='Authenticate a user', description=__doc__)
138
    subparser.add_argument('--hostname', default=platform.node(),
139
                           help="Specify the host name of this login, this should be unique (default: %(default)s)")
140
    subparser.add_argument('--username', dest='login_username',
141
                           help="Specify your username. If this is not given, you will be prompted")
142
    subparser.add_argument('--password', dest='login_password',
143
                           help="Specify your password. If this is not given, you will be prompted")
144
    subparser.set_defaults(main=main)
145