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