1 # Copyright 2012 Spanish National Research Council
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
7 # http://www.apache.org/licenses/LICENSE-2.0
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
15 # Copyright 2012-2013 GRNET S.A. All rights reserved.
17 # Redistribution and use in source and binary forms, with or
18 # without modification, are permitted provided that the following
21 # 1. Redistributions of source code must retain the above
22 # copyright notice, this list of conditions and the following
25 # 2. Redistributions in binary form must reproduce the above
26 # copyright notice, this list of conditions and the following
27 # disclaimer in the documentation and/or other materials
28 # provided with the distribution.
30 # THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
31 # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
32 # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
33 # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
34 # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
35 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
36 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
37 # USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
38 # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
39 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
40 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41 # POSSIBILITY OF SUCH DAMAGE.
43 # The views and conclusions contained in the software and
44 # documentation are those of the authors and should not be
45 # interpreted as representing official policies, either expressed
46 # or implied, of GRNET S.A.
53 from logging import getLogger
55 from kamaki.clients import ClientError
57 LOG = getLogger(__name__)
59 # Environment variable used to pass the request context
60 CONTEXT_ENV = 'snf.context'
63 SSL_CLIENT_S_DN_ENV = "SSL_CLIENT_S_DN"
64 SSL_CLIENT_CERT_ENV = "SSL_CLIENT_CERT"
65 SSL_CLIENT_CERT_CHAIN_ENV_PREFIX = "SSL_CLIENT_CERT_CHAIN_"
67 """Global variables that contain VOMS related paths
69 VOMS_POLICY = "/etc/snf/voms.json"
70 VOMSDIR_PATH = "/etc/grid-security/vomsdir/"
71 CA_PATH = "/etc/grid-security/certificates/"
72 VOMSAPI_LIB = "/usr/lib/libvomsapi.so.1"
73 PARAMS_ENV = 'snf_voms.params'
76 """Filter that checks for the SSL data in the reqest.
78 Sets 'ssl' in the context as a dictionary containing this data.
81 def __init__(self, *args, **kwargs):
86 self.voms_json = json.loads(
87 open(VOMS_POLICY).read())
90 'Bad Formatted VOMS json',
91 details='The VOMS json data was not corectly formatted in file %s' % VOMS_POLICY)
94 'No loading of VOMS json file',
95 details='The VOMS json file located in %s was not loaded' % VOMS_POLICY)
97 self._no_verify = False
99 #super(VomsAuthN, self).__init__(*args, **kwargs)
102 def _get_cert_chain(ssl_info):
103 """Return certificate and chain from the ssl info in M2Crypto format"""
105 cert = M2Crypto.X509.load_cert_string(ssl_info.get("cert", ""))
106 chain = M2Crypto.X509.X509_Stack()
107 for c in ssl_info.get("chain", []):
108 aux = M2Crypto.X509.load_cert_string(c)
112 def _get_voms_info(self, ssl_info):
113 """Extract voms info from ssl_info and return dict with it."""
116 cert, chain = self._get_cert_chain(ssl_info)
117 except M2Crypto.X509.X509Error:
119 'SSL data not verified',
122 with voms_helper.VOMS(VOMSDIR_PATH,
123 CA_PATH, VOMSAPI_LIB) as v:
127 voms_data = v.retrieve(cert, chain)
130 raise VomsError(v.error.value)
133 for attr in ('user', 'userca', 'server', 'serverca',
134 'voname', 'uri', 'version', 'serial',
135 ('not_before', 'date1'), ('not_after', 'date2')):
136 if isinstance(attr, basestring):
137 d[attr] = getattr(voms_data, attr)
139 d[attr[0]] = getattr(voms_data, attr[1])
142 for fqan in iter(voms_data.fqan):
145 d["fqans"].append(fqan)
150 def _split_fqan(fqan):
152 gets a fqan and returns a tuple containing
153 (vo/groups, role, capability)
156 capability = l.pop().split("=")[-1]
157 role = l.pop().split("=")[-1]
158 vogroup = "/".join(l)
159 return (vogroup, role, capability)
161 def _process_environ(self, environ):
163 LOG.warning("Getting the environment parameters...")
164 # the environment variable CONTENT_LENGTH may be empty or missing
166 request_body_size = int(environ.get('CONTENT_LENGTH', 0))
168 request_body_size = 0
170 'Not auth method provided',
171 details='The request body is empty, while it should contain the authentication method')
173 request_body = environ['wsgi.input'].read(request_body_size)
177 request_body = request_body.replace("true","\"true\"")
178 request_body = request_body.replace('"','\'' )
180 params_parsed = ast.literal_eval(request_body)
184 for k, v in params_parsed.iteritems():
185 if k in ('self', 'context'):
187 if k.startswith('_'):
192 environ[PARAMS_ENV] = params
193 print environ[PARAMS_ENV]
195 def is_applicable(self, environ):
196 """Check if the request is applicable for this handler or not"""
197 print "Checking if the request is applicable for this handler or not..."
198 self._process_environ(environ)
199 params = environ.get(PARAMS_ENV, {})
200 auth = params.get("auth", {})
202 if "true" in auth["voms"]:
207 details='Error in JSON, voms must be set to true')
212 def authenticate(self,ssl_data):
215 voms_info = self._get_voms_info(ssl_data)
216 except VomsError as e:
218 user_dn = voms_info["user"]
219 user_vo = voms_info["voname"]
220 user_fqans = voms_info["fqans"]
222 return user_dn, user_vo, user_fqans
225 def process_request(self, environ):
227 print "Inside process_Request at last!!!!"
228 if not self.is_applicable(environ):
229 return self.application
232 "dn": environ.get(SSL_CLIENT_S_DN_ENV, None),
233 "cert": environ.get(SSL_CLIENT_CERT_ENV, None),
236 for k, v in environ.iteritems():
237 if k.startswith(SSL_CLIENT_CERT_CHAIN_ENV_PREFIX):
238 ssl_dict["chain"].append(v)
240 voms_info = self._get_voms_info(ssl_dict)
242 params = environ[PARAMS_ENV]
244 tenant_from_req = params["auth"].get("tenantName", None)
246 print voms_info, tenant_from_req
247 user_dn = voms_info["user"]
248 user_vo = voms_info["voname"]
249 user_fqans = voms_info["fqans"]
250 environ['REMOTE_USER'] = user_dn
252 return user_dn, user_vo, user_fqans