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