Completed
Pull Request — master (#2920)
by Anthony
04:27
created

AuthHandlerBase   A

Complexity

Total Complexity 2

Size/Duplication

Total Lines 8
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 8
rs 10
wmc 2

2 Methods

Rating   Name   Duplication   Size   Complexity  
A handle_auth() 0 3 1
A _create_token_for_user() 0 3 1
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain a copy of the License at
7
#
8
#     http://www.apache.org/licenses/LICENSE-2.0
9
#
10
# Unless required by applicable law or agreed to in writing, software
11
# distributed under the License is distributed on an "AS IS" BASIS,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import pecan
0 ignored issues
show
Unused Code introduced by
The import pecan seems to be unused.
Loading history...
17
import base64
18
import logging
19
from six.moves import http_client
20
from oslo_config import cfg
21
22
from st2common.exceptions.auth import TTLTooLargeException, UserNotFoundError
23
from st2common.exceptions.auth import NoNicknameOriginProvidedError, AmbiguousUserError
24
from st2common.exceptions.auth import NotServiceUserError
25
from st2common.persistence.auth import User
26
from st2common.util.pecan_util import abort_request
27
from st2common.services.access import create_token
28
from st2common.models.api.auth import TokenAPI
29
from st2auth.backends import get_backend_instance
30
31
LOG = logging.getLogger(__name__)
32
33
34
class AuthHandlerBase(object):
35
    def handle_auth(self, request, headers=None, remote_addr=None,
36
                    remote_user=None, authorization=None, **kwargs):
37
        raise NotImplementedError()
38
39
    def _create_token_for_user(self, username, ttl=None):
40
        tokendb = create_token(username=username, ttl=ttl)
41
        return TokenAPI.from_model(tokendb)
42
43
44
class ProxyAuthHandler(AuthHandlerBase):
45
    def handle_auth(self, request, headers=None, remote_addr=None,
46
                    remote_user=None, authorization=None, **kwargs):
47
        remote_addr = headers.get('x-forwarded-for',
48
                                  remote_addr)
49
        extra = {'remote_addr': remote_addr}
50
51
        if remote_user:
52
            ttl = getattr(request, 'ttl', None)
53
            try:
54
                token = self._create_token_for_user(username=remote_user,
55
                                                    ttl=ttl)
56
            except TTLTooLargeException as e:
57
                abort_request(status_code=http_client.BAD_REQUEST,
58
                              message=e.message)
59
            return token
60
61
        LOG.audit('Access denied to anonymous user.', extra=extra)
62
        abort_request()
63
64
65
class StandaloneAuthHandler(AuthHandlerBase):
66
    def __init__(self, *args, **kwargs):
67
        self._auth_backend = get_backend_instance(name=cfg.CONF.auth.backend)
68
        super(StandaloneAuthHandler, self).__init__(*args, **kwargs)
69
70
    def handle_auth(self, request, headers=None, remote_addr=None, remote_user=None,
71
                    authorization=None, **kwargs):
72
        auth_backend = self._auth_backend.__class__.__name__
73
74
        extra = {'auth_backend': auth_backend, 'remote_addr': remote_addr}
75
76
        if not authorization:
77
            LOG.audit('Authorization header not provided', extra=extra)
78
            abort_request()
79
            return
80
81
        auth_type, auth_value = authorization
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are trying to unpack a non-sequence, which was defined at line 71.
Loading history...
82
        if auth_type.lower() not in ['basic']:
83
            extra['auth_type'] = auth_type
84
            LOG.audit('Unsupported authorization type: %s' % (auth_type), extra=extra)
85
            abort_request()
86
            return
87
88
        try:
89
            auth_value = base64.b64decode(auth_value)
90
        except Exception:
91
            LOG.audit('Invalid authorization header', extra=extra)
92
            abort_request()
93
            return
94
95
        split = auth_value.split(':')
96
        if len(split) != 2:
97
            LOG.audit('Invalid authorization header', extra=extra)
98
            abort_request()
99
            return
100
101
        username, password = split
102
        result = self._auth_backend
103
104
        result = self._auth_backend.authenticate(username=username, password=password)
105
        if result is True:
106
            LOG.audit(request.body)
107
            ttl = getattr(request, 'ttl', None)
108
            impersonate_user = getattr(request.body, 'user', None)
109
110
            if impersonate_user is not None:
111
                # check this is a service account
112
                if not User.get_by_name(username).is_service:
113
                    message = "Current user is not a service and cannot " \
114
                              "request impersonated tokens"
115
                    abort_request(status_code=http_client.BAD_REQUEST,
116
                                  message=message)
117
                    return
118
                username = impersonate_user
119
            else:
120
                impersonate_user = getattr(request, 'impersonate_user', None)
121
                nickname_origin = getattr(request, 'nickname_origin', None)
122
                if impersonate_user is not None:
123
                    try:
124
                        # check this is a service account
125
                        if not User.get_by_name(username).is_service:
126
                            raise NotServiceUserError()
127
                        username = User.get_by_nickname(impersonate_user,
128
                                                        nickname_origin).name
129
                    except NotServiceUserError:
130
                        message = "Current user is not a service and cannot " \
131
                                  "request impersonated tokens"
132
                        abort_request(status_code=http_client.BAD_REQUEST,
133
                                      message=message)
134
                        return
135
                    except UserNotFoundError:
136
                        message = "Could not locate user %s@%s" % \
137
                                  (impersonate_user, nickname_origin)
138
                        abort_request(status_code=http_client.BAD_REQUEST,
139
                                      message=message)
140
                        return
141
                    except NoNicknameOriginProvidedError:
142
                        message = "Nickname origin is not provided for nickname '%s'" % \
143
                                  impersonate_user
144
                        abort_request(status_code=http_client.BAD_REQUEST,
145
                                      message=message)
146
                        return
147
                    except AmbiguousUserError:
148
                        message = "%s@%s matched more than one username" % \
149
                                  (impersonate_user, nickname_origin)
150
                        abort_request(status_code=http_client.BAD_REQUEST,
151
                                      message=message)
152
                        return
153
            try:
154
                token = self._create_token_for_user(
155
                    username=username, ttl=ttl)
156
                return token
157
            except TTLTooLargeException as e:
158
                abort_request(status_code=http_client.BAD_REQUEST,
159
                              message=e.message)
160
                return
161
162
        LOG.audit('Invalid credentials provided', extra=extra)
163
        abort_request()
164