ffc78cdc8927fb370f9def0e9bd7a84fee23975e
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / aa / attrresolv / provider / RegExAttributeDefinition.java
1 /*
2  * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
3  * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
4  * provided that the following conditions are met: Redistributions of source code must retain the above copyright
5  * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
6  * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
7  * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
8  * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu>Internet2 Project.
9  * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
10  * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
11  * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
12  * products derived from this software without specific prior written permission. For written permission, please contact
13  * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
14  * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
15  * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
16  * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
18  * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
19  * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
21  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
23  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 /*
27  * Contributed by SunGard SCT.
28  */
29
30 package edu.internet2.middleware.shibboleth.aa.attrresolv.provider;
31
32 import java.security.Principal;
33 import java.util.Collection;
34 import java.util.Iterator;
35 import java.util.regex.Matcher;
36 import java.util.regex.Pattern;
37
38 import org.apache.log4j.Logger;
39 import org.w3c.dom.Element;
40
41 import edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn;
42 import edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies;
43 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolutionPlugInException;
44 import edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute;
45
46 /**
47  * The RegExAttributeDefinition allows regular expression based replacements on attribute values, using the regex syntax
48  * allowed by java.util.regex.Pattern. Capturing groups can be specified in the regex string using parenthesis and in
49  * the replacement string using $i, where i = 0-9. Case-insensitive matches can be specified using the 'ignoreCase'
50  * attribute set to true. No other flags allowed by java.util.regex.Pattern are deemed useful and hence not supported
51  * yet, but are easy to add if needed.
52  * 
53  * @author <a href="mailto:vgoenka@sungardsct.com">Vishal Goenka </a>
54  */
55
56 /*
57  * An alternate way to implement a regex replacement would be to write a special value handler, pre-configured with the
58  * regex pattern and replacement string. Since ValueHandlers are initialized using the default no-args constructor,
59  * their re-usability based on configuration parameters is limited. The RegExAttributeDefinition therefore builds on the
60  * SimpleAttributeDefinition by allowing specification of regular expression parameters as configuration values instead.
61  * A value handler may also be specified, if needed. The value handler will see the 'formatted' value after the regular
62  * expression substitution.
63  */
64
65 public class RegExAttributeDefinition extends SimpleBaseAttributeDefinition implements AttributeDefinitionPlugIn {
66
67         private static Logger log = Logger.getLogger(RegExAttributeDefinition.class.getName());
68
69         // The pattern to match the source attribute value with
70         private Pattern pattern;
71
72         // Unless partialMatch is set to true (defaults to false), the pattern MUST match the full value
73         private boolean partialMatch = false;
74
75         // The replacement string to replace the matched groups in the pattern with, must be non-empty unless partialMatch
76         // is set to true
77         private String replacement;
78
79         public RegExAttributeDefinition(Element e) throws ResolutionPlugInException {
80
81                 super(e);
82
83                 try {
84                         String regex = e.getAttribute("regex");
85                         if ((regex == null) || ("".equals(regex)))
86                                 throw new ResolutionPlugInException("(" + getId()
87                                                 + ") 'regex' is a required attribute for RegExAttributeDefinition");
88
89                         partialMatch = Boolean.valueOf(e.getAttribute("partialMatch")).booleanValue();
90
91                         replacement = e.getAttribute("replacement");
92                         if (!partialMatch && ((replacement == null) || ("".equals(replacement))))
93                                 throw new ResolutionPlugInException(
94                                                 "("
95                                                                 + getId()
96                                                                 + ") 'replacement' MUST NOT be empty, unless 'partialMatch' is true for RegExAttributeDefinition");
97
98                         int flags = 0;
99                         boolean ignoreCase = Boolean.valueOf(e.getAttribute("ignoreCase")).booleanValue();
100                         if (ignoreCase) flags = Pattern.CASE_INSENSITIVE;
101
102                         pattern = Pattern.compile(regex, flags);
103
104                         if (log.isDebugEnabled())
105                                 log.debug("RegEx Pattern = " + pattern.pattern() + ", Replacement = " + replacement + " for ("
106                                                 + getId() + ")");
107                 } catch (ResolutionPlugInException ex) {
108                         // To ensure that exceptions thrown in the constructor are logged!
109                         log.error(ex.getMessage());
110                         throw ex;
111                 }
112         }
113
114         /**
115          * @see edu.internet2.middleware.shibboleth.aa.attrresolv.AttributeDefinitionPlugIn#resolve(edu.internet2.middleware.shibboleth.aa.attrresolv.ResolverAttribute,
116          *      java.security.Principal, java.lang.String, java.lang.String,
117          *      edu.internet2.middleware.shibboleth.aa.attrresolv.Dependencies)
118          */
119         public void resolve(ResolverAttribute attribute, Principal principal, String requester, String responder,
120                         Dependencies depends) throws ResolutionPlugInException {
121
122                 // Resolve all dependencies to arrive at the source values (unformatted)
123                 Collection results = resolveDependencies(attribute, principal, requester, depends);
124
125                 Iterator resultsIt = results.iterator();
126
127                 while (resultsIt.hasNext()) {
128                         String value = getString(resultsIt.next());
129                         Matcher m = pattern.matcher(value);
130                         try {
131                                 if (partialMatch || m.matches()) attribute.addValue(m.replaceAll(replacement));
132                                 else log.debug("Attribute value for (" + getId() + ") --> (" + value + ") did not match regex pattern");
133                         } catch (Exception e) {
134                                 // We simply log an error for values that give errors during formatting rather than abandoning the whole
135                                 // process
136                                 log.error("Attribute value for (" + getId() + ") --> (" + value
137                                                 + ") failed during regex processing with exception: " + e.getMessage());
138                         }
139                 }
140                 attribute.setResolved();
141         }
142 }