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 try{
86 for (AttributeFilterPolicy filterPolicy : filterPolicies) {
87 filterAttributes(filterContext, filterPolicy);
88 runDenyRules(filterContext);
89 }
90 }finally{
91 readLock.unlock();
92 }
93
94 Iterator<Entry<String, BaseAttribute>> attributeEntryItr = attributes.entrySet().iterator();
95 Entry<String, BaseAttribute> attributeEntry;
96 BaseAttribute attribute;
97 Collection retainedValues;
98 while (attributeEntryItr.hasNext()) {
99 attributeEntry = attributeEntryItr.next();
100 attribute = attributeEntry.getValue();
101 retainedValues = filterContext.getRetainedValues(attribute.getId(), false);
102 attribute.getValues().retainAll(retainedValues);
103 if (attribute.getValues().size() == 0) {
104 log.debug("Removing attribute from return set, no more values: {}", attribute.getId());
105 attributeEntryItr.remove();
106 }
107 }
108
109 log.debug("Filtered attributes for principal {}. The following attributes remain: {}", context
110 .getPrincipalName(), attributes.keySet());
111 return attributes;
112 }
113
114
115
116
117
118
119
120
121
122
123 protected void filterAttributes(ShibbolethFilteringContext filterContext, AttributeFilterPolicy filterPolicy)
124 throws FilterProcessingException {
125 log.debug("Evaluating if filter policy {} is active for principal {}", filterPolicy.getPolicyId(),
126 filterContext.getAttributeRequestContext().getPrincipalName());
127 MatchFunctor policyRequirement = filterPolicy.getPolicyRequirementRule();
128 if (policyRequirement == null || !policyRequirement.evaluatePolicyRequirement(filterContext)) {
129 log.debug("Filter policy {} is not active for principal {}", filterPolicy.getPolicyId(), filterContext
130 .getAttributeRequestContext().getPrincipalName());
131 return;
132 }
133
134 log.debug("Filter policy {} is active for principal {}", filterPolicy.getPolicyId(), filterContext
135 .getAttributeRequestContext().getPrincipalName());
136 for (AttributeRule attributeRule : filterPolicy.getAttributeRules()) {
137 filterAttributes(filterContext, attributeRule);
138 }
139 }
140
141
142
143
144
145
146
147
148
149
150
151
152 protected void filterAttributes(ShibbolethFilteringContext filterContext, AttributeRule attributeRule)
153 throws FilterProcessingException {
154 String attributeId = attributeRule.getAttributeId();
155 Collection attributeValues = filterContext.getRetainedValues(attributeId, false);
156
157 MatchFunctor permitRule = attributeRule.getPermitValueRule();
158 if (permitRule != null) {
159 log.debug("Processing permit value rule for attribute {} for principal {}", attributeId, filterContext
160 .getAttributeRequestContext().getPrincipalName());
161 BaseAttribute attribute = filterContext.getUnfilteredAttributes().get(attributeId);
162 if(attribute == null){
163 return;
164 }
165
166 Collection unfilteredValues = attribute.getValues();
167 for (Object attributeValue : unfilteredValues) {
168 if (permitRule.evaluatePermitValue(filterContext, attributeId, attributeValue)) {
169 attributeValues.add(attributeValue);
170 } else {
171 log.trace("The following value for attribute {} does not meet permit value rule: {}", attributeId,
172 attributeValue.toString());
173 }
174 }
175 }
176
177 MatchFunctor denyRule = attributeRule.getDenyValueRule();
178 if (denyRule != null) {
179 log.debug("Registering deny value rule for attribute {} for principal {}", attributeId, filterContext
180 .getAttributeRequestContext().getPrincipalName());
181 List<MatchFunctor> denyRules = filterContext.getDenyValueRules().get(attributeId);
182
183 if (denyRules == null) {
184 denyRules = new ArrayList<MatchFunctor>();
185 filterContext.getDenyValueRules().put(attributeId, denyRules);
186 }
187
188 denyRules.add(denyRule);
189 }
190 }
191
192
193
194
195
196
197
198
199 protected void runDenyRules(ShibbolethFilteringContext filterContext) throws FilterProcessingException {
200 Map<String, List<MatchFunctor>> denyRuleEntries = filterContext.getDenyValueRules();
201 if (denyRuleEntries.isEmpty()) {
202 return;
203 }
204
205 List<MatchFunctor> denyRules;
206 Collection attributeValues;
207 Object attributeValue;
208 for (Entry<String, List<MatchFunctor>> denyRuleEntry : denyRuleEntries.entrySet()) {
209 denyRules = denyRuleEntry.getValue();
210 attributeValues = filterContext.getRetainedValues(denyRuleEntry.getKey(), false);
211 if (denyRules.isEmpty() || attributeValues.isEmpty()) {
212 continue;
213 }
214
215 Iterator<?> attributeValueItr = attributeValues.iterator();
216 for (MatchFunctor denyRule : denyRules) {
217 while (attributeValueItr.hasNext()) {
218 attributeValue = attributeValueItr.next();
219 if (denyRule.evaluateDenyRule(filterContext, denyRuleEntry.getKey(), attributeValue)) {
220 log.trace("Removing the following value of attribute {} per deny rule: {}", denyRuleEntry
221 .getKey(), attributeValue);
222 attributeValueItr.remove();
223 }
224 }
225 }
226 }
227 }
228
229
230 protected void onNewContextCreated(ApplicationContext newServiceContext) throws ServiceException {
231 List<AttributeFilterPolicy> oldFilterPolicies = filterPolicies;
232
233 try {
234 List<AttributeFilterPolicy> newFilterPolicies = new ArrayList<AttributeFilterPolicy>();
235 String[] beanNames = newServiceContext.getBeanNamesForType(AttributeFilterPolicy.class);
236 for (String beanName : beanNames) {
237 newFilterPolicies.add((AttributeFilterPolicy) newServiceContext.getBean(beanName));
238 }
239 filterPolicies = newFilterPolicies;
240 } catch (Exception e) {
241 filterPolicies = oldFilterPolicies;
242 throw new ServiceException(getId() + " configuration is not valid, retaining old configuration", e);
243 }
244 }
245 }