Completed
Pull Request — master (#2920)
by Anthony
04:31
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.db import StackStormDBObjectNotFoundError
24
from st2common.exceptions.auth import NoNicknameOriginProvidedError, AmbiguousUserError
25
from st2common.exceptions.auth import NotServiceUserError
26
from st2common.persistence.auth import User
27
from st2common.util.pecan_util import abort_request
28
from st2common.services.access import create_token
29
from st2common.models.api.auth import TokenAPI
30
from st2auth.backends import get_backend_instance
31
32
LOG = logging.getLogger(__name__)
33
34
35
class AuthHandlerBase(object):
36
    def handle_auth(self, request, headers=None, remote_addr=None,
37
                    remote_user=None, authorization=None, **kwargs):
38
        raise NotImplementedError()
39
40
    def _create_token_for_user(self, username, ttl=None):
41
        tokendb = create_token(username=username, ttl=ttl)
42
        return TokenAPI.from_model(tokendb)
43
44
45
class ProxyAuthHandler(AuthHandlerBase):
46
    def handle_auth(self, request, headers=None, remote_addr=None,
47
                    remote_user=None, authorization=None, **kwargs):
48
        remote_addr = headers.get('x-forwarded-for',
49
                                  remote_addr)
50
        extra = {'remote_addr': remote_addr}
51
52
        if remote_user:
53
            ttl = getattr(request, 'ttl', None)
54
            try:
55
                token = self._create_token_for_user(username=remote_user,
56
                                                    ttl=ttl)
57
            except TTLTooLargeException as e:
58
                abort_request(status_code=http_client.BAD_REQUEST,
59
                              message=e.message)
60
            return token
61
62
        LOG.audit('Access denied to anonymous user.', extra=extra)
63
        abort_request()
64
65
66
class StandaloneAuthHandler(AuthHandlerBase):
67
    def __init__(self, *args, **kwargs):
68
        self._auth_backend = get_backend_instance(name=cfg.CONF.auth.backend)
69
        super(StandaloneAuthHandler, self).__init__(*args, **kwargs)
70
71
    def handle_auth(self, request, headers=None, remote_addr=None, remote_user=None,
72
                    authorization=None, **kwargs):
73
        auth_backend = self._auth_backend.__class__.__name__
74
75
        extra = {'auth_backend': auth_backend, 'remote_addr': remote_addr}
76
77
        if not authorization:
78
            LOG.audit('Authorization header not provided', extra=extra)
79
            abort_request()
80
            return
81
82
        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 72.
Loading history...
83
        if auth_type.lower() not in ['basic']:
84
            extra['auth_type'] = auth_type
85
            LOG.audit('Unsupported authorization type: %s' % (auth_type), extra=extra)
86
            abort_request()
87
            return
88
89
        try:
90
            auth_value = base64.b64decode(auth_value)
91
        except Exception:
92
            LOG.audit('Invalid authorization header', extra=extra)
93
            abort_request()
94
            return
95
96
        split = auth_value.split(':')
97
        if len(split) != 2:
98
            LOG.audit('Invalid authorization header', extra=extra)
99
            abort_request()
100
            return
101
102
        username, password = split
103
        result = self._auth_backend
104
105
        result = self._auth_backend.authenticate(username=username, password=password)
106
        if result is True:
107
            ttl = getattr(request, 'ttl', None)
108
            impersonate_user = getattr(request, 'user', None)
109
110
            if impersonate_user is not None:
111
                # check this is a service account
112
                try:
113
                    if not User.get_by_name(username).is_service:
114
                        message = "Current user is not a service and cannot " \
115
                                  "request impersonated tokens"
116
                        abort_request(status_code=http_client.BAD_REQUEST,
117
                                      message=message)
118
                        return
119
                    username = impersonate_user
120
                except (UserNotFoundError, StackStormDBObjectNotFoundError):
121
                    message = "Could not locate user %s" % \
122
                              (impersonate_user)
123
                    abort_request(status_code=http_client.BAD_REQUEST,
124
                                  message=message)
125
                    return
126
            else:
127
                impersonate_user = getattr(request, 'impersonate_user', None)
128
                nickname_origin = getattr(request, 'nickname_origin', None)
129
                if impersonate_user is not None:
130
                    try:
131
                        # check this is a service account
132
                        if not User.get_by_name(username).is_service:
133
                            raise NotServiceUserError()
134
                        username = User.get_by_nickname(impersonate_user,
135
                                                        nickname_origin).name
136
                    except NotServiceUserError:
137
                        message = "Current user is not a service and cannot " \
138
                                  "request impersonated tokens"
139
                        abort_request(status_code=http_client.BAD_REQUEST,
140
                                      message=message)
141
                        return
142
                    except (UserNotFoundError, StackStormDBObjectNotFoundError):
143
                        message = "Could not locate user %s@%s" % \
144
                                  (impersonate_user, nickname_origin)
145
                        abort_request(status_code=http_client.BAD_REQUEST,
146
                                      message=message)
147
                        return
148
                    except NoNicknameOriginProvidedError:
149
                        message = "Nickname origin is not provided for nickname '%s'" % \
150
                                  impersonate_user
151
                        abort_request(status_code=http_client.BAD_REQUEST,
152
                                      message=message)
153
                        return
154
                    except AmbiguousUserError:
155
                        message = "%s@%s matched more than one username" % \
156
                                  (impersonate_user, nickname_origin)
157
                        abort_request(status_code=http_client.BAD_REQUEST,
158
                                      message=message)
159
                        return
160
            try:
161
                token = self._create_token_for_user(
162
                    username=username, ttl=ttl)
163
                return token
164
            except TTLTooLargeException as e:
165
                abort_request(status_code=http_client.BAD_REQUEST,
166
                              message=e.message)
167
                return
168
169
        LOG.audit('Invalid credentials provided', extra=extra)
170
        abort_request()
171