root / snfOCCI / snf_voms / __init__.py @ f25a4623
History | View | Annotate | Download (8.5 kB)
1 |
# Copyright 2012 Spanish National Research Council
|
---|---|
2 |
#
|
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
|
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, WITHOUT
|
11 |
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
12 |
# License for the specific language governing permissions and limitations
|
13 |
# under the License.
|
14 |
|
15 |
# Copyright 2012-2013 GRNET S.A. All rights reserved.
|
16 |
#
|
17 |
# Redistribution and use in source and binary forms, with or
|
18 |
# without modification, are permitted provided that the following
|
19 |
# conditions are met:
|
20 |
#
|
21 |
# 1. Redistributions of source code must retain the above
|
22 |
# copyright notice, this list of conditions and the following
|
23 |
# disclaimer.
|
24 |
#
|
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.
|
29 |
#
|
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.
|
42 |
#
|
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.
|
47 |
|
48 |
|
49 |
import json |
50 |
import M2Crypto |
51 |
import ast |
52 |
|
53 |
from logging import getLogger |
54 |
import voms_helper |
55 |
from kamaki.clients import ClientError |
56 |
|
57 |
LOG = getLogger(__name__) |
58 |
|
59 |
# Environment variable used to pass the request context
|
60 |
CONTEXT_ENV = 'snf.context'
|
61 |
|
62 |
|
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_"
|
66 |
|
67 |
"""Global variables that contain VOMS related paths
|
68 |
"""
|
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'
|
74 |
|
75 |
class VomsAuthN(): |
76 |
"""Filter that checks for the SSL data in the reqest.
|
77 |
|
78 |
Sets 'ssl' in the context as a dictionary containing this data.
|
79 |
"""
|
80 |
|
81 |
def __init__(self, *args, **kwargs): |
82 |
|
83 |
|
84 |
# VOMS stuff
|
85 |
try:
|
86 |
self.voms_json = json.loads(
|
87 |
open(VOMS_POLICY).read())
|
88 |
except ValueError: |
89 |
raise ClientError(
|
90 |
'Bad Formatted VOMS json',
|
91 |
details='The VOMS json data was not corectly formatted in file %s' % VOMS_POLICY)
|
92 |
except:
|
93 |
raise ClientError(
|
94 |
'No loading of VOMS json file',
|
95 |
details='The VOMS json file located in %s was not loaded' % VOMS_POLICY)
|
96 |
|
97 |
self._no_verify = False |
98 |
|
99 |
#super(VomsAuthN, self).__init__(*args, **kwargs)
|
100 |
|
101 |
@staticmethod
|
102 |
def _get_cert_chain(ssl_info): |
103 |
"""Return certificate and chain from the ssl info in M2Crypto format"""
|
104 |
|
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) |
109 |
chain.push(aux) |
110 |
return cert, chain
|
111 |
|
112 |
def _get_voms_info(self, ssl_info): |
113 |
"""Extract voms info from ssl_info and return dict with it."""
|
114 |
|
115 |
try:
|
116 |
cert, chain = self._get_cert_chain(ssl_info)
|
117 |
except M2Crypto.X509.X509Error:
|
118 |
raise ClientError(
|
119 |
'SSL data not verified',
|
120 |
details=CONTEXT_ENV) |
121 |
|
122 |
with voms_helper.VOMS(VOMSDIR_PATH,
|
123 |
CA_PATH, VOMSAPI_LIB) as v:
|
124 |
if self._no_verify: |
125 |
v.set_no_verify() |
126 |
|
127 |
voms_data = v.retrieve(cert, chain) |
128 |
|
129 |
if not voms_data: |
130 |
raise VomsError(v.error.value)
|
131 |
|
132 |
d = {} |
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)
|
138 |
else:
|
139 |
d[attr[0]] = getattr(voms_data, attr[1]) |
140 |
|
141 |
d["fqans"] = []
|
142 |
for fqan in iter(voms_data.fqan): |
143 |
if fqan is None: |
144 |
break
|
145 |
d["fqans"].append(fqan)
|
146 |
|
147 |
return d
|
148 |
|
149 |
@staticmethod
|
150 |
def _split_fqan(fqan): |
151 |
"""
|
152 |
gets a fqan and returns a tuple containing
|
153 |
(vo/groups, role, capability)
|
154 |
"""
|
155 |
l = fqan.split("/")
|
156 |
capability = l.pop().split("=")[-1] |
157 |
role = l.pop().split("=")[-1] |
158 |
vogroup = "/".join(l)
|
159 |
return (vogroup, role, capability)
|
160 |
|
161 |
def _process_environ(self, environ): |
162 |
|
163 |
LOG.warning("Getting the environment parameters...")
|
164 |
# the environment variable CONTENT_LENGTH may be empty or missing
|
165 |
try:
|
166 |
request_body_size = int(environ.get('CONTENT_LENGTH', 0)) |
167 |
except (ValueError): |
168 |
request_body_size = 0
|
169 |
raise ClientError(
|
170 |
'Not auth method provided',
|
171 |
details='The request body is empty, while it should contain the authentication method')
|
172 |
|
173 |
request_body = environ['wsgi.input'].read(request_body_size)
|
174 |
|
175 |
print request_body
|
176 |
|
177 |
request_body = request_body.replace("true","\"true\"") |
178 |
request_body = request_body.replace('"','\'' ) |
179 |
|
180 |
params_parsed = ast.literal_eval(request_body) |
181 |
|
182 |
|
183 |
params = {} |
184 |
for k, v in params_parsed.iteritems(): |
185 |
if k in ('self', 'context'): |
186 |
continue
|
187 |
if k.startswith('_'): |
188 |
continue
|
189 |
params[k] = v |
190 |
|
191 |
|
192 |
environ[PARAMS_ENV] = params |
193 |
print environ[PARAMS_ENV]
|
194 |
|
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", {})
|
201 |
if "voms" in auth: |
202 |
if "true" in auth["voms"]: |
203 |
return True |
204 |
else:
|
205 |
raise ClientError(
|
206 |
'Error in json',
|
207 |
details='Error in JSON, voms must be set to true')
|
208 |
|
209 |
return False |
210 |
|
211 |
|
212 |
def authenticate(self,ssl_data): |
213 |
|
214 |
try:
|
215 |
voms_info = self._get_voms_info(ssl_data)
|
216 |
except VomsError as e: |
217 |
raise e
|
218 |
user_dn = voms_info["user"]
|
219 |
user_vo = voms_info["voname"]
|
220 |
user_fqans = voms_info["fqans"]
|
221 |
|
222 |
return user_dn, user_vo, user_fqans
|
223 |
|
224 |
|
225 |
def process_request(self, environ): |
226 |
|
227 |
print "Inside process_Request at last!!!!" |
228 |
if not self.is_applicable(environ): |
229 |
return self.application |
230 |
|
231 |
ssl_dict = { |
232 |
"dn": environ.get(SSL_CLIENT_S_DN_ENV, None), |
233 |
"cert": environ.get(SSL_CLIENT_CERT_ENV, None), |
234 |
"chain": [],
|
235 |
} |
236 |
for k, v in environ.iteritems(): |
237 |
if k.startswith(SSL_CLIENT_CERT_CHAIN_ENV_PREFIX):
|
238 |
ssl_dict["chain"].append(v)
|
239 |
|
240 |
voms_info = self._get_voms_info(ssl_dict)
|
241 |
|
242 |
params = environ[PARAMS_ENV] |
243 |
|
244 |
tenant_from_req = params["auth"].get("tenantName", None) |
245 |
|
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
|
251 |
|
252 |
return user_dn, user_vo, user_fqans
|