fix bug for ETSI Catalog Manager needs to raise 303 exception for the same callback_u...
[modeling/etsicatalog.git] / catalog / packages / biz / nsdm_subscription.py
1 # Copyright (C) 2019 Verizon. All Rights Reserved
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import ast
16 import json
17 import logging
18 import uuid
19 from collections import Counter
20
21 import requests
22 from rest_framework import status
23
24 from catalog.packages import const
25 from catalog.pub.database.models import NsdmSubscriptionModel
26 from catalog.pub.exceptions import CatalogException, \
27     NsdmBadRequestException, NsdmDuplicateSubscriptionException, SubscriptionDoesNotExistsException
28 from catalog.pub.utils.values import ignore_case_get
29 from catalog.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT
30
31 logger = logging.getLogger(__name__)
32
33 PARAMSBASICKEYS = ["userName", "password"]
34
35 PARAMSOAUTH2CLIENTCREDENTIALSKEYS = ["clientId", "clientPassword",
36                                      "tokenEndpoint"]
37
38
39 def is_filter_type_equal(new_filter, existing_filter):
40     return Counter(list(set(new_filter))) == Counter(existing_filter)
41
42
43 class NsdmSubscription:
44
45     def __init__(self):
46         pass
47
48     def query_single_subscription(self, subscription_id):
49         logger.debug("Start Query Subscription... ")
50         subscription = \
51             NsdmSubscriptionModel.objects.filter(
52                 subscriptionid=subscription_id)
53         if not subscription.exists():
54             raise SubscriptionDoesNotExistsException(
55                 "Subscription(%s) doesn't exist" % subscription_id)
56         logger.debug("Subscription found... ")
57         return self.fill_resp_data(subscription[0])
58
59     def delete_single_subscription(self, subscription_id):
60         logger.debug("Start Delete Subscription... ")
61         subscription = \
62             NsdmSubscriptionModel.objects.filter(
63                 subscriptionid=subscription_id)
64         if not subscription.exists():
65             raise SubscriptionDoesNotExistsException(
66                 "Subscription(%s) doesn't exist" % subscription_id)
67         subscription.delete()
68         logger.debug("Deleted Subscription... ")
69
70     def query_multi_subscriptions(self, query_params):
71         self.params = query_params
72         query_data = {}
73         logger.debug("Start QueryMultiSubscriptions get --> "
74                      "Check for filters in query params" % self.params)
75         for query, value in list(self.params.items()):
76             if query in const.NSDM_NOTIFICATION_FILTERS and value:
77                 query_data[query + '__icontains'] = json.dumps(list(set(value)))
78         # Query the database with filter if the request
79         # has fields in request params, else fetch all records
80         if query_data:
81             subscriptions = NsdmSubscriptionModel.objects.filter(**query_data)
82         else:
83             subscriptions = NsdmSubscriptionModel.objects.all()
84         if not subscriptions.exists():
85             raise SubscriptionDoesNotExistsException("Subscriptions doesn't exist")
86         return [self.fill_resp_data(subscription)
87                 for subscription in subscriptions]
88
89     def check_callbackuri_connection(self):
90         logger.debug("Create Subscription --> Test Callback URI --"
91                      "Sending GET request to %s" % self.callback_uri)
92         try:
93             response = requests.get(self.callback_uri, timeout=2, verify=False)
94             if response.status_code != status.HTTP_204_NO_CONTENT:
95                 raise CatalogException("callbackUri %s returns %s status "
96                                        "code." % (self.callback_uri,
97                                                   response.status_code))
98         except Exception:
99             raise CatalogException("callbackUri %s didn't return 204 status"
100                                    "code." % self.callback_uri)
101
102     def fill_resp_data(self, subscription):
103         subscription_filter = dict()
104         for filter_type in const.NSDM_NOTIFICATION_FILTERS:
105             if subscription.__dict__[filter_type]:
106                 subscription_filter[filter_type] = \
107                     ast.literal_eval(subscription.__dict__[filter_type])
108         resp_data = {
109             'id': subscription.subscriptionid,
110             'callbackUri': subscription.callback_uri,
111             'filter': subscription_filter,
112             '_links': json.loads(subscription.links)
113         }
114         return resp_data
115
116     def create(self, data):
117         logger.debug("Start Create Subscription... ")
118         self.filter = ignore_case_get(data, "filter", {})
119         self.callback_uri = ignore_case_get(data, "callbackUri")
120         self.authentication = ignore_case_get(data, "authentication", {})
121         self.subscription_id = str(uuid.uuid4())
122         self.check_callbackuri_connection()
123         self.check_valid_auth_info()
124         self.check_filter_types()
125         self.check_valid()
126         self.save_db()
127         subscription = \
128             NsdmSubscriptionModel.objects.get(
129                 subscriptionid=self.subscription_id)
130         return self.fill_resp_data(subscription)
131
132     def check_filter_types(self):
133         # Check if both nsdId and nsdInfoId
134         # or pnfdId and pnfdInfoId are present
135         logger.debug("Create Subscription --> Validating Filters... ")
136         if self.filter and \
137                 self.filter.get("nsdId", "") and \
138                 self.filter.get("nsdInfoId", ""):
139             raise NsdmBadRequestException("Notification Filter should contain"
140                                           " either nsdId or nsdInfoId")
141         if self.filter and \
142                 self.filter.get("pnfdId", "") and \
143                 self.filter.get("pnfdInfoIds", ""):
144             raise NsdmBadRequestException("Notification Filter should contain"
145                                           " either pnfdId or pnfdInfoIds")
146
147     def check_valid_auth_info(self):
148         logger.debug("Create Subscription --> Validating Auth "
149                      "details if provided... ")
150         if self.authentication.get("paramsBasic", {}) and \
151                 const.BASIC not in self.authentication.get("authType", ''):
152             raise NsdmBadRequestException('Auth type should be ' + const.BASIC)
153         if self.authentication.get("paramsOauth2ClientCredentials", {}) and \
154                 const.OAUTH2_CLIENT_CREDENTIALS not in \
155                 self.authentication.get("authType", ''):
156             raise NsdmBadRequestException('Auth type should '
157                                           'be ' + const.OAUTH2_CLIENT_CREDENTIALS)
158         if const.BASIC in self.authentication.get("authType", '') and \
159                 "paramsBasic" in list(self.authentication.keys()) and \
160                 not is_filter_type_equal(PARAMSBASICKEYS, list(
161                     self.authentication.get("paramsBasic").keys())):
162             raise NsdmBadRequestException('userName and password needed '
163                                           'for ' + const.BASIC)
164         if const.OAUTH2_CLIENT_CREDENTIALS in \
165                 self.authentication.get("authType", '') and \
166                 "paramsOauth2ClientCredentials" in \
167                 list(self.authentication.keys()) and \
168                 not is_filter_type_equal(PARAMSOAUTH2CLIENTCREDENTIALSKEYS, list(
169                     self.authentication.get("paramsOauth2ClientCredentials").keys())):
170             raise NsdmBadRequestException('clientId, clientPassword and '
171                                           'tokenEndpoint required '
172                                           'for ' + const.OAUTH2_CLIENT_CREDENTIALS)
173
174     def check_filter_exists(self, subscription):
175         for filter_type in const.NSDM_NOTIFICATION_FILTERS:
176             if not is_filter_type_equal(self.filter.get(filter_type, []),
177                                         ast.literal_eval(
178                                             getattr(subscription,
179                                                     filter_type))):
180                 return False
181         return True
182
183     def check_valid(self):
184         logger.debug("Create Subscription --> Checking DB if "
185                      "same subscription has already existed... ")
186         subscriptions = \
187             NsdmSubscriptionModel.objects.filter(
188                 callback_uri=self.callback_uri)
189
190         for subscription in subscriptions:
191             if self.check_filter_exists(subscription):
192                 links = json.loads(subscription.links)
193                 logger.error(
194                     "Subscription has already existed with the "
195                     "same callbackUri and filter:%s" % links)
196                 raise NsdmDuplicateSubscriptionException(
197                     "https://%s:%s/%s" % (MSB_SERVICE_IP, MSB_SERVICE_PORT, links["self"]["href"]))
198
199     def save_db(self):
200         logger.debug("Create Subscription --> Saving the subscription "
201                      "%s to the database" % self.subscription_id)
202         links = {
203             "self": {
204                 "href":
205                     const.NSDM_SUBSCRIPTION_ROOT_URI + self.subscription_id
206             }
207         }
208         subscription_save_db = {
209             "subscriptionid": self.subscription_id,
210             "callback_uri": self.callback_uri,
211             "auth_info": json.dumps(self.authentication),
212             "links": json.dumps(links)
213         }
214         for filter_type in const.NSDM_NOTIFICATION_FILTERS:
215             if self.filter:
216                 subscription_save_db[filter_type] = json.dumps(
217                     list(set(self.filter.get(filter_type, []))))
218         NsdmSubscriptionModel.objects.create(**subscription_save_db)
219         logger.debug('Create Subscription[%s] success', self.subscription_id)