aboutsummaryrefslogtreecommitdiffstats
path: root/moonv4/moon_interface/moon_interface/api/authz.py
blob: 3847cc731adbe6322ac3981810f17bc9b76908a3 (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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# Copyright 2015 Open Platform for NFV Project, Inc. and its contributors
# This software is distributed under the terms and conditions of the 'Apache-2.0'
# license which can be found in the file 'LICENSE' in this package distribution
# or at 'http://www.apache.org/licenses/LICENSE-2.0'.
"""
Authz is the endpoint to get authorization response
"""

from flask import request
from flask_restful import Resource
import logging
import pickle
import requests
import time
from uuid import uuid4

from moon_interface.containers import DockerManager
from moon_interface.authz_requests import AuthzRequest
from moon_utilities import configuration

__version__ = "0.1.0"

LOG = logging.getLogger("moon.interface.api." + __name__)


def pdp_in_cache(cache, uuid):
    """Check if a PDP exist with this Keystone Project ID in the cache of this component

    :param cache: Cache to use
    :param uuid: Keystone Project ID
    :return: True or False
    """
    for item_uuid, item_value in cache.pdp.items():
        if uuid == item_value['keystone_project_id']:
            return item_uuid, item_value
    return None, None


def pdp_in_manager(cache, uuid):
    """Check if a PDP exist with this Keystone Project ID in the Manager component

    :param cache: Cache to use
    :param uuid: Keystone Project ID
    :return: True or False
    """
    cache.update()
    return pdp_in_cache(cache, uuid)


def container_exist(cache, uuid):
    """Check if a PDP exist with this Keystone Project ID in the Manager component

    :param cache: Cache to use
    :param uuid: Keystone Project ID
    :return: True or False
    """
    for key, value in cache.containers.items():
        if "keystone_project_id" not in value:
            continue
        if value["keystone_project_id"] == uuid:
            try:
                req = requests.head("http://{}:{}/".format(
                    value.get("hostname"),
                    value.get("port")[0].get("PublicPort")))
                LOG.info("container_exist {}".format(req.status_code))
                if req.status_code in (200, 201):
                    return value
                return
            except requests.exceptions.ConnectionError:
                pass
            # maybe hostname is not working so trying with IP address
            try:
                req = requests.head("http://{}:{}/".format(
                    value.get("ip"),
                    value.get("port")[0].get("PublicPort")))
                if req.status_code in (200, 201):
                    return value
                return
            except requests.exceptions.ConnectionError:
                return


def build_container(cache, manager_url, uuid, meta_rule_id, plugin_name="authz"):
    """Create the container and update the cache with the given perimeter elements

    :param cache: Cache to use
    :param manager_url: URL of the manager
    :param uuid: Keystone Project ID
    :param meta_rule_id: UUID of the meta_rule
    :param plugin_name: name of the plugin to use
    :return: True or False
    """
    LOG.info("Building a new container for {}".format(plugin_name))
    manager = DockerManager()
    tcp_port = configuration.increment_port()
    container_name = configuration.get_plugins()[plugin_name]['container']
    name = "{}_{}".format(plugin_name, uuid4().hex)
    policy_id = cache.get_policy_from_meta_rules(meta_rule_id)
    container_data = {
        "name": name,
        "hostname": name,
        "port": {
            "PrivatePort": tcp_port,
            "Type": "tcp",
            "IP": "0.0.0.0",
            "PublicPort": tcp_port
        },
        "keystone_project_id": uuid,
        "pdp_id": cache.get_pdp_from_keystone_project(uuid),
        "meta_rule_id": meta_rule_id,
        "policy_id": policy_id,
        "container_name": container_name,
        "plugin_name": plugin_name
    }
    container = manager.create_container(container_data)
    container_data['container_id'] = container.id
    container_data['port']["IP"] = container.ip
    container_data['start_time'] = time.time()
    req = requests.post("{}/containers".format(manager_url),
                        json=container_data)
    if req.status_code == 200:
        cache.add_container(container_data)
        return True


def create_containers(cache, manager_url, uuid, plugin_name="authz"):
    """Create the container and update the cache with the given perimeter elements

    :param cache: Cache to use
    :param manager_url: URL of the manager
    :param uuid: Keystone Project ID
    :param plugin_name: name of the plugin to use
    :return: True or False
    """
    LOG.info("Need to create some containers for {}".format(uuid))
    for pdp_id, pdp_value in cache.pdp.items():
        LOG.info("pdp {}".format(pdp_value))
        if uuid == pdp_value.get("keystone_project_id", ""):
            LOG.info("uuid {}".format(uuid))
            for policy_id in pdp_value.get("security_pipeline", []):
                LOG.info("policy {}".format(policy_id))
                model_id = cache.policies[policy_id]["model_id"]
                model_value = cache.models[model_id]
                for meta_rule_id in model_value["meta_rules"]:
                    LOG.info("meta_rule {}".format(meta_rule_id))
                    build_container(
                        cache=cache,
                        uuid=uuid,
                        manager_url=manager_url,
                        meta_rule_id=meta_rule_id,
                        plugin_name=plugin_name)
            return


def create_authz_request(cache, interface_name, manager_url, uuid, subject_name, object_name, action_name):
    """Create the authorization request and make the first call to the Authz function

    :param cache: Cache to use
    :param interface_name: hostname of the interface
    :param manager_url: URL of the manager
    :param uuid: Keystone Project ID
    :param subject_name: name of the subject
    :param object_name: name of the object
    :param action_name: name of the action
    :return: Authorisation request
    """
    req_id = uuid4().hex
    ctx = {
        "project_id": uuid,
        "subject_name": subject_name,
        "object_name": object_name,
        "action_name": action_name,
        "request_id": req_id,
        "interface_name": interface_name,
        "manager_url": manager_url,
        "cookie": uuid4().hex
    }
    cache.authz_requests[req_id] = AuthzRequest(ctx)
    return cache.authz_requests[req_id]


class Authz(Resource):
    """
    Endpoint for authz requests
    """

    __urls__ = (
        "/authz/<string:uuid>",
        "/authz/<string:uuid>/<string:subject_name>/<string:object_name>/<string:action_name>",
    )

    def __init__(self, **kwargs):
        self.CACHE = kwargs.get("cache")
        self.INTERFACE_NAME = kwargs.get("interface_name", "interface")
        self.MANAGER_URL = kwargs.get("manager_url", "http://manager:8080")
        self.TIMEOUT = 5

    def get(self, uuid=None, subject_name=None, object_name=None, action_name=None):
        """Get a response on an authorization request

        :param uuid: uuid of a tenant or an intra_extension
        :param subject_name: name of the subject or the request
        :param object_name: name of the object
        :param action_name: name of the action
        :return: {
            "args": {},
            "ctx": {
                "action_name": "4567",
                "id": "123456",
                "method": "authz",
                "object_name": "234567",
                "subject_name": "123456",
                "user_id": "admin"
            },
            "error": {
                "code": 500,
                "description": "",
                "title": "Moon Error"
            },
            "intra_extension_id": "123456",
            "result": false
        }
        :internal_api: authz
        """
        pdp_id, pdp_value = pdp_in_cache(self.CACHE, uuid)
        if not pdp_id:
            pdp_id, pdp_value = pdp_in_manager(self.CACHE, uuid)
            if not pdp_id:
                return {
                           "result": False,
                           "message": "Unknown Project ID or "
                                      "Project ID is not bind to a PDP."}, 403
        if not container_exist(self.CACHE, uuid):
            create_containers(
                cache=self.CACHE,
                uuid=uuid,
                manager_url=self.MANAGER_URL,
                plugin_name="authz")
        authz_request = create_authz_request(
            cache=self.CACHE,
            uuid=uuid,
            interface_name=self.INTERFACE_NAME,
            manager_url=self.MANAGER_URL,
            subject_name=subject_name,
            object_name=object_name,
            action_name=action_name)
        cpt = 0
        while True:
            if cpt > self.TIMEOUT*10:
                return {"result": False,
                        "message": "Authz request had timed out."}, 500
            if authz_request.is_authz():
                if authz_request.final_result == "Grant":
                    return {"result": True, "message": ""}, 200
                return {"result": False, "message": ""}, 401
            cpt += 1
            time.sleep(0.1)

    def patch(self, uuid=None, subject_name=None, object_name=None, action_name=None):
        """Get a response on an authorization request

        :param uuid: uuid of the authorization request
        :param subject_name: not used
        :param object_name: not used
        :param action_name: not used
        :request body: a Context object
        :return: {}
        :internal_api: authz
        """
        if uuid in self.CACHE.authz_requests:
            self.CACHE.authz_requests[uuid].set_result(pickle.loads(request.data))
            return "", 201
        return {"result": False, "message": "The request ID is unknown"}, 500