/*
* JBoss, Home of Professional Open Source.
* Copyright 2008, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.picketlink.social.standalone.openid.api;
import org.openid4java.association.AssociationException;
import org.openid4java.consumer.ConsumerException;
import org.openid4java.consumer.ConsumerManager;
import org.openid4java.consumer.InMemoryConsumerAssociationStore;
import org.openid4java.consumer.InMemoryNonceVerifier;
import org.openid4java.consumer.VerificationResult;
import org.openid4java.discovery.DiscoveryException;
import org.openid4java.discovery.DiscoveryInformation;
import org.openid4java.discovery.Identifier;
import org.openid4java.message.AuthRequest;
import org.openid4java.message.AuthSuccess;
import org.openid4java.message.MessageException;
import org.openid4java.message.ParameterList;
import org.openid4java.message.ax.FetchRequest;
import org.openid4java.message.sreg.SRegRequest;
import org.picketlink.social.standalone.openid.api.exceptions.OpenIDAssociationException;
import org.picketlink.social.standalone.openid.api.exceptions.OpenIDConsumerException;
import org.picketlink.social.standalone.openid.api.exceptions.OpenIDDiscoveryException;
import org.picketlink.social.standalone.openid.api.exceptions.OpenIDLifeCycleException;
import org.picketlink.social.standalone.openid.api.exceptions.OpenIDMessageException;
import org.picketlink.social.standalone.openid.api.exceptions.OpenIDProtocolException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* OpenID Manager for consumers
*
* @author Anil.Saldhana@redhat.com
* @since Jul 6, 2009
*/
public class OpenIDManager {
public enum CONST {
OPENID("openid"), OPENID_CLAIMED("openid-claimed"), OPENID_DISC("openid-discovered");
private String val;
CONST(String val) {
this.val = val;
}
public String get() {
return this.val;
}
}
private OpenIDRequest request = null;
private ConsumerManager consumerManager = null;
private String userString = null;
public OpenIDManager(OpenIDRequest theReq) {
this.request = theReq;
consumerManager = new ConsumerManager();
consumerManager.setAssociations(new InMemoryConsumerAssociationStore());
consumerManager.setNonceVerifier(new InMemoryNonceVerifier(5000));
userString = request.getURL();
}
/**
* Set the user string
*
* @param userString
*/
public void setUserString(String userString) {
this.userString = userString;
}
/**
* Get the OpenID Request
*
* @return
*/
public OpenIDRequest getOpenIDRequest() {
return this.request;
}
@SuppressWarnings("unchecked")
public OpenIDProviderList discoverProviders() throws OpenIDDiscoveryException, OpenIDConsumerException {
// perform discovery on the user-supplied identifier
List<DiscoveryInformation> discoveries;
try {
discoveries = consumerManager.discover(userString);
} catch (DiscoveryException e1) {
throw new OpenIDDiscoveryException(e1);
}
return new OpenIDProviderList(discoveries);
}
/**
* Associate with a list of open id providers
*
* @param adapter Protocol adapter (such as http)
* @param listOfProviders (a list of providers from discovery)
*
* @return
*
* @throws OpenIDConsumerException
* @throws OpenIDLifeCycleException
*/
public OpenIDProviderInformation associate(OpenIDProtocolAdapter adapter, OpenIDProviderList listOfProviders)
throws OpenIDConsumerException, OpenIDLifeCycleException {
OpenIDLifecycle lifeCycle = null;
if (adapter instanceof OpenIDLifecycle) {
lifeCycle = (OpenIDLifecycle) adapter;
}
List<DiscoveryInformation> discoveries = listOfProviders.get();
if (discoveries.size() == 0)
throw new OpenIDConsumerException("No open id endpoints discovered");
// attempt to associate with the OpenID provider
// and retrieve one service endpoint for authentication
DiscoveryInformation discovered = consumerManager.associate(discoveries);
// store the discovery information in the user's session for later use
// leave out for stateless operation / if there is no session
if (lifeCycle != null) {
OpenIDLifecycleEvent ev = new OpenIDLifecycleEvent(OpenIDLifecycleEvent.TYPE.SESSION, OpenIDLifecycleEvent.OP.ADD, CONST.OPENID_DISC.get(), discovered);
lifeCycle.handle(ev);
}
return new OpenIDProviderInformation(discovered);
}
/**
* Authenticate an user with the provider
*
* @param adapter protocol adapter
* @param providerInfo Information about a provider derived from discovery process
*
* @return
*
* @throws OpenIDDiscoveryException
* @throws OpenIDConsumerException
* @throws OpenIDMessageException
* @throws OpenIDProtocolException
*/
@SuppressWarnings("unchecked")
public boolean authenticate(OpenIDProtocolAdapter adapter, OpenIDProviderInformation providerInfo)
throws OpenIDDiscoveryException, OpenIDConsumerException, OpenIDMessageException, OpenIDProtocolException {
DiscoveryInformation discovered = providerInfo.get();
// obtain a AuthRequest message to be sent to the OpenID provider
try {
AuthRequest authReq = consumerManager.authenticate(discovered, adapter.getReturnURL());
// Attribute Exchange example: fetching the 'email' attribute
FetchRequest fetch = FetchRequest.createFetchRequest();
SRegRequest sregReq = SRegRequest.createFetchRequest();
OpenIDAttributeMap amap = adapter.getAttributeMap();
if ("1".equals(amap.get("nickname"))) {
// fetch.addAttribute("nickname",
// "http://schema.openid.net/contact/nickname", false);
sregReq.addAttribute("nickname", false);
}
if ("1".equals(amap.get("email"))) {
fetch.addAttribute("email", OpenIDConstants.EMAIL.url(), false);
sregReq.addAttribute("email", false);
}
if ("1".equals(amap.get("fullname"))) {
fetch.addAttribute("fullname", OpenIDConstants.FULLNAME.url(), false);
sregReq.addAttribute("fullname", false);
}
if ("1".equals(amap.get("dob"))) {
fetch.addAttribute("dob", OpenIDConstants.DOB.url(), true);
sregReq.addAttribute("dob", false);
}
if ("1".equals(amap.get("gender"))) {
fetch.addAttribute("gender", OpenIDConstants.GENDER.url(), false);
sregReq.addAttribute("gender", false);
}
if ("1".equals(amap.get("postcode"))) {
fetch.addAttribute("postcode", OpenIDConstants.POSTCODE.url(), false);
sregReq.addAttribute("postcode", false);
}
if ("1".equals(amap.get("country"))) {
fetch.addAttribute("country", OpenIDConstants.COUNTRY.url(), false);
sregReq.addAttribute("country", false);
}
if ("1".equals(amap.get("language"))) {
fetch.addAttribute("language", OpenIDConstants.LANGUAGE.url(), false);
sregReq.addAttribute("language", false);
}
if ("1".equals(amap.get("timezone"))) {
fetch.addAttribute("timezone", OpenIDConstants.TIMEZONE.url(), false);
sregReq.addAttribute("timezone", false);
}
// attach the extension to the authentication request
if (!sregReq.getAttributes().isEmpty()) {
authReq.addExtension(sregReq);
}
if (!discovered.isVersion2()) {
// Option 1: GET HTTP-redirect to the OpenID Provider endpoint
// The only method supported in OpenID 1.x
// redirect-URL usually limited ~2048 bytes
adapter.sendToProvider(1, authReq.getDestinationUrl(true), null);
return true;
} else {
// Option 2: HTML FORM Redirection (Allows payloads >2048 bytes)
adapter.sendToProvider(2, authReq.getDestinationUrl(false), authReq.getParameterMap());
}
} catch (MessageException e) {
throw new OpenIDMessageException(e);
} catch (ConsumerException e) {
throw new OpenIDConsumerException(e);
}
return false;
}
/**
* Verify a previously authenticated user with the provider
*
* @param adapter protocol adapter
* @param parameterMap request parameters
* @param receivedURL url where the response will be received
*
* @return
*
* @throws OpenIDMessageException
* @throws OpenIDDiscoveryException
* @throws OpenIDAssociationException
* @throws OpenIDLifeCycleException
*/
public boolean verify(OpenIDProtocolAdapter adapter, Map<String, String> parameterMap, String receivedURL)
throws OpenIDMessageException, OpenIDDiscoveryException, OpenIDAssociationException, OpenIDLifeCycleException {
OpenIDLifecycle lifeCycle = null;
if (adapter instanceof OpenIDLifecycle) {
lifeCycle = (OpenIDLifecycle) adapter;
}
ParameterList responselist = new ParameterList(parameterMap);
if (lifeCycle == null)
throw new IllegalStateException("Lifecycle not found");
DiscoveryInformation discovered = (DiscoveryInformation) lifeCycle.getAttributeValue(CONST.OPENID_DISC.get());
// verify the response; ConsumerManager needs to be the same
// (static) instance used to place the authentication request
try {
VerificationResult verification = this.consumerManager.verify(receivedURL, responselist, discovered);
// examine the verification result and extract the verified identifier
Identifier verified = verification.getVerifiedId();
if (verified != null) {
AuthSuccess authSuccess = (AuthSuccess) verification.getAuthResponse();
// Create an lifecycle event array
OpenIDLifecycleEvent[] eventArr = new OpenIDLifecycleEvent[]{
/** Store the id **/
new OpenIDLifecycleEvent(OpenIDLifecycleEvent.TYPE.SESSION, OpenIDLifecycleEvent.OP.ADD, CONST.OPENID.get(), authSuccess.getIdentity()),
/** Store the claimed **/
new OpenIDLifecycleEvent(OpenIDLifecycleEvent.TYPE.SESSION, OpenIDLifecycleEvent.OP.ADD, CONST.OPENID_CLAIMED.get(), authSuccess.getClaimed()),
/** Indicate success **/
new OpenIDLifecycleEvent(OpenIDLifecycleEvent.TYPE.SUCCESS, null, null, null)};
lifeCycle.handle(eventArr);
return true;
}
} catch (MessageException e) {
throw new OpenIDMessageException(e);
} catch (DiscoveryException e) {
throw new OpenIDDiscoveryException(e);
} catch (AssociationException e) {
throw new OpenIDAssociationException(e);
}
return false;
}
/**
* Log an user out from an openid provider
*
* @param adapter protocol adapter
*
* @throws OpenIDLifeCycleException
*/
public void logout(OpenIDProtocolAdapter adapter) throws OpenIDLifeCycleException {
OpenIDLifecycle lifeCycle = null;
if (adapter instanceof OpenIDLifecycle) {
lifeCycle = (OpenIDLifecycle) adapter;
}
if (lifeCycle != null) {
lifeCycle.handle(new OpenIDLifecycleEvent(OpenIDLifecycleEvent.TYPE.SESSION, OpenIDLifecycleEvent.OP.REMOVE, CONST.OPENID.get(), null));
lifeCycle.handle(new OpenIDLifecycleEvent(OpenIDLifecycleEvent.TYPE.SESSION, OpenIDLifecycleEvent.OP.REMOVE, CONST.OPENID_CLAIMED.get(), null));
}
}
/**
* Information about a provider from the discovery process
*/
public static class OpenIDProviderInformation {
private DiscoveryInformation discovered;
OpenIDProviderInformation(DiscoveryInformation di) {
this.discovered = di;
}
DiscoveryInformation get() {
return this.discovered;
}
}
/**
* List of OpenID providers
*/
public static class OpenIDProviderList {
private List<DiscoveryInformation> providers = null;
OpenIDProviderList(List<DiscoveryInformation> providers) {
this.providers = providers;
}
void addProvider(DiscoveryInformation provider) {
this.providers.add(provider);
}
List<DiscoveryInformation> get() {
return Collections.unmodifiableList(providers);
}
public int size() {
return this.providers != null ? providers.size() : 0;
}
}
}