diff --git a/pom.xml b/pom.xml index a5f4f273298..0001d3468ad 100755 --- a/pom.xml +++ b/pom.xml @@ -157,7 +157,7 @@ 1.4 1.3.1 3.1 - 4.3.2 + 4.4.4 2.4 2.6 3.3.2 @@ -215,6 +215,7 @@ 1.0.43 3.0.1 1.7.7 + 0.9.0-SNAPSHOT 3.2.9.RELEASE 1.3.1.RELEASE 3.1.3.RELEASE @@ -595,6 +596,11 @@ + + org.apereo.portal + soffit + ${soffit.version} + org.jvnet.jaxb2_commons jaxb2-basics-runtime diff --git a/uportal-war/pom.xml b/uportal-war/pom.xml index 4135d08b6a7..95a829bff7a 100644 --- a/uportal-war/pom.xml +++ b/uportal-war/pom.xml @@ -33,7 +33,7 @@ uPortal WAR The uPortal web application. - + @@ -234,6 +234,17 @@ oauth + + org.apereo.portal + soffit + + + commons-logging + commons-logging + + + + org.jvnet.jaxb2_commons jaxb2-basics-runtime diff --git a/uportal-war/src/main/java/org/jasig/portal/soffit/AuthorizationHeaderProvider.java b/uportal-war/src/main/java/org/jasig/portal/soffit/AuthorizationHeaderProvider.java new file mode 100644 index 00000000000..28f21b3896b --- /dev/null +++ b/uportal-war/src/main/java/org/jasig/portal/soffit/AuthorizationHeaderProvider.java @@ -0,0 +1,113 @@ +/** + * Licensed to Apereo under one or more contributor license + * agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. + * Apereo licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a + * copy of the License at the following location: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jasig.portal.soffit; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import javax.portlet.PortletSession; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; + +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; +import org.apereo.portal.soffit.Headers; +import org.apereo.portal.soffit.connector.AbstractHeaderProvider; +import org.apereo.portal.soffit.model.v1_0.Bearer; +import org.apereo.portal.soffit.service.BearerService; +import org.jasig.portal.groups.IEntityGroup; +import org.jasig.portal.groups.IGroupMember; +import org.jasig.portal.security.IPerson; +import org.jasig.portal.services.GroupService; +import org.jasig.services.persondir.IPersonAttributeDao; +import org.jasig.services.persondir.IPersonAttributes; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Prepares the standard HTTP Authorization header. This component is defined + * explicitly in the portlet context (not by annotation). + * + * @since 5.0 + * @author drewwills + */ +public class AuthorizationHeaderProvider extends AbstractHeaderProvider { + + @Autowired + private IPersonAttributeDao personAttributeDao; + + @Autowired + private BearerService bearerService; + + @Override + public Header createHeader(RenderRequest renderRequest, RenderResponse renderResponse) { + + // Username + final String username = getUsername(renderRequest); + + // Attributes + final Map> attributes = new HashMap<>(); + final IPersonAttributes person = personAttributeDao.getPerson(username); + if (person != null) { + for (Entry> y : person.getAttributes().entrySet()) { + final List values = new ArrayList<>(); + for (Object value : y.getValue()) { + if (value instanceof String) { + values.add((String) value); + } + } + attributes.put(y.getKey(), values); + } + } + logger.debug("Found the following user attributes for username='{}': {}", username, attributes); + + // Groups + final List groups = new ArrayList<>(); + final IGroupMember groupMember = GroupService.getGroupMember(username, IPerson.class); + if (groupMember != null) { + Set ancestors = groupMember.getAncestorGroups(); + for (IEntityGroup g : ancestors) { + groups.add(g.getName()); + } + } + logger.debug("Found the following group affiliations for username='{}': {}", username, groups); + + // Expiration of the Bearer token + final PortletSession portletSession = renderRequest.getPortletSession(); + final Date expires = new Date( + portletSession.getLastAccessedTime() + ((long) portletSession.getMaxInactiveInterval() * 1000L) + ); + + // Authorization header + final Bearer bearer = bearerService.createBearer(username, attributes, groups, expires); + final Header rslt = new BasicHeader( + Headers.AUTHORIZATION.getName(), + Headers.BEARER_TOKEN_PREFIX + bearer.getEncryptedToken()); + logger.debug("Produced the following Authorization header for username='{}': {}", username, rslt); + + return rslt; + + } + +} diff --git a/uportal-war/src/main/java/org/jasig/portal/soffit/DefinitionHeaderProvider.java b/uportal-war/src/main/java/org/jasig/portal/soffit/DefinitionHeaderProvider.java new file mode 100644 index 00000000000..a61cdde205c --- /dev/null +++ b/uportal-war/src/main/java/org/jasig/portal/soffit/DefinitionHeaderProvider.java @@ -0,0 +1,140 @@ +/** + * Licensed to Apereo under one or more contributor license + * agreements. See the NOTICE file distributed with this work + * for additional information regarding copyright ownership. + * Apereo licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a + * copy of the License at the following location: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.jasig.portal.soffit; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.servlet.http.HttpServletRequest; + +import org.apache.http.Header; +import org.apache.http.message.BasicHeader; +import org.apereo.portal.soffit.Headers; +import org.apereo.portal.soffit.connector.AbstractHeaderProvider; +import org.apereo.portal.soffit.model.v1_0.Definition; +import org.apereo.portal.soffit.service.DefinitionService; +import org.jasig.portal.i18n.ILocaleStore; +import org.jasig.portal.i18n.LocaleManager; +import org.jasig.portal.portlet.marketplace.IMarketplaceService; +import org.jasig.portal.portlet.marketplace.MarketplacePortletDefinition; +import org.jasig.portal.portlet.om.IPortletDefinition; +import org.jasig.portal.portlet.om.IPortletDefinitionParameter; +import org.jasig.portal.portlet.om.IPortletWindow; +import org.jasig.portal.portlet.om.IPortletWindowId; +import org.jasig.portal.portlet.om.PortletCategory; +import org.jasig.portal.portlet.registry.IPortletWindowRegistry; +import org.jasig.portal.security.IPerson; +import org.jasig.portal.security.IPersonManager; +import org.jasig.portal.url.IPortalRequestUtils; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Prepares the custom HTTP X-Soffit-Definition header. This component is + * defined explicitly in the portlet context (not by annotation). + * + * @since 5.0 + * @author drewwills + */ +public class DefinitionHeaderProvider extends AbstractHeaderProvider { + + @Autowired + private IPortalRequestUtils portalRequestUtils; + + @Autowired + private IPortletWindowRegistry portletWindowRegistry; + + @Autowired + private IMarketplaceService marketplaceService; + + @Autowired + private IPersonManager personManager; + + @Autowired + private ILocaleStore localeStore; + + @Autowired + private DefinitionService definitionService; + + @Override + public Header createHeader(RenderRequest renderRequest, RenderResponse renderResponse) { + + // Username + final String username = getUsername(renderRequest); + + // Obtain the MarketplacePortletDefinition for this soffit + final HttpServletRequest httpr = portalRequestUtils.getCurrentPortalRequest(); + final IPortletWindowId portletWindowId = portletWindowRegistry.getPortletWindowId(httpr, renderRequest.getWindowID()); + final IPortletWindow portletWindow = portletWindowRegistry.getPortletWindow(httpr, portletWindowId); + final IPortletDefinition pdef = portletWindow.getPortletEntity().getPortletDefinition(); + final MarketplacePortletDefinition mpdef = this.marketplaceService.getOrCreateMarketplacePortletDefinition(pdef); + + final IPerson user = personManager.getPerson(httpr); + final Locale locale = getUserLocale(user); + + // Title + final String title = mpdef.getTitle(locale.toString()); + + // FName + final String fname = mpdef.getFName(); + + // Description + final String description = mpdef.getDescription(locale.toString()); + + // Categories + List categories = new ArrayList<>(); + for (PortletCategory pc : mpdef.getCategories()) { + categories.add(pc.getName()); + } + + // Parameters + Map> parameters = new HashMap<>(); + for (IPortletDefinitionParameter param : mpdef.getParameters()) { + parameters.put(param.getName(), Collections.singletonList(param.getValue())); + } + + final Definition definition = definitionService.createDefinition(title, fname, + description, categories, parameters, username, getExpiration(renderRequest)); + final Header rslt = new BasicHeader( + Headers.DEFINITION.getName(), + definition.getEncryptedToken()); + logger.debug("Produced the following {} header for username='{}': {}", Headers.DEFINITION.getName(), username, rslt); + + return rslt; + } + + /* + * Implementation + */ + + private Locale getUserLocale(IPerson user) { + // get user locale + Locale[] locales = localeStore.getUserLocales(user); + LocaleManager localeManager = new LocaleManager(user, locales); + Locale rslt = localeManager.getLocales()[0]; + return rslt; + } + +} diff --git a/uportal-war/src/main/resources/properties/ehcache.xml b/uportal-war/src/main/resources/properties/ehcache.xml index 406dddddbe6..cb79e866c16 100644 --- a/uportal-war/src/main/resources/properties/ehcache.xml +++ b/uportal-war/src/main/resources/properties/ehcache.xml @@ -1997,4 +1997,14 @@ eternal="false" maxElementsInMemory="2000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="0" timeToLiveSeconds="300" memoryStoreEvictionPolicy="LRU" statistics="true" /> + + + diff --git a/uportal-war/src/main/resources/properties/portal.properties b/uportal-war/src/main/resources/properties/portal.properties index 1bf5880012e..1cb4920e258 100644 --- a/uportal-war/src/main/resources/properties/portal.properties +++ b/uportal-war/src/main/resources/properties/portal.properties @@ -768,4 +768,18 @@ org.jasig.portal.tincan-api.enabled=false # org.jasig.rest.interceptor.basic-auth.scorm-cloud-lrs.username=UsernameForProvider # org.jasig.rest.interceptor.basic-auth.scorm-cloud-lrs.password=PasswordForProvider +# Signature Key (Soffit) +# ---------------------- +# Uncomment and change the value of this setting. 'CHANGEME' is the default +# value; it may work (if both sides of the transaction have the default), but +# isn't secure and will produce a WARNING. +# +#org.apereo.portal.soffit.jwt.signatureKey=CHANGEME +# Encryption Password (Soffit) +# ---------------------------- +# Uncomment and change the value of this setting. 'CHANGEME' is the default +# value; it may work (if both sides of the transaction have the default), but +# isn't secure and will produce a WARNING. +# +#org.apereo.portal.soffit.jwt.encryptionPassword=CHANGEME diff --git a/uportal-war/src/main/webapp/WEB-INF/portlet.xml b/uportal-war/src/main/webapp/WEB-INF/portlet.xml index 0404f894848..83bbf428682 100644 --- a/uportal-war/src/main/webapp/WEB-INF/portlet.xml +++ b/uportal-war/src/main/webapp/WEB-INF/portlet.xml @@ -1105,10 +1105,37 @@ + + Soffit Connector + Soffit Connector + org.springframework.web.portlet.DispatcherPortlet + + contextConfigLocation + classpath:/org/apereo/portal/soffit/connector/connector-portlet.xml + + 0 + + text/html + VIEW + EDIT + HELP + + en + + Soffit Connector + + + + + org.apereo.portlet.soffit.connector.SoffitConnectorController.serviceUrl + + + + Allows the portlet to render a preferences edit UI during publishing config - + The P3P username attribute