# Copyright (c) 2016 EMC Corporation # 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. try: import cookielib as cookie_lib except ImportError: import http.cookiejar as cookie_lib import socket import requests from requests import exceptions import six from cinder.i18n import _ from cinder.volume.drivers.coprhd.helpers import commoncoprhdapi as common class Authentication(common.CoprHDResource): # Commonly used URIs for the 'Authentication' module URI_SERVICES_BASE = '' URI_AUTHENTICATION = '/login' HEADERS = {'Content-Type': 'application/json', 'ACCEPT': 'application/json', 'X-EMC-REST-CLIENT': 'TRUE'} def authenticate_user(self, username, password): """Makes REST API call to generate the authentication token. Authentication token is generated for the specified user after validation :param username: Name of the user :param password: Password for the user :returns: The authtoken """ SEC_REDIRECT = 302 SEC_AUTHTOKEN_HEADER = 'X-SDS-AUTH-TOKEN' LB_API_PORT = 4443 # Port on which load-balancer/reverse-proxy listens to all incoming # requests for CoprHD REST APIs APISVC_PORT = 8443 # Port on which apisvc listens to incoming requests cookiejar = cookie_lib.LWPCookieJar() url = ('https://%(ip)s:%(port)d%(uri)s' % {'ip': self.ipaddr, 'port': self.port, 'uri': self.URI_AUTHENTICATION}) try: if self.port == APISVC_PORT: login_response = requests.get( url, headers=self.HEADERS, verify=False, auth=(username, password), cookies=cookiejar, allow_redirects=False, timeout=common.TIMEOUT_SEC) if login_response.status_code == SEC_REDIRECT: location = login_response.headers['Location'] if not location: raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_("The redirect" " location of the" " authentication" " service is not" " provided"))) # Make the second request login_response = requests.get( location, headers=self.HEADERS, verify=False, cookies=cookiejar, allow_redirects=False, timeout=common.TIMEOUT_SEC) if (login_response.status_code != requests.codes['unauthorized']): raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_("The" " authentication" " service failed" " to reply with" " 401"))) # Now provide the credentials login_response = requests.get( location, headers=self.HEADERS, auth=(username, password), verify=False, cookies=cookiejar, allow_redirects=False, timeout=common.TIMEOUT_SEC) if login_response.status_code != SEC_REDIRECT: raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_("Access forbidden: Authentication required"))) location = login_response.headers['Location'] if not location: raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_("The" " authentication service failed to provide the" " location of the service URI when redirecting" " back"))) authtoken = login_response.headers[SEC_AUTHTOKEN_HEADER] if not authtoken: details_str = self.extract_error_detail(login_response) raise common.CoprHdError(common.CoprHdError.HTTP_ERR, (_("The token is not" " generated by" " authentication service." "%s") % details_str)) # Make the final call to get the page with the token new_headers = self.HEADERS new_headers[SEC_AUTHTOKEN_HEADER] = authtoken login_response = requests.get( location, headers=new_headers, verify=False, cookies=cookiejar, allow_redirects=False, timeout=common.TIMEOUT_SEC) if login_response.status_code != requests.codes['ok']: raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_( "Login failure code: " "%(statuscode)s Error: %(responsetext)s") % {'statuscode': six.text_type( login_response.status_code), 'responsetext': login_response.text})) elif self.port == LB_API_PORT: login_response = requests.get( url, headers=self.HEADERS, verify=False, cookies=cookiejar, allow_redirects=False) if(login_response.status_code == requests.codes['unauthorized']): # Now provide the credentials login_response = requests.get( url, headers=self.HEADERS, auth=(username, password), verify=False, cookies=cookiejar, allow_redirects=False) authtoken = None if SEC_AUTHTOKEN_HEADER in login_response.headers: authtoken = login_response.headers[SEC_AUTHTOKEN_HEADER] else: raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_("Incorrect port number. Load balanced port is: " "%(lb_api_port)s, api service port is: " "%(apisvc_port)s") % {'lb_api_port': LB_API_PORT, 'apisvc_port': APISVC_PORT})) if not authtoken: details_str = self.extract_error_detail(login_response) raise common.CoprHdError( common.CoprHdError.HTTP_ERR, (_("The token is not generated by authentication service." " %s") % details_str)) if login_response.status_code != requests.codes['ok']: error_msg = None if login_response.status_code == 401: error_msg = _("Access forbidden: Authentication required") elif login_response.status_code == 403: error_msg = _("Access forbidden: You don't have" " sufficient privileges to perform" " this operation") elif login_response.status_code == 500: error_msg = _("Bourne internal server error") elif login_response.status_code == 404: error_msg = _( "Requested resource is currently unavailable") elif login_response.status_code == 405: error_msg = (_("GET method is not supported by resource:" " %s"), url) elif login_response.status_code == 503: error_msg = _("Service temporarily unavailable:" " The server is temporarily unable" " to service your request") else: error_msg = login_response.text raise common.CoprHdError(common.CoprHdError.HTTP_ERR, (_("HTTP code: %(status_code)s" ", response: %(reason)s" " [%(error_msg)s]") % { 'status_code': six.text_type( login_response.status_code), 'reason': six.text_type( login_response.reason), 'error_msg': six.text_type( error_msg) })) except (exceptions.SSLError, socket.error, exceptions.ConnectionError, exceptions.Timeout) as e: raise common.CoprHdError( common.CoprHdError.HTTP_ERR, six.text_type(e)) return authtoken def extract_error_detail(self, login_response): details_str = "" try: if login_response.content: json_object = common.json_decode(login_response.content) if 'details' in json_object: details_str = json_object['details'] return details_str except common.CoprHdError: return details_str