Passed
Push — develop ( e1e109...2dd18e )
by Plexxi
06:53 queued 03:26
created

validate_token_and_source()   B

Complexity

Conditions 5

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
dl 0
loc 24
rs 8.1671
c 1
b 0
f 0
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
import hashlib
18
import os
19
import random
20
21
from st2common import log as logging
22
from st2common.persistence.auth import Token, ApiKey
23
from st2common.exceptions import auth as exceptions
24
from st2common.util import date as date_utils
25
from st2common.util import hash as hash_utils
26
27
__all__ = [
28
    'validate_token',
29
    'validate_token_and_source',
30
    'generate_api_key',
31
    'validate_api_key',
32
    'validate_api_key_and_source'
33
]
34
35
LOG = logging.getLogger(__name__)
36
37
38
def validate_token(token_string):
39
    """
40
    Validate the provided authentication token.
41
42
    :param token_string: Authentication token provided.
43
    :type token_string: ``str``
44
45
    :return: TokenDB object on success.
46
    :rtype: :class:`.TokenDB`
47
    """
48
    token = Token.get(token_string)
49
50
    if token.expiry <= date_utils.get_datetime_utc_now():
51
        # TODO: purge expired tokens
52
        LOG.audit('Token with id "%s" has expired.' % (token.id))
53
        raise exceptions.TokenExpiredError('Token has expired.')
54
55
    LOG.audit('Token with id "%s" is validated.' % (token.id))
56
57
    return token
58
59
60
def validate_token_and_source(token_in_headers, token_in_query_params):
61
    """
62
    Validate the provided authentication token.
63
64
    :param token_in_headers: Authentication token provided via headers.
65
    :type token_in_headers: ``str``
66
67
    :param token_in_query_params: Authentication token provided via query params.
68
    :type token_in_query_params: ``str``
69
70
    :return: TokenDB object on success.
71
    :rtype: :class:`.TokenDB`
72
    """
73
    if not token_in_headers and not token_in_query_params:
74
        LOG.audit('Token is not found in header or query parameters.')
75
        raise exceptions.TokenNotProvidedError('Token is not provided.')
76
77
    if token_in_headers:
78
        LOG.audit('Token provided in headers')
79
80
    if token_in_query_params:
81
        LOG.audit('Token provided in query parameters')
82
83
    return validate_token(token_in_headers or token_in_query_params)
84
85
86
def generate_api_key():
87
    """
88
    Generates an sufficiently large and random key.
89
90
    credit: http://jetfar.com/simple-api-key-generation-in-python/
91
    """
92
    # 256bit seed from urandom
93
    seed = os.urandom(256)
94
    # since urandom does not provide sufficient entropy hash, base64encode and salt.
95
    # The resulting value is now large and should be hard to predict.
96
    hashed_seed = hashlib.sha256(seed).hexdigest()
97
    return base64.b64encode(
98
        hashed_seed,
99
        random.choice(['rA', 'aZ', 'gQ', 'hH', 'hG', 'aR', 'DD'])).rstrip('==')
100
101
102
def generate_api_key_and_hash():
103
    api_key = generate_api_key()
104
    api_key_hash = hash_utils.hash(api_key)
105
    return api_key, api_key_hash
106
107
108
def validate_api_key(api_key):
109
    """
110
    Validate the provided API key.
111
112
    :param api_key: API key provided.
113
    :type api_key: ``str``
114
115
    :return: TokenDB object on success.
116
    :rtype: :class:`.ApiKeyDB`
117
    """
118
    api_key_db = ApiKey.get(api_key)
119
120
    if not api_key_db.enabled:
121
        raise exceptions.ApiKeyDisabledError('API key is disabled.')
122
123
    LOG.audit('API key with id "%s" is validated.' % (api_key_db.id))
124
125
    return api_key_db
126
127
128
def validate_api_key_and_source(api_key_in_headers, api_key_query_params):
129
    """
130
    Validate the provided API key.
131
132
    :param api_key_in_headers: API key provided via headers.
133
    :type api_key_in_headers: ``str``
134
135
    :param api_key_query_params: API key provided via query params.
136
    :type api_key_query_params: ``str``
137
138
    :return: TokenDB object on success.
139
    :rtype: :class:`.ApiKeyDB`
140
    """
141
    if not api_key_in_headers and not api_key_query_params:
142
        LOG.audit('API key is not found in header or query parameters.')
143
        raise exceptions.ApiKeyNotProvidedError('API key is not provided.')
144
145
    if api_key_in_headers:
146
        LOG.audit('API key provided in headers')
147
148
    if api_key_query_params:
149
        LOG.audit('API key provided in query parameters')
150
151
    return validate_api_key(api_key_in_headers or api_key_query_params)
152