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 package fr.paris.lutece.plugins.mylutece.modules.saml.authentication.engine;
35
36 import fr.paris.lutece.plugins.mylutece.modules.saml.authentication.config.ConfigProperties;
37 import fr.paris.lutece.plugins.mylutece.modules.saml.authentication.config.Constants;
38 import fr.paris.lutece.plugins.mylutece.modules.saml.authentication.exceptions.CertificateValidationException;
39 import fr.paris.lutece.plugins.mylutece.modules.saml.authentication.exceptions.SAMLParsingException;
40 import fr.paris.lutece.plugins.mylutece.modules.saml.authentication.util.X509CertificateHelper;
41 import fr.paris.lutece.portal.service.security.LuteceUser;
42 import fr.paris.lutece.portal.service.util.AppLogService;
43
44 import org.opensaml.saml2.core.Assertion;
45 import org.opensaml.saml2.core.Attribute;
46 import org.opensaml.saml2.core.AttributeStatement;
47 import org.opensaml.saml2.core.Audience;
48 import org.opensaml.saml2.core.AudienceRestriction;
49 import org.opensaml.saml2.core.Conditions;
50 import org.opensaml.saml2.core.Response;
51 import org.opensaml.saml2.core.Subject;
52 import org.opensaml.saml2.core.SubjectConfirmation;
53 import org.opensaml.saml2.core.SubjectConfirmationData;
54 import org.opensaml.saml2.core.validator.ResponseSchemaValidator;
55 import org.opensaml.saml2.metadata.RequestedAttribute;
56
57 import org.opensaml.xml.XMLObject;
58 import org.opensaml.xml.schema.XSString;
59 import org.opensaml.xml.signature.KeyInfo;
60 import org.opensaml.xml.signature.Signature;
61 import org.opensaml.xml.signature.X509Data;
62 import org.opensaml.xml.validation.ValidationException;
63
64 import java.io.IOException;
65
66 import java.security.cert.CertificateException;
67 import java.security.cert.X509Certificate;
68
69 import java.util.ArrayList;
70 import java.util.HashMap;
71 import java.util.Iterator;
72 import java.util.List;
73 import java.util.Map;
74
75
76 public class SAMLResponseManager
77 {
78 private Response response = null;
79
80
81
82
83
84
85 public SAMLResponseManager( Response response ) throws SAMLParsingException
86 {
87 initialize( response );
88 }
89
90
91
92
93
94
95
96
97 public void initialize( Response response ) throws SAMLParsingException
98 {
99 if ( response == null )
100 {
101 String message = "La Response ne devrait pas �tre nulle";
102 AppLogService.info( message );
103 throw new SAMLParsingException( message );
104 }
105
106 this.response = response;
107
108
109 this.validateResponseSchema( );
110
111
112 this.validateResponseContent( );
113 }
114
115
116
117
118
119
120 private void validateResponseSchema( ) throws SAMLParsingException
121 {
122
123 ResponseSchemaValidator validator = new ResponseSchemaValidator( );
124
125 try
126 {
127 validator.validate( response );
128 }
129 catch ( ValidationException e )
130 {
131 String message = "Erreur lors de la validation du sch�ma de la Response : " + e.getLocalizedMessage( );
132 AppLogService.info( message );
133 throw new SAMLParsingException( message );
134 }
135 }
136
137
138
139
140
141
142 private void validateResponseContent( ) throws SAMLParsingException
143 {
144
145 String destination = response.getDestination( );
146
147 if ( destination == null )
148 {
149 String message = "La Response devrait contenir un attribut Destination";
150 AppLogService.info( message );
151 throw new SAMLParsingException( message );
152 }
153
154
155 List<Assertion> assertions = response.getAssertions( );
156
157 if ( assertions.size( ) != 1 )
158 {
159 String message = "La Response devrait contenir une et une seule Assertion. Elle en contient " +
160 assertions.size( );
161 AppLogService.info( message );
162 throw new SAMLParsingException( message );
163 }
164
165
166 Signature signature = assertions.get( 0 ).getSignature( );
167
168 if ( signature == null )
169 {
170 String message = "L'Assertion devrait contenir une Signature";
171 AppLogService.info( message );
172 throw new SAMLParsingException( message );
173 }
174
175
176 KeyInfo keyInfo = signature.getKeyInfo( );
177
178 if ( keyInfo == null )
179 {
180 String message = "La Signature devrait contenir un KeyInfo";
181 AppLogService.info( message );
182 throw new SAMLParsingException( message );
183 }
184
185
186 List<X509Data> x509Datas = keyInfo.getX509Datas( );
187
188 if ( x509Datas.size( ) != 1 )
189 {
190 String message = "Le KeyInfo devrait contenir un et un seul X509Data. Il en contient " +
191 x509Datas.size( );
192 AppLogService.info( message );
193 throw new SAMLParsingException( message );
194 }
195
196
197 List<org.opensaml.xml.signature.X509Certificate> x509Certificates = x509Datas.get( 0 ).getX509Certificates( );
198
199 if ( x509Certificates.size( ) != 1 )
200 {
201 String message = "Le X509Data devrait contenir un et un seul X509Certificate. Il en contient " +
202 x509Certificates.size( );
203 AppLogService.info( message );
204 throw new SAMLParsingException( message );
205 }
206
207
208 Subject subject = assertions.get( 0 ).getSubject( );
209
210 if ( subject == null )
211 {
212 String message = "L'Assertion devrait contenir un Subject";
213 AppLogService.info( message );
214 throw new SAMLParsingException( message );
215 }
216
217
218 List<SubjectConfirmation> subjectConfirmation = subject.getSubjectConfirmations( );
219
220 if ( subjectConfirmation.size( ) != 1 )
221 {
222 String message = "Le Subject devrait contenir un et un seul SubjectConfirmations. Il en contient " +
223 subjectConfirmation.size( );
224 AppLogService.info( message );
225 throw new SAMLParsingException( message );
226 }
227
228
229 SubjectConfirmationData subjectConfirmationData = subjectConfirmation.get( 0 ).getSubjectConfirmationData( );
230
231 if ( subjectConfirmationData == null )
232 {
233 String message = "Le SubjectConfirmation devrait contenir un SubjectConfirmationData";
234 AppLogService.info( message );
235 throw new SAMLParsingException( message );
236 }
237
238
239 if ( ( subjectConfirmationData.getRecipient( ) == null ) || ( subjectConfirmationData.getAddress( ) == null ) )
240 {
241 String message = "Le SubjectConfirmationData ne contient pas tous les attributs requis";
242 AppLogService.info( message );
243 throw new SAMLParsingException( message );
244 }
245
246
247 Conditions conditions = assertions.get( 0 ).getConditions( );
248
249 if ( conditions == null )
250 {
251 String message = "L'Assertion devrait contenir un Conditions";
252 AppLogService.info( message );
253 throw new SAMLParsingException( message );
254 }
255
256
257 if ( ( conditions.getNotBefore( ) == null ) || ( conditions.getNotOnOrAfter( ) == null ) )
258 {
259 String message = "Conditions devrait contenir un NotBefore et un NotOnOrAfter";
260 AppLogService.info( message );
261 throw new SAMLParsingException( message );
262 }
263
264
265 List<AudienceRestriction> audienceRestr = conditions.getAudienceRestrictions( );
266
267 if ( audienceRestr.size( ) != 1 )
268 {
269 String message = "Conditions devrait contenir un et un seul AudienceRestrictions. Il en contient " +
270 audienceRestr.size( );
271 AppLogService.info( message );
272 throw new SAMLParsingException( message );
273 }
274
275
276 List<Audience> audience = audienceRestr.get( 0 ).getAudiences( );
277
278 if ( audience.size( ) != 1 )
279 {
280 String message = "AudienceRestriction devrait contenir un et un seul Audience. Il en contient " +
281 audience.size( );
282 AppLogService.info( message );
283 throw new SAMLParsingException( message );
284 }
285
286
287 List<AttributeStatement> attributesStatements = assertions.get( 0 ).getAttributeStatements( );
288
289 if ( attributesStatements.size( ) != 1 )
290 {
291 String message = "L'Assertion devrait contenir un et un seul AttributeStatement. Elle en contient " +
292 attributesStatements.size( );
293 AppLogService.info( message );
294 throw new SAMLParsingException( message );
295 }
296 }
297
298
299
300
301
302
303
304
305
306 private String getAttributeValue( Attribute attribute )
307 throws SAMLParsingException
308 {
309 List<XMLObject> values = attribute.getAttributeValues( );
310
311 if ( values.size( ) != 1 )
312 {
313 String message = "L'attribut devrait contenir une et une seule AttributeValue. Il en contient " +
314 values.size( );
315 AppLogService.info( message );
316 throw new SAMLParsingException( message );
317 }
318
319 XSString stringValue = ( (XSString) values.get( 0 ) );
320
321 return stringValue.getValue( );
322 }
323
324
325
326
327
328
329
330 private List<String> getAttributeValues( Attribute attribute )
331 {
332 List<String> result = new ArrayList<String>( );
333 List<XMLObject> values = attribute.getAttributeValues( );
334 Iterator<XMLObject> iter = values.listIterator( );
335
336 while ( iter.hasNext( ) )
337 {
338 XSString stringValue = (XSString) iter.next( );
339 result.add( stringValue.getValue( ) );
340 }
341
342 return result;
343 }
344
345
346
347
348
349
350
351 public Assertion getAssertion( )
352 {
353 return response.getAssertions( ).get( 0 );
354 }
355
356
357
358
359
360
361
362
363 public X509Certificate getSignatureCertificate( )
364 throws SAMLParsingException
365 {
366 X509Certificate cert = null;
367 KeyInfo keyInfo = this.getAssertion( ).getSignature( ).getKeyInfo( );
368 List<X509Data> x509Datas = keyInfo.getX509Datas( );
369 List<org.opensaml.xml.signature.X509Certificate> x509Certificates = x509Datas.get( 0 ).getX509Certificates( );
370 String b64ResponseCert = x509Certificates.get( 0 ).getValue( );
371
372 try
373 {
374 cert = X509CertificateHelper.buildX509Cert( b64ResponseCert );
375 }
376 catch ( CertificateException e )
377 {
378 String message = "Erreur lors de la recuperation du certificat de signature : " +
379 e.getLocalizedMessage( );
380 AppLogService.info( message );
381 throw new SAMLParsingException( message );
382 }
383 catch ( IOException e )
384 {
385 String message = "Erreur lors de la recuperation du certificat de signature : " +
386 e.getLocalizedMessage( );
387 AppLogService.info( message );
388 throw new SAMLParsingException( message );
389 }
390
391 return cert;
392 }
393
394
395
396
397
398
399
400 public List<Attribute> getAssertionAttributes( )
401 {
402 List<AttributeStatement> attributesStatements = getAssertion( ).getAttributeStatements( );
403
404 return attributesStatements.get( 0 ).getAttributes( );
405 }
406
407
408
409
410
411
412
413
414 private List<Attribute> getFilteredAssertionAttributes( List<RequestedAttribute> requestedAttributes )
415 {
416 List<Attribute> attributes = this.getAssertionAttributes( );
417
418 List<Attribute> filteredAttributes = new ArrayList<Attribute>( );
419 Iterator<Attribute> iter = attributes.listIterator( );
420 Attribute attribute;
421
422 while ( iter.hasNext( ) )
423 {
424 attribute = iter.next( );
425
426 Iterator<RequestedAttribute> iterReq = requestedAttributes.listIterator( );
427 RequestedAttribute reqAttribute;
428
429 while ( iterReq.hasNext( ) )
430 {
431 reqAttribute = iterReq.next( );
432
433 if ( reqAttribute.getName( ).equals( attribute.getName( ) ) )
434 {
435 filteredAttributes.add( attribute );
436
437 break;
438 }
439 }
440 }
441
442 return filteredAttributes;
443 }
444
445
446
447
448
449
450
451
452 public Map<String, String> getFilteredAssertionAttributesValues( List<RequestedAttribute> requestedAttributes )
453 {
454 List<Attribute> attributes = this.getFilteredAssertionAttributes( requestedAttributes );
455 Map<String, String> attributesValues = new HashMap<String, String>( );
456
457 Iterator<Attribute> iter = attributes.listIterator( );
458 Attribute attribute;
459
460 while ( iter.hasNext( ) )
461 {
462 attribute = iter.next( );
463 attributesValues.put( attribute.getName( ), this.getAttributeValues( attribute ).get( 0 ) );
464 }
465
466 return attributesValues;
467 }
468
469
470
471
472
473
474
475
476 public String getLuteceUserName( )
477 {
478 List<Attribute> attributes = this.getAssertionAttributes( );
479 Iterator<Attribute> iter = attributes.listIterator( );
480 Attribute attribute;
481
482 while ( iter.hasNext( ) )
483 {
484 attribute = iter.next( );
485
486 if ( attribute.getName( )
487 .equals( ConfigProperties.getInstance( )
488 .getProperty( Constants.LUTECE_USER_NAME_ATTRIBUTE_NAME_PROP ) ) )
489 {
490 try
491 {
492 return this.getAttributeValue( attribute );
493 }
494 catch ( SAMLParsingException e )
495 {
496 String message = "L'Attribut contenant le nom du LuteceUser ne devrait pas �tre multivalu�";
497 AppLogService.info( message );
498
499 return LuteceUser.ANONYMOUS_USERNAME;
500 }
501 }
502 }
503
504 return LuteceUser.ANONYMOUS_USERNAME;
505 }
506
507
508
509
510
511
512
513
514 public List<String> getLuteceUserGroups( )
515 {
516 List<Attribute> attributes = this.getAssertionAttributes( );
517 Iterator<Attribute> iter = attributes.listIterator( );
518 Attribute attribute;
519
520 while ( iter.hasNext( ) )
521 {
522 attribute = iter.next( );
523
524 if ( attribute.getName( )
525 .equals( ConfigProperties.getInstance( )
526 .getProperty( Constants.LUTECE_USER_GROUPS_ATTRIBUTE_NAME_PROP ) ) )
527 {
528 return this.getAttributeValues( attribute );
529 }
530 }
531
532 return new ArrayList<String>( );
533 }
534
535
536
537
538
539 public Response getResponse( )
540 {
541 return response;
542 }
543 }