Completed
Pull Request — master (#2920)
by Anthony
04:30
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 base64
17
from six.moves import http_client
18
from oslo_config import cfg
19
20
from st2common import log as logging
21
from st2common.exceptions.auth import TTLTooLargeException, UserNotFoundError
22
from st2common.exceptions.db import StackStormDBObjectNotFoundError
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
            ttl = getattr(request, 'ttl', None)
107
            impersonate_user = getattr(request, 'user', None)
108
109
            if impersonate_user is not None:
110
                # check this is a service account
111
                try:
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
                except (UserNotFoundError, StackStormDBObjectNotFoundError):
120
                    message = "Could not locate user %s" % \
121
                              (impersonate_user)
122
                    abort_request(status_code=http_client.BAD_REQUEST,
123
                                  message=message)
124
                    return
125
            else:
126
                impersonate_user = getattr(request, 'impersonate_user', None)
127
                nickname_origin = getattr(request, 'nickname_origin', None)
128
                if impersonate_user is not None:
129
                    try:
130
                        # check this is a service account
131
                        if not User.get_by_name(username).is_service:
132
                            raise NotServiceUserError()
133
                        username = User.get_by_nickname(impersonate_user,
134
                                                        nickname_origin).name
135
                    except NotServiceUserError:
136
                        message = "Current user is not a service and cannot " \
137
                                  "request impersonated tokens"
138
                        abort_request(status_code=http_client.BAD_REQUEST,
139
                                      message=message)
140
                        return
141
                    except (UserNotFoundError, StackStormDBObjectNotFoundError):
142
                        message = "Could not locate user %s@%s" % \
143
                                  (impersonate_user, nickname_origin)
144
                        abort_request(status_code=http_client.BAD_REQUEST,
145
                                      message=message)
146
                        return
147
                    except NoNicknameOriginProvidedError:
148
                        message = "Nickname origin is not provided for nickname '%s'" % \
149
                                  impersonate_user
150
                        abort_request(status_code=http_client.BAD_REQUEST,
151
                                      message=message)
152
                        return
153
                    except AmbiguousUserError:
154
                        message = "%s@%s matched more than one username" % \
155
                                  (impersonate_user, nickname_origin)
156
                        abort_request(status_code=http_client.BAD_REQUEST,
157
                                      message=message)
158
                        return
159
            try:
160
                token = self._create_token_for_user(
161
                    username=username, ttl=ttl)
162
                return token
163
            except TTLTooLargeException as e:
164
                abort_request(status_code=http_client.BAD_REQUEST,
165
                              message=e.message)
166
                return
167
168
        LOG.audit('Invalid credentials provided', extra=extra)
169
        abort_request()
170