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