1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package edu.internet2.middleware.shibboleth.common.attribute.filtering.provider;
19
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.concurrent.locks.Lock;
28
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31 import org.springframework.context.ApplicationContext;
32
33 import edu.internet2.middleware.shibboleth.common.attribute.BaseAttribute;
34 import edu.internet2.middleware.shibboleth.common.attribute.filtering.AttributeFilteringEngine;
35 import edu.internet2.middleware.shibboleth.common.attribute.filtering.AttributeFilteringException;
36 import edu.internet2.middleware.shibboleth.common.config.BaseReloadableService;
37 import edu.internet2.middleware.shibboleth.common.profile.provider.SAMLProfileRequestContext;
38 import edu.internet2.middleware.shibboleth.common.service.ServiceException;
39
40
41
42
43 public class ShibbolethAttributeFilteringEngine extends BaseReloadableService implements
44 AttributeFilteringEngine<SAMLProfileRequestContext> {
45
46
47 private final Logger log = LoggerFactory.getLogger(ShibbolethAttributeFilteringEngine.class);
48
49
50 private List<AttributeFilterPolicy> filterPolicies;
51
52
53 public ShibbolethAttributeFilteringEngine() {
54 super();
55 filterPolicies = new ArrayList<AttributeFilterPolicy>();
56 }
57
58
59
60
61
62
63 public List<AttributeFilterPolicy> getFilterPolicies() {
64 return filterPolicies;
65 }
66
67
68 public Map<String, BaseAttribute> filterAttributes(Map<String, BaseAttribute> attributes,
69 SAMLProfileRequestContext context) throws AttributeFilteringException {
70
71 log.debug(getId() + " filtering {} attributes for principal {}", attributes.size(), context.getPrincipalName());
72
73 if (attributes.size() == 0) {
74 return new HashMap<String, BaseAttribute>();
75 }
76
77 if (getFilterPolicies() == null) {
78 log.debug("No filter policies were loaded in {}, filtering out all attributes for {}", getId(), context
79 .getPrincipalName());
80 return new HashMap<String, BaseAttribute>();
81 }
82
83 ShibbolethFilteringContext filterContext = new ShibbolethFilteringContext(attributes, context);
84 Lock readLock = getReadWriteLock().readLock();
85 readLock.lock();
86 try{
87 for (AttributeFilterPolicy filterPolicy : filterPolicies) {
88 filterAttributes(filterContext, filterPolicy);
89 runDenyRules(filterContext);
90 }
91 }finally{
92 readLock.unlock();
93 }
94
95 Iterator<Entry<String, BaseAttribute>> attributeEntryItr = attributes.entrySet().iterator();
96 Entry<String, BaseAttribute> attributeEntry;
97 BaseAttribute attribute;
98 Collection retainedValues;
99 while (attributeEntryItr.hasNext()) {
100 attributeEntry = attributeEntryItr.next();
101 attribute = attributeEntry.getValue();
102 retainedValues = filterContext.getRetainedValues(attribute.getId(), false);
103 attribute.getValues().clear();
104 attribute.getValues().addAll(retainedValues);
105 if (attribute.getValues().size() == 0) {
106 log.debug("Removing attribute from return set, no more values: {}", attribute.getId());
107 attributeEntryItr.remove();
108 }else{
109 log.debug("Attribute {} has {} values after filtering", attribute.getId(), attribute
110 .getValues().size());
111 }
112 }
113
114 log.debug("Filtered attributes for principal {}. The following attributes remain: {}", context
115 .getPrincipalName(), attributes.keySet());
116 return attributes;
117 }
118
119
120
121
122
123
124
125
126
127
128 protected void filterAttributes(ShibbolethFilteringContext filterContext, AttributeFilterPolicy filterPolicy)
129 throws FilterProcessingException {
130 log.debug("Evaluating if filter policy {} is active for principal {}", filterPolicy.getPolicyId(),
131 filterContext.getAttributeRequestContext().getPrincipalName());
132 MatchFunctor policyRequirement = filterPolicy.getPolicyRequirementRule();
133 if (policyRequirement == null || !policyRequirement.evaluatePolicyRequirement(filterContext)) {
134 log.debug("Filter policy {} is not active for principal {}", filterPolicy.getPolicyId(), filterContext
135 .getAttributeRequestContext().getPrincipalName());
136 return;
137 }
138
139 log.debug("Filter policy {} is active for principal {}", filterPolicy.getPolicyId(), filterContext
140 .getAttributeRequestContext().getPrincipalName());
141 for (AttributeRule attributeRule : filterPolicy.getAttributeRules()) {
142 filterAttributes(filterContext, attributeRule);
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155
156
157 protected void filterAttributes(ShibbolethFilteringContext filterContext, AttributeRule attributeRule)
158 throws FilterProcessingException {
159 String attributeId = attributeRule.getAttributeId();
160
161 MatchFunctor permitRule = attributeRule.getPermitValueRule();
162 if (permitRule != null) {
163 log.debug("Processing permit value rule for attribute {} for principal {}", attributeId, filterContext
164 .getAttributeRequestContext().getPrincipalName());
165 BaseAttribute attribute = filterContext.getUnfilteredAttributes().get(attributeId);
166 if(attribute == null){
167 return;
168 }
169
170 Collection unfilteredValues = attribute.getValues();
171 Collection retainedValues = filterContext.getRetainedValues(attributeId, false);
172 for (Object value : unfilteredValues) {
173 if (permitRule.evaluatePermitValue(filterContext, attributeId, value)) {
174 log.trace("The following value for attribute {} meets the permit value rule: {}", attributeId,
175 value == null ? "<null>" : value.toString());
176 retainedValues.add(value);
177 } else {
178 log.trace("The following value for attribute {} does not meet permit value rule: {}", attributeId,
179 value == null ? "<null>" : value.toString());
180 }
181 }
182 }
183
184 MatchFunctor denyRule = attributeRule.getDenyValueRule();
185 if (denyRule != null) {
186 log.debug("Registering deny value rule for attribute {} for principal {}", attributeId, filterContext
187 .getAttributeRequestContext().getPrincipalName());
188 List<MatchFunctor> denyRules = filterContext.getDenyValueRules().get(attributeId);
189
190 if (denyRules == null) {
191 denyRules = new ArrayList<MatchFunctor>();
192 filterContext.getDenyValueRules().put(attributeId, denyRules);
193 }
194
195 denyRules.add(denyRule);
196 }
197 }
198
199
200
201
202
203
204
205
206 protected void runDenyRules(ShibbolethFilteringContext filterContext) throws FilterProcessingException {
207 Map<String, List<MatchFunctor>> denyRuleEntries = filterContext.getDenyValueRules();
208 if (denyRuleEntries.isEmpty()) {
209 return;
210 }
211
212 List<MatchFunctor> denyRules;
213 Collection attributeValues;
214 Object attributeValue;
215 for (Entry<String, List<MatchFunctor>> denyRuleEntry : denyRuleEntries.entrySet()) {
216 denyRules = denyRuleEntry.getValue();
217 attributeValues = filterContext.getRetainedValues(denyRuleEntry.getKey(), false);
218 if (denyRules.isEmpty() || attributeValues.isEmpty()) {
219 continue;
220 }
221
222 Iterator<?> attributeValueItr = attributeValues.iterator();
223 for (MatchFunctor denyRule : denyRules) {
224 while (attributeValueItr.hasNext()) {
225 attributeValue = attributeValueItr.next();
226 if (denyRule.evaluateDenyRule(filterContext, denyRuleEntry.getKey(), attributeValue)) {
227 log.trace("Removing the following value of attribute {} per deny rule: {}", denyRuleEntry
228 .getKey(), attributeValue);
229 attributeValueItr.remove();
230 }
231 }
232 }
233 }
234 }
235
236
237 protected void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException {
238 List<AttributeFilterPolicy> oldFilterPolicies = filterPolicies;
239
240 try {
241 List<AttributeFilterPolicy> newFilterPolicies = new ArrayList<AttributeFilterPolicy>();
242 String[] beanNames = newServiceContext.getBeanNamesForType(AttributeFilterPolicy.class);
243 for (String beanName : beanNames) {
244 newFilterPolicies.add((AttributeFilterPolicy) newServiceContext.getBean(beanName));
245 }
246 filterPolicies = newFilterPolicies;
247 } catch (Exception e) {
248 filterPolicies = oldFilterPolicies;
249 throw new ServiceException(getId() + " configuration is not valid, retaining old configuration", e);
250 }
251 }
252 }