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