VIVO-769 Merge branch 'feature/orcid' into develop
This commit is contained in:
parent
73fbe66534
commit
939950dcd4
25 changed files with 1551 additions and 1 deletions
|
@ -198,6 +198,31 @@ VitroConnection.DataSource.validationQuery = SELECT 1
|
||||||
# languages.selectableLocales = en_US, es_GO
|
# languages.selectableLocales = en_US, es_GO
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# ORCID INTEGRATION
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# orcid.clientId = 0000-0000-0000-000X
|
||||||
|
# orcid.clientPassword = 00000000-0000-0000-0000-000000000000
|
||||||
|
# orcid.webappBaseUrl = http://localhost:8080/vivo
|
||||||
|
# orcid.messageVersion = 1.0.23
|
||||||
|
# orcid.externalIdCommonName = VIVO Cornell Identifier
|
||||||
|
|
||||||
|
# ---- Setup for the sandbox ----
|
||||||
|
|
||||||
|
# orcid.publicApiBaseUrl = http://pub.sandbox-1.orcid.org/v1.1
|
||||||
|
# orcid.authorizedApiBaseUrl = http://api.sandbox-1.orcid.org/v1.1
|
||||||
|
# orcid.oauthAuthorizeUrl = http://sandbox-1.orcid.org/oauth/authorize
|
||||||
|
# orcid.oauthTokenUrl = http://api.sandbox-1.orcid.org/oauth/token
|
||||||
|
|
||||||
|
# ---- or for the mockorcid webapp ----
|
||||||
|
|
||||||
|
# orcid.publicApiBaseUrl = http://localhost:8080/mockorcid/mock/
|
||||||
|
# orcid.authorizedApiBaseUrl = http://localhost:8080/mockorcid/mock/
|
||||||
|
# orcid.oauthAuthorizeUrl = http://localhost:8080/mockorcid/mock/oauth/authorize
|
||||||
|
# orcid.oauthTokenUrl = http://localhost:8080/mockorcid/mock/oauth/token
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# OTHER OPTIONS
|
# OTHER OPTIONS
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
|
@ -191,3 +191,7 @@ xsdlib
|
||||||
None
|
None
|
||||||
----
|
----
|
||||||
Trippi
|
Trippi
|
||||||
|
|
||||||
|
ORCID API client
|
||||||
|
----------------
|
||||||
|
Written as part of the ORCID A&I grant. Source is available at https://github.com/j2blake/orcid-api-client
|
BIN
lib/orcid-api-client-0.1.jar
Normal file
BIN
lib/orcid-api-client-0.1.jar
Normal file
Binary file not shown.
|
@ -85,5 +85,7 @@ org.apache.commons.fileupload.servlet.FileCleanerCleanup
|
||||||
# and the PermissionRegistry must already be set up.
|
# and the PermissionRegistry must already be set up.
|
||||||
edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache$Setup
|
edu.cornell.mannlib.vitro.webapp.dao.jena.VClassGroupCache$Setup
|
||||||
|
|
||||||
|
edu.cornell.mannlib.vivo.orcid.OrcidContextSetup
|
||||||
|
|
||||||
# This should be near the end, because it will issue a warning if the connection to Solr times out.
|
# This should be near the end, because it will issue a warning if the connection to Solr times out.
|
||||||
edu.cornell.mannlib.vitro.webapp.servlet.setup.SolrSmokeTest
|
edu.cornell.mannlib.vitro.webapp.servlet.setup.SolrSmokeTest
|
||||||
|
|
|
@ -1400,6 +1400,14 @@
|
||||||
<url-pattern>/searchService/*</url-pattern>
|
<url-pattern>/searchService/*</url-pattern>
|
||||||
</servlet-mapping>
|
</servlet-mapping>
|
||||||
|
|
||||||
|
<servlet>
|
||||||
|
<servlet-name>OrcidIntegrationController</servlet-name>
|
||||||
|
<servlet-class>edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController</servlet-class>
|
||||||
|
</servlet>
|
||||||
|
<servlet-mapping>
|
||||||
|
<servlet-name>OrcidIntegrationController</servlet-name>
|
||||||
|
<url-pattern>/orcid/*</url-pattern>
|
||||||
|
</servlet-mapping>
|
||||||
|
|
||||||
<!-- ==================== tag libraries ============================== -->
|
<!-- ==================== tag libraries ============================== -->
|
||||||
<jsp-config>
|
<jsp-config>
|
||||||
|
|
148
productMods/templates/freemarker/body/orcid/orcidConfirm.ftl
Normal file
148
productMods/templates/freemarker/body/orcid/orcidConfirm.ftl
Normal file
|
@ -0,0 +1,148 @@
|
||||||
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
|
<#--
|
||||||
|
The body map contains the orcidInfo structure, which is set up like this:
|
||||||
|
|
||||||
|
orcidInfo
|
||||||
|
progress - a string set to one of these values: START, DENIED_AUTHENTICATE,
|
||||||
|
FAILED_AUTHENTICATE, GOT_PROFILE, ID_ALREADY_PRESENT, DENIED_ID,
|
||||||
|
FAILED_ID, ADDED_ID
|
||||||
|
individualUri - the URI of the person
|
||||||
|
profilePage - the URL of the individual's profile page
|
||||||
|
orcid - the confirmed ORCID (just xxxx-xxxx-xxxx-xxxx),
|
||||||
|
or the empty string.
|
||||||
|
orcidUri - the confirmed ORCID (full URI), or the empty string.
|
||||||
|
externalIds - empty if we haven't read their profile. Otherwise, a sequence
|
||||||
|
of maps, one for each external ID in their profile. These
|
||||||
|
might include SCOPUS ID, etc. Each map looks like this:
|
||||||
|
commonName - e.g., "VIVO Cornell"
|
||||||
|
reference - e.g., their VIVO localname
|
||||||
|
uri - e.g., their VIVO URI
|
||||||
|
hasVivoId - true, if we have read the profile and they already have
|
||||||
|
their VIVO URI as an external ID. False otherwise.
|
||||||
|
existingOrcids - A sequence of the ORCIDs (full URI) that we already associate
|
||||||
|
with this individual.
|
||||||
|
progressUrl - The URL to go to, that will continue this process. If the
|
||||||
|
process is complete or has failed, this is empty.
|
||||||
|
|
||||||
|
-->
|
||||||
|
|
||||||
|
<style TYPE="text/css">
|
||||||
|
#orcid-offer .step {
|
||||||
|
background-color: #F7F7F4;
|
||||||
|
border: 1px solid #cdcdcd;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 0 1em 1em;
|
||||||
|
margin: 12px
|
||||||
|
}
|
||||||
|
|
||||||
|
#orcid-offer .links {
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#orcid-offer ul {
|
||||||
|
list-style: disc inside;
|
||||||
|
line-height: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#orcid-offer ul.inner {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#orcid-offer li {
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#orcid-offer .dimmed {
|
||||||
|
opacity:0.35;
|
||||||
|
filter:alpha(opacity=35);
|
||||||
|
}
|
||||||
|
span.completed {
|
||||||
|
color: #9a9a9a;
|
||||||
|
font-size: .8em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<#assign orcidTextOne = "add an" />
|
||||||
|
<#assign orcidTextTwo = "Adding" />
|
||||||
|
<#if (orcidInfo.existingOrcids?size > 0) >
|
||||||
|
<#assign orcidTextOne = "confirm your" />
|
||||||
|
<#assign orcidTextTwo = "Confirming" />
|
||||||
|
</#if>
|
||||||
|
<#assign step2dimmed = (["START", "FAILED_AUTHENTICATE", "DENIED_AUTHENTICATE"]?seq_contains(orcidInfo.progress))?string("dimmed", "") />
|
||||||
|
<#assign continueAppears = (["START", "GOT_PROFILE"]?seq_contains(orcidInfo.progress))/>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
|
||||||
|
<section id="orcid-offer" role="region">
|
||||||
|
<h2>Do you want to ${orcidTextOne} ORCID Identification?</h2>
|
||||||
|
|
||||||
|
<div class="step">
|
||||||
|
<#if "START" == orcidInfo.progress>
|
||||||
|
<h2>Step 1: ${orcidTextTwo} your ORCID ID</h2>
|
||||||
|
<ul>
|
||||||
|
<li>VIVO redirects you to ORCID's web site.</li>
|
||||||
|
<li>You log in to your ORCID account.
|
||||||
|
<ul class="inner"><li>If you don't have an account, you can create one.</li></ul>
|
||||||
|
</li>
|
||||||
|
<li>You tell ORCID that VIVO may read your ORCID Record. (one-time permission)</li>
|
||||||
|
<li>VIVO reads your ORCID Record.</li>
|
||||||
|
<li>VIVO notes that your ORCID ID is confirmed.</li>
|
||||||
|
</ul>
|
||||||
|
<#elseif "DENIED_AUTHENTICATE" == orcidInfo.progress>
|
||||||
|
<h2>Step 1: ${orcidTextTwo} your ORCID ID</h2>
|
||||||
|
<p>You denied VIVO's request to read your ORCID profile.</p>
|
||||||
|
<p>Confirmation can't continue.</p>
|
||||||
|
<#elseif "FAILED_AUTHENTICATE" == orcidInfo.progress>
|
||||||
|
<h2>Step 1: ${orcidTextTwo} your ORCID ID</h2>
|
||||||
|
<p>VIVO failed to read your ORCID profile.</p>
|
||||||
|
<p>Confirmation can't continue.</p>
|
||||||
|
<#else>
|
||||||
|
<h2>Step 1: ${orcidTextTwo} your ORCID ID <span class="completed">(step completed)</span></h2>
|
||||||
|
<p>Your ORCID ID is confirmed as ${orcidInfo.orcid}</p>
|
||||||
|
<p><a href="${orcidInfo.orcidUri}" target="_blank">View your ORCID profile page.</a></p>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="step ${step2dimmed}">
|
||||||
|
<#if "ID_ALREADY_PRESENT" == orcidInfo.progress>
|
||||||
|
<h2>Step 2 (recommended): Linking your ORCID Record to VIVO <span class="completed">(step completed)</span></h2>
|
||||||
|
<p>Your ORCID profile already includes a link to VIVO.</p>
|
||||||
|
<#elseif "DENIED_ID" == orcidInfo.progress>
|
||||||
|
<h2>Step 2 (recommended): Linking your ORCID Record to VIVO</h2>
|
||||||
|
<p>You denied VIVO's request to add an External ID to your ORCID profile.</p>
|
||||||
|
<p>Linking can't continue.</p>
|
||||||
|
<#elseif "FAILED_ID" == orcidInfo.progress>
|
||||||
|
<h2>Step 2 (recommended): Linking your ORCID Record to VIVO</h2>
|
||||||
|
<p>VIVO failed to add an External ID to your ORCID profile.</p>
|
||||||
|
<p>Linking can't continue.</p>
|
||||||
|
<#elseif "ADDED_ID" == orcidInfo.progress>
|
||||||
|
<h2>Step 2 (recommended): Linking your ORCID Record to VIVO <span class="completed">(step completed)</span></h2>
|
||||||
|
<p>Your ORCID profile is linked to VIVO</p>
|
||||||
|
<p><a href="${orcidInfo.orcidUri}" target="_blank">View your ORCID profile page.</a></p>
|
||||||
|
<#else>
|
||||||
|
<h2>Step 2 (recommended): Linking your ORCID Record to VIVO</h2>
|
||||||
|
<ul>
|
||||||
|
<li>VIVO redirects you to ORCID's web site</li>
|
||||||
|
<li>You tell ORCID that VIVO may add an "external ID" to your ORCID Record. (one-time permission)</li>
|
||||||
|
<li>VIVO adds the external ID.</li>
|
||||||
|
</ul>
|
||||||
|
</#if>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class=links>
|
||||||
|
<form method="GET" action="${orcidInfo.progressUrl}">
|
||||||
|
<p>
|
||||||
|
<#if continueAppears>
|
||||||
|
<input type="submit" name="submit" value="Continue <#if "START" == orcidInfo.progress>Step 1<#else>Step 2</#if>" class="submit"/>
|
||||||
|
or
|
||||||
|
</#if>
|
||||||
|
<a class="cancel" href="${orcidInfo.profilePage}">Return to your VIVO profile page</a>
|
||||||
|
</p>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</div>
|
|
@ -0,0 +1,34 @@
|
||||||
|
<#-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||||
|
|
||||||
|
<#--
|
||||||
|
If authorized to confirm ORCID IDs, add the function that will replace the add link.
|
||||||
|
The OrcidIdDataGetter is attached to this template; it sets the orcidInfo structure,
|
||||||
|
which looks like this:
|
||||||
|
|
||||||
|
orcidInfo = map {
|
||||||
|
authorizedToConfirm: boolean
|
||||||
|
orcidUrl: link to the orcid controller
|
||||||
|
orcids: map of String to boolean [
|
||||||
|
orcid: String (full URI)
|
||||||
|
confirmed: boolean
|
||||||
|
]
|
||||||
|
}
|
||||||
|
-->
|
||||||
|
<#assign confirmThis = "" />
|
||||||
|
<#if orcidInfo??>
|
||||||
|
|
||||||
|
<#list orcidInfo.orcids?keys as key>
|
||||||
|
<#if "no" == orcidInfo.orcids[key]?string("yes","no") >
|
||||||
|
<#assign confirmThis = "Confirm the ID" />
|
||||||
|
</#if>
|
||||||
|
</#list>
|
||||||
|
|
||||||
|
<#if orcidInfo.authorizedToConfirm>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function(){
|
||||||
|
$('#orcidId a.add-orcidId').replaceWith("<a class='add-orcidId' style='padding-left:20px' href='${orcidInfo.orcidUrl}'><#if orcidInfo.orcids?size == 0>Add an ID<#else>${confirmThis}</#if></a> ");
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
|
|
|
@ -9,6 +9,13 @@
|
||||||
|
|
||||||
<#macro showStatement statement>
|
<#macro showStatement statement>
|
||||||
<a href="${statement.value!}" title="ORCID iD" target="_blank">${statement.value!"ORCID iD not found"}</a>
|
<a href="${statement.value!}" title="ORCID iD" target="_blank">${statement.value!"ORCID iD not found"}</a>
|
||||||
|
<#if orcidInfo??>
|
||||||
|
<#if (orcidInfo.orcids[statement.value])!false>
|
||||||
|
<span style="color:#FF7700">(confirmed)</span>
|
||||||
|
<#else>
|
||||||
|
<span style="color:#FF7700">(pending confirmation)</span>
|
||||||
|
</#if>
|
||||||
|
</#if>
|
||||||
</#macro>
|
</#macro>
|
||||||
|
|
||||||
|
|
||||||
|
|
2
rdf/abox/filegraph/validation.n3
Normal file
2
rdf/abox/filegraph/validation.n3
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<http://orcid.org/1234-1231-1231-3333>
|
||||||
|
<http://vivoweb.org/ontology/core#validatedOrcidId> <http://vivo.mydomain.edu/individual/n4571> .
|
14
rdf/display/everytime/orcidInterfaceDataGetters.n3
Normal file
14
rdf/display/everytime/orcidInterfaceDataGetters.n3
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# $This file is distributed under the terms of the license in /doc/license.txt$
|
||||||
|
|
||||||
|
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||||
|
@prefix display: <http://vitro.mannlib.cornell.edu/ontologies/display/1.1#> .
|
||||||
|
|
||||||
|
#
|
||||||
|
# datagetter to fetch ORCID info for the person.
|
||||||
|
#
|
||||||
|
|
||||||
|
<freemarker:individual-orcidInterface.ftl> display:hasDataGetter display:orcidIdDataGetter .
|
||||||
|
|
||||||
|
display:orcidIdDataGetter
|
||||||
|
a <java:edu.cornell.mannlib.vivo.orcid.OrcidIdDataGetter> .
|
||||||
|
|
10
rdf/tbox/filegraph/orcid-interface.n3
Normal file
10
rdf/tbox/filegraph/orcid-interface.n3
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||||
|
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
||||||
|
@prefix core: <http://vivoweb.org/ontology/core#> .
|
||||||
|
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
||||||
|
|
||||||
|
core:confirmedOrcidId
|
||||||
|
a owl:ObjectProperty ;
|
||||||
|
rdfs:label "Orcid ID confirmation"@en-US ;
|
||||||
|
rdfs:comment "Indicates that the Orcid ID has been confirmed by this Person" ;
|
||||||
|
rdfs:range foaf:Person .
|
107
src/edu/cornell/mannlib/vivo/orcid/OrcidContextSetup.java
Normal file
107
src/edu/cornell/mannlib/vivo/orcid/OrcidContextSetup.java
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.AUTHORIZED_API_BASE_URL;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.CALLBACK_PATH;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.CLIENT_ID;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.CLIENT_SECRET;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.MESSAGE_VERSION;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.OAUTH_AUTHORIZE_URL;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.OAUTH_TOKEN_URL;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.PUBLIC_API_BASE_URL;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting.WEBAPP_BASE_URL;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.DEFAULT_EXTERNAL_ID_COMMON_NAME;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.PROPERTY_EXTERNAL_ID_COMMON_NAME;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
|
|
||||||
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext;
|
||||||
|
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.config.ConfigurationProperties;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.startup.StartupStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup for the ORCID interface.
|
||||||
|
*
|
||||||
|
* Note that the property for CLIENT_SECRET is "orcid.clientPassword". Since it
|
||||||
|
* ends in "password", it will not be displayed on the ShowConfiguration page.
|
||||||
|
*
|
||||||
|
* The CALLBACK_PATH is hardcoded. It is relative to the WEBAPP_BASE_URL, so it
|
||||||
|
* won't change.
|
||||||
|
*/
|
||||||
|
public class OrcidContextSetup implements ServletContextListener {
|
||||||
|
private static final Log log = LogFactory.getLog(OrcidContextSetup.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextInitialized(ServletContextEvent sce) {
|
||||||
|
ServletContext ctx = sce.getServletContext();
|
||||||
|
ConfigurationProperties props = ConfigurationProperties.getBean(ctx);
|
||||||
|
StartupStatus ss = StartupStatus.getBean(ctx);
|
||||||
|
|
||||||
|
if (props.getProperty("orcid.clientId", "").isEmpty()) {
|
||||||
|
ss.info(this, "ORCID Integration is not configured.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeOrcidClientContext(props, ss);
|
||||||
|
|
||||||
|
checkForCommonNameProperty(props, ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeOrcidClientContext(ConfigurationProperties props,
|
||||||
|
StartupStatus ss) {
|
||||||
|
try {
|
||||||
|
Map<Setting, String> settings = new EnumMap<>(Setting.class);
|
||||||
|
settings.put(CLIENT_ID, props.getProperty("orcid.clientId"));
|
||||||
|
settings.put(CLIENT_SECRET,
|
||||||
|
props.getProperty("orcid.clientPassword"));
|
||||||
|
settings.put(PUBLIC_API_BASE_URL,
|
||||||
|
props.getProperty("orcid.publicApiBaseUrl"));
|
||||||
|
settings.put(AUTHORIZED_API_BASE_URL,
|
||||||
|
props.getProperty("orcid.authorizedApiBaseUrl"));
|
||||||
|
settings.put(OAUTH_AUTHORIZE_URL,
|
||||||
|
props.getProperty("orcid.oauthAuthorizeUrl"));
|
||||||
|
settings.put(OAUTH_TOKEN_URL,
|
||||||
|
props.getProperty("orcid.oauthTokenUrl"));
|
||||||
|
settings.put(MESSAGE_VERSION,
|
||||||
|
props.getProperty("orcid.messageVersion"));
|
||||||
|
settings.put(WEBAPP_BASE_URL,
|
||||||
|
props.getProperty("orcid.webappBaseUrl"));
|
||||||
|
settings.put(CALLBACK_PATH, "orcid/callback");
|
||||||
|
|
||||||
|
OrcidClientContext.initialize(settings);
|
||||||
|
ss.info(this, "Context is: " + OrcidClientContext.getInstance());
|
||||||
|
|
||||||
|
} catch (OrcidClientException e) {
|
||||||
|
ss.warning(this, "Failed to initialize OrcidClientContent", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkForCommonNameProperty(ConfigurationProperties props,
|
||||||
|
StartupStatus ss) {
|
||||||
|
if (StringUtils.isBlank(props
|
||||||
|
.getProperty(PROPERTY_EXTERNAL_ID_COMMON_NAME))) {
|
||||||
|
ss.warning(this, "'" + PROPERTY_EXTERNAL_ID_COMMON_NAME
|
||||||
|
+ "' is not set. " + "Using default value of '"
|
||||||
|
+ DEFAULT_EXTERNAL_ID_COMMON_NAME + "'");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contextDestroyed(ServletContextEvent sce) {
|
||||||
|
// Nothing to tear down.
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
217
src/edu/cornell/mannlib/vivo/orcid/OrcidIdDataGetter.java
Normal file
217
src/edu/cornell/mannlib/vivo/orcid/OrcidIdDataGetter.java
Normal file
|
@ -0,0 +1,217 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import com.hp.hpl.jena.query.QuerySolution;
|
||||||
|
import com.hp.hpl.jena.query.ResultSet;
|
||||||
|
import com.hp.hpl.jena.rdf.model.RDFNode;
|
||||||
|
import com.hp.hpl.jena.rdf.model.Resource;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.HasAssociatedIndividual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.HasProfile;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.HasProxyEditingRights;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.IsRootUser;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.SparqlQueryRunner.QueryParser;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.utils.dataGetter.DataGetter;
|
||||||
|
import edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This data getter should be assigned to the template that renders the list
|
||||||
|
* view for ORCID IDs.
|
||||||
|
*
|
||||||
|
* Find out whether the user is authorized to confirm the ORCID IDs on this
|
||||||
|
* page. Find the list of ORCID IDs, and whether each has already been
|
||||||
|
* confirmed.
|
||||||
|
*
|
||||||
|
* The information is stored in the values map like this:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* orcidInfo = map {
|
||||||
|
* authorizedToConfirm: boolean
|
||||||
|
* orcids: map of String to boolean [
|
||||||
|
* orcid: String
|
||||||
|
* confirm: boolean
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class OrcidIdDataGetter implements DataGetter {
|
||||||
|
private static final Log log = LogFactory.getLog(OrcidIdDataGetter.class);
|
||||||
|
|
||||||
|
private static final Map<String, Object> EMPTY_RESULT = Collections
|
||||||
|
.emptyMap();
|
||||||
|
public static final String ORCID_ID = "http://vivoweb.org/ontology/core#orcidId";
|
||||||
|
public static final String ORCID_IS_CONFIRMED = "http://vivoweb.org/ontology/core#confirmedOrcidId";
|
||||||
|
private static final String QUERY_TEMPLATE = "SELECT ?orcid ?confirmed \n"
|
||||||
|
+ "WHERE { \n" //
|
||||||
|
+ " <%s> <%s> ?orcid . \n" //
|
||||||
|
+ " OPTIONAL { \n" //
|
||||||
|
+ " ?orcid <%s> ?confirmed . \n" //
|
||||||
|
+ " } \n" //
|
||||||
|
+ "}\n";
|
||||||
|
|
||||||
|
private final VitroRequest vreq;
|
||||||
|
|
||||||
|
public OrcidIdDataGetter(VitroRequest vreq) {
|
||||||
|
this.vreq = vreq;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> getData(Map<String, Object> valueMap) {
|
||||||
|
try {
|
||||||
|
String individualUri = findIndividualUri(valueMap);
|
||||||
|
if (individualUri == null) {
|
||||||
|
return EMPTY_RESULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isAuthorizedToConfirm = figureIsAuthorizedtoConfirm(individualUri);
|
||||||
|
List<OrcidInfo> orcids = runSparqlQuery(individualUri);
|
||||||
|
return buildMap(isAuthorizedToConfirm, orcids, individualUri);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to get orcID information", e);
|
||||||
|
return EMPTY_RESULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findIndividualUri(Map<String, Object> valueMap) {
|
||||||
|
try {
|
||||||
|
String uri = (String) valueMap.get("individualURI");
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
log.warn("valueMap has no individualURI. Keys are: "
|
||||||
|
+ valueMap.keySet());
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return uri;
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.debug("has a problem finding the individualURI", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* You are authorized to confirm an orcId only if you are a self-editor or
|
||||||
|
* root.
|
||||||
|
*/
|
||||||
|
private boolean figureIsAuthorizedtoConfirm(String individualUri) {
|
||||||
|
IdentifierBundle ids = RequestIdentifiers.getIdBundleForRequest(vreq);
|
||||||
|
boolean isSelfEditor = HasProfile.getProfileUris(ids).contains(
|
||||||
|
individualUri);
|
||||||
|
boolean isProxyEditor = HasProxyEditingRights.getProxiedPageUris(ids)
|
||||||
|
.contains(individualUri);
|
||||||
|
boolean isRoot = IsRootUser.isRootUser(ids);
|
||||||
|
return isRoot || isProxyEditor || isSelfEditor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<OrcidInfo> runSparqlQuery(String individualUri) {
|
||||||
|
String queryStr = String.format(QUERY_TEMPLATE, individualUri,
|
||||||
|
ORCID_ID, ORCID_IS_CONFIRMED);
|
||||||
|
SparqlQueryRunner runner = new SparqlQueryRunner(vreq.getJenaOntModel());
|
||||||
|
return runner.executeSelect(new OrcidResultParser(), queryStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> buildMap(boolean isAuthorizedToConfirm,
|
||||||
|
List<OrcidInfo> orcids, String individualUri) {
|
||||||
|
Map<String, Boolean> confirmationMap = new HashMap<>();
|
||||||
|
for (OrcidInfo oInfo : orcids) {
|
||||||
|
confirmationMap.put(oInfo.getOrcid(), oInfo.isConfirmed());
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> orcidInfoMap = new HashMap<>();
|
||||||
|
orcidInfoMap.put("authorizedToConfirm", isAuthorizedToConfirm);
|
||||||
|
orcidInfoMap.put("orcidUrl", UrlBuilder.getUrl(
|
||||||
|
OrcidIntegrationController.PATH_DEFAULT, "individualUri",
|
||||||
|
individualUri));
|
||||||
|
orcidInfoMap.put("orcids", confirmationMap);
|
||||||
|
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("orcidInfo", orcidInfoMap);
|
||||||
|
|
||||||
|
log.debug("Returning these values:" + map);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Helper classes
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the results of the SPARQL query.
|
||||||
|
*/
|
||||||
|
private static class OrcidResultParser extends QueryParser<List<OrcidInfo>> {
|
||||||
|
@Override
|
||||||
|
protected List<OrcidInfo> defaultValue() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected List<OrcidInfo> parseResults(String queryStr,
|
||||||
|
ResultSet results) {
|
||||||
|
List<OrcidInfo> orcids = new ArrayList<>();
|
||||||
|
|
||||||
|
while (results.hasNext()) {
|
||||||
|
try {
|
||||||
|
QuerySolution solution = results.next();
|
||||||
|
Resource orcid = solution.getResource("orcid");
|
||||||
|
RDFNode cNode = solution.get("confirmed");
|
||||||
|
log.debug("Result is orcid=" + orcid + ", confirmed="
|
||||||
|
+ cNode);
|
||||||
|
|
||||||
|
if (orcid != null && orcid.isURIResource()) {
|
||||||
|
boolean confirmed = (cNode != null);
|
||||||
|
orcids.add(new OrcidInfo(orcid.getURI(), confirmed));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to parse the query result: " + queryStr, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return orcids;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bean to hold info for each ORCID.
|
||||||
|
*/
|
||||||
|
static class OrcidInfo {
|
||||||
|
private final String orcid;
|
||||||
|
private final boolean confirmed;
|
||||||
|
|
||||||
|
public OrcidInfo(String orcid, boolean confirmed) {
|
||||||
|
this.orcid = orcid;
|
||||||
|
this.confirmed = confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrcid() {
|
||||||
|
return orcid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConfirmed() {
|
||||||
|
return confirmed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "OrcidInfo[orcid=" + orcid + ", confirmed=" + confirmed
|
||||||
|
+ "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary.OWL_THING;
|
||||||
|
import static edu.cornell.mannlib.vitro.webapp.dao.VitroVocabulary.RDF_TYPE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.OrcidIdDataGetter.ORCID_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.OrcidIdDataGetter.ORCID_IS_CONFIRMED;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.TEMPLATE_CONFIRM;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationManager;
|
||||||
|
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidMessage;
|
||||||
|
import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatementImpl;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.IndividualDao;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.dao.ObjectPropertyStatementDao;
|
||||||
|
import edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some utility methods for the handlers.
|
||||||
|
*/
|
||||||
|
public abstract class OrcidAbstractHandler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidAbstractHandler.class);
|
||||||
|
|
||||||
|
protected final VitroRequest vreq;
|
||||||
|
protected final OrcidClientContext occ;
|
||||||
|
protected final AuthorizationManager auth;
|
||||||
|
protected final OrcidConfirmationState state;
|
||||||
|
protected final UserAccount currentUser;
|
||||||
|
|
||||||
|
protected OrcidAbstractHandler(VitroRequest vreq) {
|
||||||
|
this.vreq = vreq;
|
||||||
|
this.occ = OrcidClientContext.getInstance();
|
||||||
|
this.auth = this.occ.getAuthorizationManager(vreq);
|
||||||
|
this.state = OrcidConfirmationState.fetch(vreq);
|
||||||
|
this.currentUser = LoginStatusBean.getCurrentUser(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Individual findIndividual() {
|
||||||
|
String uri = state.getIndividualUri();
|
||||||
|
try {
|
||||||
|
IndividualDao iDao = vreq.getWebappDaoFactory().getIndividualDao();
|
||||||
|
Individual individual = iDao.getIndividualByURI(uri);
|
||||||
|
if (individual == null) {
|
||||||
|
throw new IllegalStateException("Individual URI not valid: '"
|
||||||
|
+ uri + "'");
|
||||||
|
}
|
||||||
|
return individual;
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new IllegalStateException("Individual URI not valid: '" + uri
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void recordConfirmation() {
|
||||||
|
String individualUri = state.getIndividualUri();
|
||||||
|
String orcidUri = state.getOrcidUri();
|
||||||
|
log.debug("Recording confirmation of ORCID '" + orcidUri + "' on '"
|
||||||
|
+ individualUri + "'");
|
||||||
|
ObjectPropertyStatement ops1 = new ObjectPropertyStatementImpl(
|
||||||
|
individualUri, ORCID_ID, orcidUri);
|
||||||
|
ObjectPropertyStatement ops2 = new ObjectPropertyStatementImpl(
|
||||||
|
orcidUri, RDF_TYPE, OWL_THING);
|
||||||
|
ObjectPropertyStatement ops3 = new ObjectPropertyStatementImpl(
|
||||||
|
orcidUri, ORCID_IS_CONFIRMED, individualUri);
|
||||||
|
|
||||||
|
ObjectPropertyStatementDao opsd = vreq.getWebappDaoFactory()
|
||||||
|
.getObjectPropertyStatementDao();
|
||||||
|
opsd.insertNewObjectPropertyStatement(ops1);
|
||||||
|
opsd.insertNewObjectPropertyStatement(ops2);
|
||||||
|
opsd.insertNewObjectPropertyStatement(ops3);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String cornellNetId() {
|
||||||
|
if (currentUser == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String externalId = currentUser.getExternalAuthId();
|
||||||
|
if (externalId == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (externalId.trim().isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return externalId;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResponseValues show500InternalServerError(String message) {
|
||||||
|
log.error("Problem with ORCID request: " + message);
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("title", "500 Internal Server Error");
|
||||||
|
map.put("errorMessage", message);
|
||||||
|
return new TemplateResponseValues("error-titled.ftl", map,
|
||||||
|
SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResponseValues showConfirmationPage(Progress p,
|
||||||
|
OrcidMessage... messages) {
|
||||||
|
state.progress(p, messages);
|
||||||
|
return showConfirmationPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected ResponseValues showConfirmationPage() {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("orcidInfo", state.toMap());
|
||||||
|
return new TemplateResponseValues(TEMPLATE_CONFIRM, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.orcidclient.actions.ApiAction.ADD_EXTERNAL_ID;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.orcidmessage.Visibility.PUBLIC;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.ADDED_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.DENIED_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.FAILED_ID;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
import edu.cornell.mannlib.orcidclient.actions.AddExternalIdAction;
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationStatus;
|
||||||
|
import edu.cornell.mannlib.orcidclient.beans.ExternalId;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidMessage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We should now be logged in to ORCID and authorized to add an external ID.
|
||||||
|
*/
|
||||||
|
public class OrcidAddExternalIdHandler extends OrcidAbstractHandler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidAddExternalIdHandler.class);
|
||||||
|
|
||||||
|
private AuthorizationStatus status;
|
||||||
|
private OrcidMessage profile;
|
||||||
|
|
||||||
|
protected OrcidAddExternalIdHandler(VitroRequest vreq) {
|
||||||
|
super(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseValues exec() throws OrcidClientException {
|
||||||
|
status = auth.getAuthorizationStatus(ADD_EXTERNAL_ID);
|
||||||
|
if (status.isSuccess()) {
|
||||||
|
addVivoId();
|
||||||
|
return showConfirmationPage(ADDED_ID, profile);
|
||||||
|
} else if (status.isDenied()) {
|
||||||
|
return showConfirmationPage(DENIED_ID);
|
||||||
|
} else {
|
||||||
|
return showConfirmationPage(FAILED_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addVivoId() throws OrcidClientException {
|
||||||
|
Individual individual = findIndividual();
|
||||||
|
ExternalId externalId = new ExternalId().setCommonName("VIVO Cornell")
|
||||||
|
.setReference(individual.getLocalName())
|
||||||
|
.setUrl(individual.getURI()).setVisibility(PUBLIC);
|
||||||
|
|
||||||
|
log.debug("Adding external VIVO ID");
|
||||||
|
profile = new AddExternalIdAction().execute(externalId,
|
||||||
|
status.getAccessToken());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.orcidclient.actions.ApiAction.AUTHENTICATE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.DENIED_AUTHENTICATE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.FAILED_AUTHENTICATE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.PATH_READ_PROFILE;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We offered the confirmation screen, and they decided to go ahead. Get
|
||||||
|
* authorization to authenticate them.
|
||||||
|
*
|
||||||
|
* We can't assume that they haven't been here before, so they might already
|
||||||
|
* have authorized, or denied authorization.
|
||||||
|
*/
|
||||||
|
public class OrcidAuthAuthenticateHandler extends OrcidAbstractHandler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidAuthAuthenticateHandler.class);
|
||||||
|
|
||||||
|
private AuthorizationStatus status;
|
||||||
|
|
||||||
|
public OrcidAuthAuthenticateHandler(VitroRequest vreq) {
|
||||||
|
super(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseValues exec() throws URISyntaxException,
|
||||||
|
OrcidClientException {
|
||||||
|
status = auth.getAuthorizationStatus(AUTHENTICATE);
|
||||||
|
if (status.isNone()) {
|
||||||
|
return seekAuthorizationForAuthenticate();
|
||||||
|
} else if (status.isSuccess()) {
|
||||||
|
return redirectToReadProfile();
|
||||||
|
} else if (status.isDenied()) {
|
||||||
|
return showConfirmationPage(DENIED_AUTHENTICATE);
|
||||||
|
} else {
|
||||||
|
return showConfirmationPage(FAILED_AUTHENTICATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseValues seekAuthorizationForAuthenticate()
|
||||||
|
throws OrcidClientException, URISyntaxException {
|
||||||
|
log.debug("Seeking authorization to authenticate.");
|
||||||
|
String returnUrl = occ.resolvePathWithWebapp(PATH_READ_PROFILE);
|
||||||
|
String seekUrl = auth.seekAuthorization(AUTHENTICATE, returnUrl);
|
||||||
|
return new RedirectResponseValues(seekUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseValues redirectToReadProfile() throws URISyntaxException {
|
||||||
|
log.debug("Already authorized to authenticate.");
|
||||||
|
return new RedirectResponseValues(
|
||||||
|
occ.resolvePathWithWebapp(PATH_READ_PROFILE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.orcidclient.actions.ApiAction.ADD_EXTERNAL_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.DENIED_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.FAILED_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.PATH_ADD_EXTERNAL_ID;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationStatus;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.RedirectResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We offered to add external IDs and they decided to go ahead. Get
|
||||||
|
* authorization.
|
||||||
|
*
|
||||||
|
* We can't assume that they haven't been here before, so they might already
|
||||||
|
* have authorized, or denied authorization.
|
||||||
|
*/
|
||||||
|
public class OrcidAuthExternalIdsHandler extends OrcidAbstractHandler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidAuthExternalIdsHandler.class);
|
||||||
|
|
||||||
|
private AuthorizationStatus status;
|
||||||
|
|
||||||
|
public OrcidAuthExternalIdsHandler(VitroRequest vreq) {
|
||||||
|
super(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseValues exec() throws URISyntaxException,
|
||||||
|
OrcidClientException {
|
||||||
|
status = auth.getAuthorizationStatus(ADD_EXTERNAL_ID);
|
||||||
|
if (status.isNone()) {
|
||||||
|
return seekAuthorizationForExternalId();
|
||||||
|
} else if (status.isSuccess()) {
|
||||||
|
return redirectToAddExternalId();
|
||||||
|
} else if (status.isDenied()) {
|
||||||
|
return showConfirmationPage(DENIED_ID);
|
||||||
|
} else {
|
||||||
|
return showConfirmationPage(FAILED_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseValues seekAuthorizationForExternalId()
|
||||||
|
throws OrcidClientException, URISyntaxException {
|
||||||
|
log.debug("Seeking authorization to add external ID");
|
||||||
|
String returnUrl = occ.resolvePathWithWebapp(PATH_ADD_EXTERNAL_ID);
|
||||||
|
String seekUrl = auth.seekAuthorization(ADD_EXTERNAL_ID, returnUrl);
|
||||||
|
return new RedirectResponseValues(seekUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseValues redirectToAddExternalId() throws URISyntaxException {
|
||||||
|
log.debug("Already authorized to add external ID.");
|
||||||
|
return new RedirectResponseValues(
|
||||||
|
occ.resolvePathWithWebapp(PATH_ADD_EXTERNAL_ID));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationManager;
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationStatus;
|
||||||
|
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the callbacks during the OAuth dance.
|
||||||
|
*
|
||||||
|
* This is not like other handlers. It is created and invoked from doGet(), not
|
||||||
|
* from processRequest().
|
||||||
|
*/
|
||||||
|
public class OrcidCallbackHandler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidCallbackHandler.class);
|
||||||
|
|
||||||
|
private final HttpServletRequest req;
|
||||||
|
private final HttpServletResponse resp;
|
||||||
|
|
||||||
|
public OrcidCallbackHandler(HttpServletRequest req, HttpServletResponse resp) {
|
||||||
|
this.req = req;
|
||||||
|
this.resp = resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exec() throws IOException {
|
||||||
|
OrcidClientContext occ = OrcidClientContext.getInstance();
|
||||||
|
AuthorizationManager authManager = occ.getAuthorizationManager(req);
|
||||||
|
try {
|
||||||
|
AuthorizationStatus auth = authManager
|
||||||
|
.processAuthorizationResponse();
|
||||||
|
if (auth.isSuccess()) {
|
||||||
|
resp.sendRedirect(auth.getSuccessUrl());
|
||||||
|
} else {
|
||||||
|
resp.sendRedirect(auth.getFailureUrl());
|
||||||
|
}
|
||||||
|
} catch (OrcidClientException e) {
|
||||||
|
log.error("Invalid authorization response", e);
|
||||||
|
resp.sendError(SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,250 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.START;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.PATH_AUTH_EXTERNAL_ID;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidIntegrationController.PATH_AUTH_AUTHENTICATE;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
import javax.xml.bind.JAXBElement;
|
||||||
|
import javax.xml.namespace.QName;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.ExternalIdentifier;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.ExternalIdentifiers;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidBio;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidId;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidMessage;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidProfile;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keep track of where we are in the Orcid confirmation process; what has been
|
||||||
|
* requested, and what has been returned.
|
||||||
|
*/
|
||||||
|
class OrcidConfirmationState {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidConfirmationState.class);
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// The factory
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
private static final String ATTRIBUTE_NAME = OrcidConfirmationState.class
|
||||||
|
.getName();
|
||||||
|
|
||||||
|
static OrcidConfirmationState fetch(HttpServletRequest req) {
|
||||||
|
HttpSession session = req.getSession();
|
||||||
|
Object o = session.getAttribute(ATTRIBUTE_NAME);
|
||||||
|
if (o instanceof OrcidConfirmationState) {
|
||||||
|
return (OrcidConfirmationState) o;
|
||||||
|
} else {
|
||||||
|
OrcidConfirmationState ocs = new OrcidConfirmationState();
|
||||||
|
session.setAttribute(ATTRIBUTE_NAME, ocs);
|
||||||
|
return ocs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// The instance
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public enum Progress {
|
||||||
|
START, DENIED_AUTHENTICATE, FAILED_AUTHENTICATE, GOT_PROFILE, ID_ALREADY_PRESENT, DENIED_ID, FAILED_ID, ADDED_ID
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Set<Progress> requiresMessage = EnumSet.of(
|
||||||
|
Progress.GOT_PROFILE, Progress.ADDED_ID);
|
||||||
|
|
||||||
|
private Progress progress;
|
||||||
|
private String individualUri;
|
||||||
|
private Set<String> existingOrcids;
|
||||||
|
private OrcidMessage profile;
|
||||||
|
private String profilePageUrl;
|
||||||
|
|
||||||
|
public void reset(String uri, String profileUrl) {
|
||||||
|
progress = START;
|
||||||
|
individualUri = uri;
|
||||||
|
existingOrcids = Collections.emptySet();
|
||||||
|
profile = null;
|
||||||
|
profilePageUrl = profileUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExistingOrcids(Set<String> existing) {
|
||||||
|
existingOrcids = new HashSet<>(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void progress(Progress p, OrcidMessage... messages) {
|
||||||
|
progress = p;
|
||||||
|
|
||||||
|
if (requiresMessage.contains(p)) {
|
||||||
|
if (messages.length != 1) {
|
||||||
|
throw new IllegalStateException("Progress to " + p
|
||||||
|
+ " requires an OrcidMessage");
|
||||||
|
}
|
||||||
|
profile = messages[0];
|
||||||
|
} else {
|
||||||
|
if (messages.length != 0) {
|
||||||
|
throw new IllegalStateException("Progress to " + p
|
||||||
|
+ " does not accept an OrcidMessage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Convenience methods for extracting information from the profile.
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
|
public String getProgress() {
|
||||||
|
return progress.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProgressUrl() {
|
||||||
|
switch (progress) {
|
||||||
|
case START:
|
||||||
|
return UrlBuilder.getUrl(PATH_AUTH_AUTHENTICATE);
|
||||||
|
case GOT_PROFILE:
|
||||||
|
return UrlBuilder.getUrl(PATH_AUTH_EXTERNAL_ID);
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getIndividualUri() {
|
||||||
|
return individualUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getProfilePageUrl() {
|
||||||
|
return profilePageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrcid() {
|
||||||
|
return getElementFromOrcidIdentifier("path");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrcidUri() {
|
||||||
|
return getElementFromOrcidIdentifier("uri");
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExternalIdentifier getVivoId() {
|
||||||
|
for (ExternalIdentifier id : getExternalIds()) {
|
||||||
|
if (individualUri.equals(id.getExternalIdUrl().getValue())) {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ExternalIdentifier> getExternalIds() {
|
||||||
|
OrcidProfile orcidProfile = getOrcidProfile();
|
||||||
|
if (orcidProfile == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
OrcidBio bio = orcidProfile.getOrcidBio();
|
||||||
|
if (bio == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExternalIdentifiers identifiers = bio.getExternalIdentifiers();
|
||||||
|
if (identifiers == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ExternalIdentifier> list = identifiers.getExternalIdentifier();
|
||||||
|
if (list == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getElementFromOrcidIdentifier(String elementName) {
|
||||||
|
OrcidProfile orcidProfile = getOrcidProfile();
|
||||||
|
if (orcidProfile == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
OrcidId id = orcidProfile.getOrcidIdentifier();
|
||||||
|
if (id == null) {
|
||||||
|
log.warn("There is no ORCID Identifier in the profile.");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
List<JAXBElement<String>> idElements = id.getContent();
|
||||||
|
if (idElements != null) {
|
||||||
|
for (JAXBElement<String> idElement : idElements) {
|
||||||
|
QName name = idElement.getName();
|
||||||
|
if (name != null && elementName.equals(name.getLocalPart())) {
|
||||||
|
String value = idElement.getValue();
|
||||||
|
if (value != null) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.warn("Didn't find the element '' in the ORCID Identifier: " + idElements);
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private OrcidProfile getOrcidProfile() {
|
||||||
|
if (profile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
OrcidProfile orcidProfile = profile.getOrcidProfile();
|
||||||
|
if (orcidProfile == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return orcidProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, Object> toMap() {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("progress", progress.toString());
|
||||||
|
map.put("individualUri", individualUri);
|
||||||
|
map.put("profilePage", profilePageUrl);
|
||||||
|
map.put("orcid", getOrcid());
|
||||||
|
map.put("orcidUri", getOrcidUri());
|
||||||
|
map.put("hasVivoId", getVivoId() == null);
|
||||||
|
map.put("externalIds", formatExternalIds());
|
||||||
|
map.put("existingOrcids", existingOrcids);
|
||||||
|
|
||||||
|
String progressUrl = getProgressUrl();
|
||||||
|
if (progressUrl == null) {
|
||||||
|
map.put("progressUrl", "");
|
||||||
|
} else {
|
||||||
|
map.put("progressUrl", progressUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Map<String, String>> formatExternalIds() {
|
||||||
|
List<Map<String, String>> list = new ArrayList<>();
|
||||||
|
for (ExternalIdentifier id : getExternalIds()) {
|
||||||
|
Map<String, String> map = new HashMap<>();
|
||||||
|
map.put("commonName", id.getExternalIdCommonName().getContent());
|
||||||
|
map.put("reference", id.getExternalIdReference().getContent());
|
||||||
|
map.put("uri", id.getExternalIdUrl().getValue());
|
||||||
|
list.add(map);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.orcidclient.actions.ApiAction.ADD_EXTERNAL_ID;
|
||||||
|
import static edu.cornell.mannlib.orcidclient.actions.ApiAction.READ_PROFILE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.OrcidIdDataGetter.ORCID_ID;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.vedit.beans.LoginStatusBean;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.IdentifierBundle;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.RequestIdentifiers;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.identifier.common.HasProfile;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.Individual;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.ObjectPropertyStatement;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.beans.UserAccount;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.UrlBuilder;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.NotAuthorizedResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.TemplateResponseValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A request came from the "Confirm" button on the individual profile. Get a
|
||||||
|
* fresh state object, clear the AuthorizationCache and show the confirmation
|
||||||
|
* page.
|
||||||
|
*/
|
||||||
|
public class OrcidDefaultHandler extends OrcidAbstractHandler {
|
||||||
|
private static final Log log = LogFactory.getLog(OrcidDefaultHandler.class);
|
||||||
|
|
||||||
|
private Individual individual;
|
||||||
|
private final Set<String> existingOrcids = new HashSet<>();
|
||||||
|
|
||||||
|
public OrcidDefaultHandler(VitroRequest vreq) {
|
||||||
|
super(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseValues exec() {
|
||||||
|
try {
|
||||||
|
initializeState();
|
||||||
|
initializeAuthorizationCache();
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("No proper individual URI on the request", e);
|
||||||
|
return show400BadRequest(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAuthorized()) {
|
||||||
|
return showNotAuthorized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return showConfirmationPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeState() {
|
||||||
|
String uri = vreq.getParameter("individualUri");
|
||||||
|
if (uri == null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"No 'individualUri' parameter on request.");
|
||||||
|
}
|
||||||
|
|
||||||
|
String profilePage = UrlBuilder.getIndividualProfileUrl(uri, vreq);
|
||||||
|
state.reset(uri, profilePage);
|
||||||
|
|
||||||
|
individual = findIndividual();
|
||||||
|
locateExistingOrcids();
|
||||||
|
state.setExistingOrcids(existingOrcids);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void locateExistingOrcids() {
|
||||||
|
if (individual == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ObjectPropertyStatement> opss = individual
|
||||||
|
.getObjectPropertyStatements(ORCID_ID);
|
||||||
|
if (opss == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ObjectPropertyStatement ops : opss) {
|
||||||
|
existingOrcids.add(ops.getObjectURI());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initializeAuthorizationCache() {
|
||||||
|
auth.clearStatus(READ_PROFILE);
|
||||||
|
auth.clearStatus(ADD_EXTERNAL_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseValues show400BadRequest(Exception e) {
|
||||||
|
Map<String, Object> map = new HashMap<>();
|
||||||
|
map.put("title", "400 Bad Request");
|
||||||
|
map.put("errorMessage", e.getMessage());
|
||||||
|
return new TemplateResponseValues("error-titled.ftl", map,
|
||||||
|
SC_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isAuthorized() {
|
||||||
|
// Only a self-editor is authorized.
|
||||||
|
IdentifierBundle ids = RequestIdentifiers.getIdBundleForRequest(vreq);
|
||||||
|
Collection<String> profileUris = HasProfile.getProfileUris(ids);
|
||||||
|
log.debug("Authorized? individualUri=" + state.getIndividualUri()
|
||||||
|
+ ", profileUris=" + profileUris);
|
||||||
|
return profileUris.contains(state.getIndividualUri());
|
||||||
|
}
|
||||||
|
|
||||||
|
private ResponseValues showNotAuthorized() {
|
||||||
|
UserAccount user = LoginStatusBean.getCurrentUser(vreq);
|
||||||
|
String userName = (user == null) ? "ANONYMOUS" : user.getEmailAddress();
|
||||||
|
return new NotAuthorizedResponseValues(userName
|
||||||
|
+ "is not authorized for ORCID operations on '" + individual
|
||||||
|
+ "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The OrcidConfirmationState is not as we expected. Probably deserves a 500
|
||||||
|
* error.
|
||||||
|
*/
|
||||||
|
public class OrcidIllegalStateException extends OrcidClientException {
|
||||||
|
public OrcidIllegalStateException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public OrcidIllegalStateException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
||||||
|
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext;
|
||||||
|
import edu.cornell.mannlib.orcidclient.context.OrcidClientContext.Setting;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.auth.requestedAction.AuthorizationRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.authenticate.LogoutRedirector;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.FreemarkerHttpServlet;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ExceptionResponseValues;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* New workflow:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* Default: clear status for both readProfile and addExternalIDs
|
||||||
|
* show intro screen orcidOffer.ftl
|
||||||
|
* The click "do it", goes to /getProfileAuth
|
||||||
|
* Or "return to profile"
|
||||||
|
* /getProfileAuth: If already authorized, redirect to /readProfile
|
||||||
|
* Else, do the dance, ending with /readProfile callback
|
||||||
|
* Denied? show orcidDenied.ftl
|
||||||
|
* Failed? show orcidFailed.ftl
|
||||||
|
* /readProfile: read the profile, store in status
|
||||||
|
* figure external ID options, show orcidOfferIds.ftl
|
||||||
|
* If they click "do it", goes /authExternalIds
|
||||||
|
* If they click "nah", return to profile
|
||||||
|
* /authExternalIds: if already authorized, redirect to /addExternalIds
|
||||||
|
* Else, do the dance, ending with /addExternalIds callback
|
||||||
|
* /addExternalIds add one or both IDs, store new profile in status
|
||||||
|
* show orcidSuccess.ftl with "return to profile" and "view profile" links.
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
public class OrcidIntegrationController extends FreemarkerHttpServlet {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidIntegrationController.class);
|
||||||
|
|
||||||
|
private final static String PATHINFO_CALLBACK = "/callback";
|
||||||
|
private final static String PATHINFO_AUTH_AUTHENTICATE = "/getAuthticateAuth";
|
||||||
|
private final static String PATHINFO_READ_PROFILE = "/readProfile";
|
||||||
|
private final static String PATHINFO_AUTH_EXTERNAL_ID = "/authExternalId";
|
||||||
|
private final static String PATHINFO_ADD_EXTERNAL_ID = "/addExternalId";
|
||||||
|
|
||||||
|
public final static String PATH_DEFAULT = "orcid";
|
||||||
|
|
||||||
|
final static String PATH_AUTH_AUTHENTICATE = path(PATHINFO_AUTH_AUTHENTICATE);
|
||||||
|
final static String PATH_READ_PROFILE = path(PATHINFO_READ_PROFILE);
|
||||||
|
final static String PATH_AUTH_EXTERNAL_ID = path(PATHINFO_AUTH_EXTERNAL_ID);
|
||||||
|
final static String PATH_ADD_EXTERNAL_ID = path(PATHINFO_ADD_EXTERNAL_ID);
|
||||||
|
|
||||||
|
static String path(String pathInfo) {
|
||||||
|
return PATH_DEFAULT + pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
final static String TEMPLATE_CONFIRM = "orcidConfirm.ftl";
|
||||||
|
|
||||||
|
public static final String PROPERTY_EXTERNAL_ID_COMMON_NAME = "orcid.externalIdCommonName";
|
||||||
|
public static final String DEFAULT_EXTERNAL_ID_COMMON_NAME = "VIVO Identifier";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get in before FreemarkerHttpServlet for special handling.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void doGet(HttpServletRequest req, HttpServletResponse resp)
|
||||||
|
throws IOException, ServletException {
|
||||||
|
if (!isOrcidConfigured()) {
|
||||||
|
show404NotFound(resp);
|
||||||
|
}
|
||||||
|
if (PATHINFO_CALLBACK.equals(req.getPathInfo())) {
|
||||||
|
new OrcidCallbackHandler(req, resp).exec();
|
||||||
|
} else {
|
||||||
|
super.doGet(req, resp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We return AUTHORIZED here, but we want the LogoutRedirector to know that
|
||||||
|
* the user should not remain on this page after logging out.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected AuthorizationRequest requiredActions(VitroRequest vreq) {
|
||||||
|
LogoutRedirector.recordRestrictedPageUri(vreq);
|
||||||
|
return AuthorizationRequest.AUTHORIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Look at the path info and delegate to a handler.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected ResponseValues processRequest(VitroRequest vreq) throws Exception {
|
||||||
|
try {
|
||||||
|
String pathInfo = vreq.getPathInfo();
|
||||||
|
log.debug("Path info: " + pathInfo);
|
||||||
|
if (PATHINFO_AUTH_AUTHENTICATE.equals(pathInfo)) {
|
||||||
|
return new OrcidAuthAuthenticateHandler(vreq).exec();
|
||||||
|
} else if (PATHINFO_READ_PROFILE.equals(pathInfo)) {
|
||||||
|
return new OrcidReadProfileHandler(vreq).exec();
|
||||||
|
} else if (PATHINFO_AUTH_EXTERNAL_ID.equals(pathInfo)) {
|
||||||
|
return new OrcidAuthExternalIdsHandler(vreq).exec();
|
||||||
|
} else if (PATHINFO_ADD_EXTERNAL_ID.equals(pathInfo)) {
|
||||||
|
return new OrcidAddExternalIdHandler(vreq).exec();
|
||||||
|
} else {
|
||||||
|
return new OrcidDefaultHandler(vreq).exec();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new ExceptionResponseValues(e, SC_INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the ORCID interface is configured, it should not throw an exception
|
||||||
|
* when asked for the value of a setting.
|
||||||
|
*/
|
||||||
|
private boolean isOrcidConfigured() {
|
||||||
|
try {
|
||||||
|
OrcidClientContext.getInstance().getSetting(Setting.CLIENT_ID);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void show404NotFound(HttpServletResponse resp) throws IOException {
|
||||||
|
resp.sendError(SC_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||||
|
|
||||||
|
package edu.cornell.mannlib.vivo.orcid.controller;
|
||||||
|
|
||||||
|
import static edu.cornell.mannlib.orcidclient.actions.ApiAction.AUTHENTICATE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.DENIED_AUTHENTICATE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.FAILED_AUTHENTICATE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.GOT_PROFILE;
|
||||||
|
import static edu.cornell.mannlib.vivo.orcid.controller.OrcidConfirmationState.Progress.ID_ALREADY_PRESENT;
|
||||||
|
|
||||||
|
import org.apache.commons.logging.Log;
|
||||||
|
import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
|
import edu.cornell.mannlib.orcidclient.OrcidClientException;
|
||||||
|
import edu.cornell.mannlib.orcidclient.actions.ReadPublicBioAction;
|
||||||
|
import edu.cornell.mannlib.orcidclient.auth.AuthorizationStatus;
|
||||||
|
import edu.cornell.mannlib.orcidclient.orcidmessage.OrcidMessage;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.VitroRequest;
|
||||||
|
import edu.cornell.mannlib.vitro.webapp.controller.freemarker.responsevalues.ResponseValues;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We should now know the user's ORCID, so read the user's public ORCID profile.
|
||||||
|
*/
|
||||||
|
public class OrcidReadProfileHandler extends OrcidAbstractHandler {
|
||||||
|
private static final Log log = LogFactory
|
||||||
|
.getLog(OrcidReadProfileHandler.class);
|
||||||
|
|
||||||
|
private AuthorizationStatus status;
|
||||||
|
private OrcidMessage profile;
|
||||||
|
|
||||||
|
protected OrcidReadProfileHandler(VitroRequest vreq) {
|
||||||
|
super(vreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResponseValues exec() throws OrcidClientException {
|
||||||
|
status = auth.getAuthorizationStatus(AUTHENTICATE);
|
||||||
|
if (status.isSuccess()) {
|
||||||
|
readProfile();
|
||||||
|
state.progress(GOT_PROFILE, profile);
|
||||||
|
|
||||||
|
recordConfirmation();
|
||||||
|
|
||||||
|
if (state.getVivoId() != null) {
|
||||||
|
state.progress(ID_ALREADY_PRESENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
return showConfirmationPage();
|
||||||
|
} else if (status.isDenied()) {
|
||||||
|
return showConfirmationPage(DENIED_AUTHENTICATE);
|
||||||
|
} else {
|
||||||
|
return showConfirmationPage(FAILED_AUTHENTICATE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readProfile() throws OrcidClientException {
|
||||||
|
profile = new ReadPublicBioAction().execute(status.getAccessToken()
|
||||||
|
.getOrcid());
|
||||||
|
log.debug("Read profile");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -21,6 +21,10 @@
|
||||||
<#assign languageCount = 1>
|
<#assign languageCount = 1>
|
||||||
</#if>
|
</#if>
|
||||||
<#assign visRequestingTemplate = "foaf-person-wilma">
|
<#assign visRequestingTemplate = "foaf-person-wilma">
|
||||||
|
|
||||||
|
<#--add the VIVO-ORCID interface -->
|
||||||
|
<#include "individual-orcidInterface.ftl">
|
||||||
|
|
||||||
<section id="individual-intro" class="vcard person" role="region">
|
<section id="individual-intro" class="vcard person" role="region">
|
||||||
|
|
||||||
<section id="share-contact" role="region">
|
<section id="share-contact" role="region">
|
||||||
|
|
BIN
utilities/orcid/mockorcid.war
Normal file
BIN
utilities/orcid/mockorcid.war
Normal file
Binary file not shown.
Loading…
Add table
Reference in a new issue