summaryrefslogtreecommitdiff
path: root/nova/consoleauth/manager.py
blob: 2ed2e2787eb66749f8e4da62f122ba95c77b068c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/usr/bin/env python
# Copyright (c) 2012 OpenStack Foundation
# All Rights Reserved.
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

"""Auth Components for Consoles."""

import time

from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_serialization import jsonutils

from nova import cache_utils
from nova.cells import rpcapi as cells_rpcapi
from nova.compute import rpcapi as compute_rpcapi
import nova.conf
from nova import context as nova_context
from nova.i18n import _LI
from nova import manager
from nova import objects


LOG = logging.getLogger(__name__)

CONF = nova.conf.CONF


class ConsoleAuthManager(manager.Manager):
    """Manages token based authentication."""

    target = messaging.Target(version='2.1')

    def __init__(self, scheduler_driver=None, *args, **kwargs):
        super(ConsoleAuthManager, self).__init__(service_name='consoleauth',
                                                 *args, **kwargs)
        self._mc = None
        self._mc_instance = None
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()
        self.cells_rpcapi = cells_rpcapi.CellsAPI()

    @property
    def mc(self):
        if self._mc is None:
            self._mc = cache_utils.get_client(CONF.consoleauth.token_ttl)
        return self._mc

    @property
    def mc_instance(self):
        if self._mc_instance is None:
            self._mc_instance = cache_utils.get_client()
        return self._mc_instance

    def reset(self):
        LOG.info(_LI('Reloading compute RPC API'))
        compute_rpcapi.LAST_VERSION = None
        self.compute_rpcapi = compute_rpcapi.ComputeAPI()

    def _get_tokens_for_instance(self, instance_uuid):
        tokens_str = self.mc_instance.get(instance_uuid.encode('UTF-8'))
        if not tokens_str:
            tokens = []
        else:
            tokens = jsonutils.loads(tokens_str)
        return tokens

    def authorize_console(self, context, token, console_type, host, port,
                          internal_access_path, instance_uuid,
                          access_url=None):

        token_dict = {'token': token,
                      'instance_uuid': instance_uuid,
                      'console_type': console_type,
                      'host': host,
                      'port': port,
                      'internal_access_path': internal_access_path,
                      'access_url': access_url,
                      'last_activity_at': time.time()}
        data = jsonutils.dumps(token_dict)

        self.mc.set(token.encode('UTF-8'), data)
        tokens = self._get_tokens_for_instance(instance_uuid)

        # Remove the expired tokens from cache.
        token_values = self.mc.get_multi(
            [tok.encode('UTF-8') for tok in tokens])
        tokens = [name for name, value in zip(tokens, token_values)
                  if value is not None]
        tokens.append(token)

        self.mc_instance.set(instance_uuid.encode('UTF-8'),
                             jsonutils.dumps(tokens))

        LOG.info(_LI("Received Token: %(token)s, %(token_dict)s"),
                  {'token': token, 'token_dict': token_dict})

    def _validate_token(self, context, token):
        instance_uuid = token['instance_uuid']
        if instance_uuid is None:
            return False

        # NOTE(comstud): consoleauth was meant to run in API cells.  So,
        # if cells is enabled, we must call down to the child cell for
        # the instance.
        if CONF.cells.enable:
            return self.cells_rpcapi.validate_console_port(context,
                    instance_uuid, token['port'], token['console_type'])

        mapping = objects.InstanceMapping.get_by_instance_uuid(context,
                                                               instance_uuid)
        with nova_context.target_cell(context, mapping.cell_mapping) as cctxt:
            instance = objects.Instance.get_by_uuid(cctxt, instance_uuid)

            return self.compute_rpcapi.validate_console_port(
                cctxt,
                instance,
                token['port'],
                token['console_type'])

    def check_token(self, context, token):
        token_str = self.mc.get(token.encode('UTF-8'))
        token_valid = (token_str is not None)
        LOG.info(_LI("Checking Token: %(token)s, %(token_valid)s"),
                  {'token': token, 'token_valid': token_valid})
        if token_valid:
            token = jsonutils.loads(token_str)
            if self._validate_token(context, token):
                return token

    def delete_tokens_for_instance(self, context, instance_uuid):
        tokens = self._get_tokens_for_instance(instance_uuid)
        self.mc.delete_multi(
                [tok.encode('UTF-8') for tok in tokens])
        self.mc_instance.delete(instance_uuid.encode('UTF-8'))