Skip to content
Snippets Groups Projects
Commit 4d8cf48a authored by uhensler's avatar uhensler
Browse files

OO-2947: Attribute Handlers for UID and preferred language

parent 17b09b52
No related branches found
No related tags found
No related merge requests found
Showing
with 103 additions and 298 deletions
......@@ -31,7 +31,6 @@ import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import javax.servlet.http.HttpServletRequest;
......@@ -64,7 +63,6 @@ import org.olat.core.util.WebappHelper;
import org.olat.core.util.i18n.I18nModule;
import org.olat.restapi.security.RestSecurityBean;
import org.olat.shibboleth.manager.ShibbolethAttributes;
import org.olat.shibboleth.util.ShibbolethAttribute;
import org.springframework.beans.factory.annotation.Autowired;
/**
......@@ -158,8 +156,10 @@ public class ShibbolethDispatcher implements Dispatcher{
Map<String, String> attributesMap = getShibbolethAttributesFromRequest(req);
ShibbolethAttributes shibbolethAttriutes = CoreSpringFactory.getImpl(ShibbolethAttributes.class);
shibbolethAttriutes.init(attributesMap);
String uniqueID = getUniqueIdentifierFromRequest(req, resp, shibbolethAttriutes);
if(uniqueID == null) {
String uid = shibbolethAttriutes.getUID();
if(uid == null) {
handleException(new ShibbolethException(ShibbolethException.UNIQUE_ID_NOT_FOUND,"Unable to get unique identifier for subject. Make sure you are listed in the metadata.xml file and your resources your are trying to access are available and your are allowed to see them. (Resourceregistry). "),
req, resp, translator);
return;
}
......@@ -185,10 +185,10 @@ public class ShibbolethDispatcher implements Dispatcher{
return;
}
Authentication auth = securityManager.findAuthenticationByAuthusername(uniqueID, PROVIDER_SHIB);
Authentication auth = securityManager.findAuthenticationByAuthusername(uid, PROVIDER_SHIB);
if (auth == null) { // no matching authentication...
ShibbolethRegistrationController.putShibAttributes(req, shibbolethAttriutes);
ShibbolethRegistrationController.putShibUniqueID(req, uniqueID);
ShibbolethRegistrationController.putShibUniqueID(req, uid);
redirectToShibbolethRegistration(resp);
return;
}
......@@ -232,30 +232,21 @@ public class ShibbolethDispatcher implements Dispatcher{
}
}
private String getUniqueIdentifierFromRequest(HttpServletRequest req, HttpServletResponse resp, ShibbolethAttributes shibbolethAttributes) {
String uniqueID = shibbolethAttributes.getValueForAttributeName(shibbolethModule.getDefaultUIDAttribute());
if (uniqueID == null) {
handleException(new ShibbolethException(ShibbolethException.UNIQUE_ID_NOT_FOUND,"Unable to get unique identifier for subject. Make sure you are listed in the metadata.xml file and your resources your are trying to access are available and your are allowed to see them. (Resourceregistry). "),
req, resp, translator);
return null;
}
return uniqueID;
}
private Map<String, String> getShibbolethAttributesFromRequest(HttpServletRequest req) {
Set<String> translateableAttributes = shibbolethModule.getAttributeTranslator().getTranslateableAttributes();
Map<String, String> attributesMap = new HashMap<>();
Enumeration<String> headerEnum = req.getHeaderNames();
while(headerEnum.hasMoreElements()) {
String attribute = headerEnum.nextElement();
String attributeValue = req.getHeader(attribute);
ShibbolethAttribute shibbolethAttribute = ShibbolethAttribute.createFromUserRequestValue(attribute, attributeValue);
String attributeName = headerEnum.nextElement();
String attributeValue = req.getHeader(attributeName);
boolean validAndTranslateableAttribute = shibbolethAttribute.isValid() && translateableAttributes.contains(shibbolethAttribute.getName());
if(validAndTranslateableAttribute){
attributesMap.put(shibbolethAttribute.getName(),shibbolethAttribute.getValueString());
try {
attributeValue = new String(attributeValue.getBytes("ISO-8859-1"), "UTF-8");
if (shibbolethModule.getShibbolethAttributeNames().contains(attributeName)) {
attributesMap.put(attributeName, attributeValue);
}
} catch (UnsupportedEncodingException e) {
//bad luck
throw new AssertException("ISO-8859-1, or UTF-8 Encoding not supported",e);
}
}
......@@ -305,14 +296,6 @@ public class ShibbolethDispatcher implements Dispatcher{
if(val.equalsIgnoreCase(allowedValue)) {
return true;
}
// Could be multi-field attribute. Check for semi-colon delimited encodings
String[] multiValues = val.split(";");
for (String singleValue : multiValues) {
singleValue = singleValue.trim();
if(singleValue.equalsIgnoreCase(allowedValue)) {
return true;
}
}
}
}
return false;
......
......@@ -26,10 +26,13 @@
package org.olat.shibboleth;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.olat.core.configuration.AbstractSpringModule;
import org.olat.core.configuration.ConfigOnOff;
......@@ -78,10 +81,10 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
public static final String MULTIVALUE_SEPARATOR = ";";
@Value("${shibboleth.preferred.language}")
@Value("${shibboleth.preferred.language.shib}")
private String preferredLanguageAttribute;
@Value("${shibboleth.defaultUID:Shib-SwissEP-UniqueID}")
private String defaultUIDAttribute;
@Value("${shibboleth.uid.shib}")
private String uidAttributeName;
@Autowired @Qualifier("shibbolethUserMapping")
private HashMap<String, String> userMapping;
@Autowired @Qualifier("shibbolethAttributeHandler")
......@@ -195,12 +198,8 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
return operators;
}
/**
*
* @return the shib. default attribute which identifies an user by an unique key
*/
public String getDefaultUIDAttribute() {
return defaultUIDAttribute;
public String getUIDAttributeName() {
return uidAttributeName;
}
public String getLoginTemplate() {
......@@ -269,7 +268,7 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
setStringProperty("attribute2Values", attribute2Values, true);
}
public String getPreferredLanguageAttribute() {
public String getPreferredLanguageAttributeName() {
return preferredLanguageAttribute;
}
......@@ -319,4 +318,20 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
return authorMappingContains;
}
/**
* Get the names of all Shibboleth attributes which are configured either in
* the context.xml or in olat.local.properties
*/
public Collection<String> getShibbolethAttributeNames() {
Set<String> attributeNames = new HashSet<>();
attributeNames.addAll(attributeTranslator.getTranslateableAttributes());
attributeNames.addAll(userMapping.keySet());
attributeNames.add(uidAttributeName);
attributeNames.add(preferredLanguageAttribute);
attributeNames.add(authorMappingAttributeName);
attributeNames.add(attribute1);
attributeNames.add(attribute2);
return attributeNames;
}
}
\ No newline at end of file
......@@ -137,7 +137,7 @@ public class ShibbolethRegistrationController extends DefaultController implemen
locale = (Locale)ureq.getUserSession().getEntry(LocaleNegotiator.NEGOTIATED_LOCALE);
if(locale == null) {
String preferedLanguage = shibbolethAttributes.getValueForAttributeName(shibbolethModule.getPreferredLanguageAttribute());
String preferedLanguage = shibbolethAttributes.getPreferredLanguage();
if(preferedLanguage == null) {
locale = LocaleNegotiator.getPreferedLocale(ureq);
} else {
......
......@@ -80,6 +80,8 @@
<bean name="shibbolethAttributeHandler" class="java.util.HashMap" scope="prototype" >
<constructor-arg>
<map key-type="java.lang.String" value-type="java.lang.String">
<entry key='${shibboleth.uid.shib}' value='${shibboleth.uid.handler}' />
<entry key='${shibboleth.preferred.language.shib}' value='${shibboleth.preferred.language.handler}' />
<entry key='${shibboleth.user.mapping.email.shib}' value='${shibboleth.user.mapping.email.handler}' />
<entry key='${shibboleth.user.mapping.first.name.shib}' value='${shibboleth.user.mapping.first.name.handler}' />
<entry key='${shibboleth.user.mapping.last.name.shib}' value='${shibboleth.user.mapping.last.name.handler}' />
......
......@@ -84,6 +84,17 @@ public class ShibbolethAttributes {
return shibbolethAttributeHandlerFactory.getHandler(handlerName);
}
public String getUID() {
String uidAttributeName = shibbolethModule.getUIDAttributeName();
return getValueForAttributeName(uidAttributeName);
}
public String getPreferredLanguage() {
String langAttributeName = shibbolethModule.getPreferredLanguageAttributeName();
return getValueForAttributeName(langAttributeName);
}
public String getValueForAttributeName(String attributeName) {
return shibbolethMap.get(attributeName);
}
......
......@@ -36,18 +36,18 @@ public class AttributeTranslator {
// contains the mapping from the shib-attribute to the simplified shib attribute
private Map<String, String> attributeTranslations;
// contains predefined values that can be selected for a specific shib-attribute.
// Note that as key the simplified shib attribute is used and not the original
// contains predefined values that can be selected for a specific shib-attribute.
// Note that as key the simplified shib attribute is used and not the original
// attributes (see attributeTranslations map)
private Map<String, List<String>> attributeSelectableValues;
/**
* [used by spring]
*/
public AttributeTranslator() {
//
}
/**
* [used by spring]
*/
......@@ -64,21 +64,22 @@ public class AttributeTranslator {
}
public final Map<String, String> translateAttributesMap(Map<String, String> attributesMap) {
Map<String, String> convertedMap = new HashMap<String, String>(attributesMap.size());
Map<String, String> convertedMap = new HashMap<>(attributesMap.size());
Iterator<String> keys = attributesMap.keySet().iterator();
while (keys.hasNext()) {
String attribute = keys.next();
String translatedKey = translateAttribute(attribute);
String value = attributesMap.get(attribute);
convertedMap.put(translatedKey, value);
if (attributeTranslations.keySet().contains(attribute)) {
String translatedKey = translateAttribute(attribute);
String value = attributesMap.get(attribute);
convertedMap.put(translatedKey, value);
}
}
return convertedMap;
}
/**
* Translate Shibboleth Attributes according to configured attribute translations
* @param inName
......@@ -92,19 +93,19 @@ public class AttributeTranslator {
/**
* Get all valid values for this attribute if such values are defined. When no
* values are defined, NULL will be returned.
*
*
* @param attribute
* @return
*/
public String[] getSelectableValuesForAttribute(String attribute) {
List<String> list = attributeSelectableValues.get(attribute);
List<String> list = attributeSelectableValues.get(attribute);
if(list!=null) {
//only some attributes have selectable values
return list.toArray(new String[0]);
return list.toArray(new String[0]);
}
return new String[0];
}
/**
* Get all attributes identifiers that can be translated
* @return
......@@ -112,5 +113,5 @@ public class AttributeTranslator {
public Set<String> getTranslateableAttributes() {
return this.attributeTranslations.keySet();
}
}
/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
*/
package org.olat.shibboleth.util;
import java.io.UnsupportedEncodingException;
import java.util.regex.Pattern;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.StringHelper;
/**
* Description:<br>
* Represents a shibboleth attribut, which may contain values delimited by ";".
*
*
* <P>
* Initial Date: Oct 27, 2010 <br>
* @author patrick
*/
public final class ShibbolethAttribute{
private static final OLog log = Tracing.createLoggerFor(ShibbolethAttribute.class);
private static final Pattern SPLITTER = Pattern.compile(";");
private final String attributeName;
private final String originalValue;
private final String[] splittedValues;
private final static ShibbolethAttribute INVALID_ATTRIBUTE = new ShibbolethAttribute("INVALIDMARKED","INVALIDMARKED");
ShibbolethAttribute(String name, String value) {
if(isInvalidValue(value)){
throw new IllegalArgumentException("value must be not null and not empty");
}
this.attributeName = name;
this.originalValue = value;
this.splittedValues = SPLITTER.split(value);
}
public String getName(){
return attributeName;
}
public String getValueString(){
return originalValue;
}
public String[] getValues(){
return splittedValues;
}
String getFirstValue(){
return splittedValues[0];
}
public boolean isValid(){
return this != INVALID_ATTRIBUTE;
}
public static ShibbolethAttribute createFromUserRequestValue(String name, String rawRequestValue){
if(isInvalidValue(rawRequestValue)){
if(log.isDebug()){
log.debug("invalid attribute: " + name + " attributeValue: " + rawRequestValue);
}
return INVALID_ATTRIBUTE;
}
try {
String utf8Value = new String(rawRequestValue.getBytes("ISO-8859-1"), "UTF-8");
return new ShibbolethAttribute(name, utf8Value);
} catch (UnsupportedEncodingException e) {
//bad luck
throw new AssertException("ISO-8859-1, or UTF-8 Encoding not supported",e);
}
}
private static boolean isInvalidValue(String value){
return ( ! StringHelper.containsNonWhitespace(value));
}
}
......@@ -785,16 +785,19 @@ shibboleth.wayfReturnMobileUrl=$shibboleth.wayfReturnUrl
#for an example
shibboleth.wayf.additionalIDPs=
# set the name of the Shibboleth attribute used to identify authorized users
shibboleth.uid.shib=
shibboleth.uid.handler=
#Shibboleth attribute to use if a user logs in the first time.
shibboleth.preferred.language=
shibboleth.preferred.language.shib=
shibboleth.preferred.language.handler=
#Apply author role automatically to users according to the value of a Shibboleth attribute.
shibboleth.role.mapping.author.enable=false
shibboleth.role.mapping.author.shib=
shibboleth.role.mapping.author.contains=
# set the name of the Shibboleth attribute used to identify authorized users
shibboleth.defaultUID=defaultUID
#Define which Shibboleth attributes are mapped to OLAT user properties
#and how the attributes are mapped. Use a handler to transform the from
#Shibboleth delivered value to the desired form. If no handler is
......
......@@ -52,6 +52,10 @@ import org.springframework.test.util.ReflectionTestUtils;
*/
public class ShibbolethAttributesTest {
private static final String SHIB_UID_KEY = "shibUidKey";
private static final String SHIB_UID_VALUE = "shibUidValue";
private static final String SHIB_LANG_KEY = "shibLangKey";
private static final String SHIB_LANG_VALUE = "shibLangValue";
private static final String USER_EMAIL_KEY = UserConstants.EMAIL;
private static final String SHIB_EMAIL_KEY = "shibEmailKey";
private static final String SHIB_EMAIL_VALUE = "shibEmailValue";
......@@ -86,6 +90,8 @@ public class ShibbolethAttributesTest {
ReflectionTestUtils.setField(sut, "shibbolethModule", shibbolethModuleMock);
Map<String, String> shibbolethUserMapping = initUserMapping();
when(shibbolethModuleMock.getUserMapping()).thenReturn(shibbolethUserMapping);
when(shibbolethModuleMock.getUIDAttributeName()).thenReturn(SHIB_UID_KEY);
when(shibbolethModuleMock.getPreferredLanguageAttributeName()).thenReturn(SHIB_LANG_KEY);
// ShibbolethAttributeHandler.parse() does not modify the value
ReflectionTestUtils.setField(sut, "shibbolethAttributeHandlerFactory", shibbolethAttributeHandlerFactoryMock);
......@@ -95,13 +101,14 @@ public class ShibbolethAttributesTest {
ReflectionTestUtils.setField(sut, "differenceChecker", differenceCheckerMock);
Map<String, String> shibbolethKeysValues = initShibbolethMap();
sut.init(shibbolethKeysValues);
}
private Map<String, String> initShibbolethMap() {
Map<String, String> shibbolethMap = new HashMap<>();
shibbolethMap.put(SHIB_UID_KEY, SHIB_UID_VALUE);
shibbolethMap.put(SHIB_LANG_KEY, SHIB_LANG_VALUE);
shibbolethMap.put(SHIB_EMAIL_KEY, SHIB_EMAIL_VALUE);
shibbolethMap.put(SHIB_NAME_KEY, SHIB_NAME_VALUE);
shibbolethMap.put(SHIB_GENDER_KEY, SHIB_GENDER_VALUE);
......@@ -129,10 +136,24 @@ public class ShibbolethAttributesTest {
@Test
public void shouldParseValuesWhenInit() {
verify(shibbolethAttributeHandlerMock, times(3)).parse(anyString());
verify(shibbolethAttributeHandlerMock, times(5)).parse(anyString());
verify(shibbolethAttributeHandlerMock, times(1)).parse(isNull());
}
@Test
public void shouldReturnUniqueIdentifier() {
String uid = sut.getUID();
assertThat(uid).isEqualTo(SHIB_UID_VALUE);
}
@Test
public void shouldReturnPreferredLanguage() {
String lang = sut.getPreferredLanguage();
assertThat(lang).isEqualTo(SHIB_LANG_VALUE);
}
@Test
public void shouldReplaceValueByUserPropertyName() {
String newValue = "newValue";
......@@ -156,8 +177,7 @@ public class ShibbolethAttributesTest {
public void shouldNotParseValueWhenSet() {
sut.setValueForUserPropertyName(USER_NAME_KEY, "newValue");
// Only 3 times in the init().
verify(shibbolethAttributeHandlerMock, times(3)).parse(anyString());
verify(shibbolethAttributeHandlerMock, times(initUserMapping().size() + 1)).parse(anyString());
}
@Test
......
/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <hr>
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* This file has been modified by the OpenOLAT community. Changes are licensed
* under the Apache 2.0 license as the original file.
* <p>
*/
package org.olat.shibboleth.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
import java.io.UnsupportedEncodingException;
import org.junit.Test;
/**
* Description:<br>
* TODO: patrick Class Description for MultivalueAttributeTest
*
* <P>
* Initial Date: Oct 27, 2010 <br>
* @author patrick
*/
public class ShibbolethAttributeTest {
private static final String ATTR_NAME = "InstitutionalEmail";
private static final String LEARNER = "learner.ulrich@test.com";
private static final String AUTHOR ="author.ernest@mailbox.com";
private static final String ADMIN = "administrator.system@provider.com";
private static final String MULTIVALUE_SEP =";";
private static final String MULTIVALUE_VALID = createMultivalueString(LEARNER,AUTHOR,ADMIN);
private static final String MULTIVALUE_INVALID_EMPTY = "";
private static final String SINGLEVALUE_VALID = LEARNER;
@Test
public void multivalueAttributeWithEmails(){
ShibbolethAttribute multivalueAttrValid = new ShibbolethAttribute(ATTR_NAME, MULTIVALUE_VALID);
String[] values = multivalueAttrValid.getValues();
assertNotNull("contains three values", values);
assertEquals(3, values.length);
assertEquals(LEARNER, values[0]);
assertEquals(AUTHOR, values[1]);
assertEquals(ADMIN, values[2]);
assertEquals(LEARNER, multivalueAttrValid.getFirstValue());
}
@Test(expected=IllegalArgumentException.class)
public void multivalueAttributeWithEmptyString(){
new ShibbolethAttribute(ATTR_NAME, MULTIVALUE_INVALID_EMPTY);
}
@Test
public void singlevalueAttribute(){
ShibbolethAttribute singleAttrValid = new ShibbolethAttribute(ATTR_NAME, SINGLEVALUE_VALID);
String[] values = singleAttrValid.getValues();
assertNotNull("contains one value", values);
assertEquals(1, values.length);
assertEquals(LEARNER, values[0]);
assertEquals(LEARNER, singleAttrValid.getFirstValue());
}
@Test
public void multivalueAttributeWith8859String(){
try {
String rawRequestValue = new String(MULTIVALUE_VALID.getBytes("ISO-8859-1"));
ShibbolethAttribute fromRequestShibbAttribute = ShibbolethAttribute.createFromUserRequestValue(ATTR_NAME, rawRequestValue);
String[] values = fromRequestShibbAttribute.getValues();
assertNotNull("contains three values",values);
assertEquals(3, values.length);
assertEquals(LEARNER, values[0]);
assertEquals(AUTHOR, values[1]);
assertEquals(ADMIN, values[2]);
assertEquals(LEARNER, fromRequestShibbAttribute.getFirstValue());
} catch (UnsupportedEncodingException e) {
fail(e.toString());
}
}
private static String createMultivalueString(String... values) {
String retVal = values[0]+ MULTIVALUE_SEP;
for (int i = 1; i < values.length; i++) {
retVal = retVal + values[i] + MULTIVALUE_SEP;
}
return retVal;
}
}
......@@ -249,7 +249,6 @@ import org.junit.runners.Suite;
org.olat.core.commons.services.commentAndRating.UserRatingsDAOTest.class,
org.olat.course.auditing.UserNodeAuditManagerTest.class,
org.olat.shibboleth.handler.SpringShibbolethAttributeHandlerFactoryTest.class,
org.olat.shibboleth.util.ShibbolethAttributeTest.class,
org.olat.core.CoreSpringFactoryTest.class,
org.olat.portfolio.PortfolioModuleTest.class,
org.olat.portfolio.EPArtefactManagerTest.class,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment