Index: src/main/java/com/xpn/xwiki/plugin/openid/OpenIdHelper.java
===================================================================
--- src/main/java/com/xpn/xwiki/plugin/openid/OpenIdHelper.java (revision 0)
+++ src/main/java/com/xpn/xwiki/plugin/openid/OpenIdHelper.java (revision 0)
@@ -0,0 +1,377 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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 com.xpn.xwiki.plugin.openid;
+
+import java.net.MalformedURLException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.openid4java.consumer.ConsumerException;
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.server.ServerManager;
+import org.xwiki.query.Query;
+import org.xwiki.query.QueryException;
+import org.xwiki.query.QueryManager;
+
+import com.ibm.icu.text.MessageFormat;
+import com.xpn.xwiki.XWiki;
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.objects.BaseObject;
+import com.xpn.xwiki.objects.classes.BaseClass;
+import com.xpn.xwiki.web.XWikiServletURLFactory;
+
+/**
+ * OpenID helper class. This singleton class contains various helper methods used for OpenID related tasks.
+ *
+ * @since 2.3-milestone-2
+ * @version $Id$
+ */
+public final class OpenIdHelper
+{
+
+ /** The fullName of the document that contains the class definition of an OpenID identifier. */
+ private static final String XWIKI_OPENIDIDENTIFIER_CLASSNAME = "XWiki.OpenIdIdentifier";
+
+ /** The name of the field representing the user password in the XWiki.XWikiUsers class. */
+ private static final String PASSWORD_FIELD_NAME = "password";
+
+ /** The name of the field representing the identifier in the XWiki.OpenIdIdentifier class. */
+ private static final String IDENTIFIER_FIELD_NAME = "identifier";
+
+ /** String representing a hyphen (-). */
+ private static final String HYPHEN = "-";
+
+ /** String representing a dot (.). */
+ private static final String DOT = ".";
+
+ /** The name of the XWiki space. */
+ private static final String XWIKI_SPACENAME = "XWiki";
+
+ /** The name of the XWiki.XWikiUsers document. */
+ private static final String XWIKIUSERS_DOCUMENT_NAME = "XWiki.XWikiUsers";
+
+ /** The log used by this class. */
+ private static final Log LOG = LogFactory.getLog(OpenIdHelper.class);
+
+ /** Our instance. */
+ private static OpenIdHelper instance;
+
+ /** The Open Id consumer manager this class uses. */
+ private ConsumerManager consumerManager;
+
+ /** The Open Id server manager this class uses. */
+ private ServerManager serverManager;
+
+ /**
+ * The constructor instantiates a ConsumerManager and a ServerManager object.
+ *
+ * @throws ConsumerException when the consumer manager failed to initialize.
+ */
+ private OpenIdHelper() throws ConsumerException
+ {
+ consumerManager = new ConsumerManager();
+ serverManager = new ServerManager();
+ }
+
+ /**
+ * Gets the unique ConsumerManager class.
+ *
+ * @return the unique ConsumerManager instance
+ * @throws ConsumerException when the consumer manager failed to initialize.
+ */
+ public static ConsumerManager getConsumerManager() throws ConsumerException
+ {
+ if (instance == null) {
+ instance = new OpenIdHelper();
+ }
+
+ return instance.consumerManager;
+ }
+
+ /**
+ * Gets the unique ServerManager class.
+ *
+ * @param context the context
+ * @return the unique ServerManager instance
+ * @throws ConsumerException when the consumer manager failed to initialize.
+ */
+ public static ServerManager getServerManager(XWikiContext context) throws ConsumerException
+ {
+ if (instance == null) {
+ instance = new OpenIdHelper();
+ instance.serverManager.setOPEndpointUrl(getOpenIdServerURL(context));
+ }
+
+ return instance.serverManager;
+ }
+
+ /**
+ * Converts an OpenID identifier to an user name which can be used as a XWiki document name. This method
+ * doesn't check if that user already exists!
+ *
+ * @param openidIdentifier the OpenID identifier to convert
+ * @param context the context
+ * @return the converted OpenID identifier
+ */
+ public static String openIdIdentifierToUsername(String openidIdentifier, XWikiContext context)
+ {
+ return "OpenID-" + context.getWiki().clearName(openidIdentifier, true, true, context) + HYPHEN
+ + openidIdentifier.hashCode();
+ }
+
+ /**
+ * Finds the user belonging to a specific OpenID identifier.
+ *
+ * @param openidIdentifier the OpenID identifier to search for
+ * @param context the context
+ * @return the full document name for the user belonging to the OpenID identifier or null
if the
+ * OpenID identifier was not found.
+ */
+ public static String findUser(String openidIdentifier, XWikiContext context)
+ {
+ XWiki xwiki = context.getWiki();
+
+ QueryManager qm = xwiki.getStore().getQueryManager();
+ Query searchUser;
+ try {
+ searchUser = qm.getNamedQuery("getUserDocByOpenIdIdentifier");
+ } catch (QueryException e) {
+ throw new RuntimeException("Named query 'getUserDocByOpenIdIdentifier' was not found!", e);
+ }
+
+ searchUser.bindValue(IDENTIFIER_FIELD_NAME, openidIdentifier);
+
+ try {
+ List foundUsers = searchUser.setLimit(1).execute();
+
+ if (foundUsers.size() > 0) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug(MessageFormat.format("OpenID {0} already registered.",
+ new String[] {openidIdentifier}));
+ }
+ return foundUsers.get(0);
+ }
+
+ } catch (QueryException e) {
+ throw new RuntimeException("Failed to execute query getUserDocByOpenIdIdentifier", e);
+ }
+
+ return null;
+ }
+
+ /**
+ * Creates a new OpenID user with a random password. The user doesn't need to know the password - in fact he even
+ * doesn't need to know of its existence. It's just used to use the {@link PersistentLoginManager} and the
+ * Authenticator as they are implemented at the moment.
+ *
+ * @param openidIdentifier the OpenID identifier
+ * @param firstname users first name
+ * @param lastname users last name
+ * @param email users email address
+ * @param context the context
+ * @return a code which describes the success or failure of the method
+ * @throws XWikiException when the user creation failed
+ */
+ public static int createUser(String openidIdentifier, String firstname, String lastname, String email,
+ XWikiContext context) throws XWikiException
+ {
+ XWiki xwiki = context.getWiki();
+ String xwikiname = openIdIdentifierToUsername(openidIdentifier, context);
+
+ // Generate a unique document name for the new user
+ XWikiDocument userdoc = xwiki.getDocument(XWIKI_SPACENAME + DOT + xwikiname, context);
+ while (!userdoc.isNew()) {
+ userdoc = xwiki.getDocument(XWIKI_SPACENAME + DOT + xwikiname + HYPHEN
+ + xwiki.generateRandomString(5), context);
+ }
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Creating user for OpenID " + openidIdentifier);
+ }
+
+ String content = "{{include document=\"XWiki.XWikiUserSheet\" /}}";
+ String parent = XWIKIUSERS_DOCUMENT_NAME;
+ Map map = new HashMap();
+ map.put("active", "1");
+ map.put("first_name", StringUtils.isEmpty(firstname) ? openidIdentifier : firstname);
+
+ if (lastname != null) {
+ map.put("last_name", lastname);
+ }
+ if (email != null) {
+ map.put("email", email);
+ }
+
+ String password = xwiki.generateRandomString(255);
+ map.put(PASSWORD_FIELD_NAME, password);
+
+ int result = xwiki.createUser(xwikiname, map, parent, content, "edit", context);
+ if (result == 1) {
+ // change the return value to output a different message for OpenID users
+ result = 3;
+
+ userdoc = xwiki.getDocument(XWIKI_SPACENAME + DOT + xwikiname, context);
+
+ if (!attachOpenIdToUser(userdoc, openidIdentifier, context)) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Deleting previously created document for OpenID user " + openidIdentifier
+ + ". OpenID identifier is already in use.");
+ }
+ xwiki.deleteDocument(userdoc, false, context);
+ result = -13;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Attaches an OpenID identifier to a document. The method assures that the identifier is unique. If the OpenID
+ * identifier is already used, the method fails and returns false
. If the the document has already
+ * attached an OpenID it will be replaced by this method. The document is automatically saved by this
+ * method.
+ *
+ * @param doc document to which the OpenID identifier should be attached
+ * @param openidIdentifier the OpenID identifier to attach
+ * @param context the context
+ * @return true
if attaching the OpenID identifier was successful, otherwise false
.
+ * @throws XWikiException when saving the user document fails
+ */
+ public static synchronized boolean attachOpenIdToUser(XWikiDocument doc, String openidIdentifier,
+ XWikiContext context) throws XWikiException
+ {
+ XWiki xwiki = context.getWiki();
+
+ if (findUser(openidIdentifier, context) != null) {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("OpenID " + openidIdentifier + " is already registered");
+ }
+ return false;
+ }
+
+ BaseClass baseclass = getOpenIdIdentifierClass(context);
+
+ BaseObject object = doc.getObject(XWIKI_OPENIDIDENTIFIER_CLASSNAME);
+ if (object == null) {
+ object = (BaseObject) baseclass.newObject(context);
+ object.setName(doc.getFullName());
+ doc.addObject(baseclass.getName(), object);
+ }
+ object.setStringValue(IDENTIFIER_FIELD_NAME, openidIdentifier);
+
+ xwiki.saveDocument(doc, context.getMessageTool().get("core.comment.addedOpenIdIdentifier"), context);
+
+ return true;
+ }
+
+ /**
+ * Retrieves the password for an OpenID identifier. Since for all OpenID users a random password which the user
+ * doesn't know is created automatically, we need some way to retrieve it. This method does exactly that.
+ *
+ * @param openidIdentifier the OpenID identifier
+ * @param context the context
+ * @return the internal used password for the OpenID user or null
if the user was not found
+ * @throws XWikiException when the user document could not be retrieve against the wiki.
+ */
+ public static String getOpenIdUserPassword(String openidIdentifier, XWikiContext context) throws XWikiException
+ {
+ String xwikiname = findUser(openidIdentifier, context);
+
+ XWikiDocument doc = context.getWiki().getDocument(xwikiname, context);
+ // We only allow empty password from users having a XWikiUsers object.
+ if (doc.getObject(XWIKI_OPENIDIDENTIFIER_CLASSNAME) != null
+ && doc.getObject(XWIKIUSERS_DOCUMENT_NAME) != null) {
+ return doc.getStringValue(XWIKIUSERS_DOCUMENT_NAME, PASSWORD_FIELD_NAME);
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieves the OpenID identifier for an user.
+ *
+ * @param username the user
+ * @param context the context
+ * @return the OpenID identifier for the passed user name or null
if the user was not found
+ * @throws XWikiException when the user document could not be retrieve against the wiki.
+ */
+ public static String getUserOpenId(String username, XWikiContext context) throws XWikiException
+ {
+ XWikiDocument doc = context.getWiki().getDocument(username, context);
+ if (doc != null && doc.getObject(XWIKIUSERS_DOCUMENT_NAME) != null) {
+ return getOpenIdServerURL(context) + doc.getName();
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the URL of the OpenID server.
+ *
+ * @param context the context
+ * @return the URL of the OpenID server endpoint.
+ */
+ public static String getOpenIdServerURL(XWikiContext context)
+ {
+ try {
+ return ((XWikiServletURLFactory) context.getURLFactory()).getServerURL(context).toString()
+ + ((XWikiServletURLFactory) context.getURLFactory()).getContextPath() + "openid/";
+ } catch (MalformedURLException e) {
+ // should not happen :-)
+ return null;
+ }
+ }
+
+ /**
+ * Gets the OpenIDIdentifer class. Verifies if the XWiki.OpenIdIdentifier
page exists and that it
+ * contains all the required configuration properties to make the OpenID feature work properly. If some properties
+ * are missing they are created and saved in the database.
+ *
+ * @param context the XWiki Context
+ * @return the OpenIdIdentifier Base Class object containing the properties
+ * @throws XWikiException if an error happens while saving
+ */
+ public static BaseClass getOpenIdIdentifierClass(XWikiContext context) throws XWikiException
+ {
+ XWiki xwiki = context.getWiki();
+ XWikiDocument doc;
+ boolean needsUpdate = false;
+
+ doc = xwiki.getDocument(XWIKI_OPENIDIDENTIFIER_CLASSNAME, context);
+
+ BaseClass bclass = doc.getxWikiClass();
+ bclass.setName(XWIKI_OPENIDIDENTIFIER_CLASSNAME);
+
+ needsUpdate |= bclass.addTextField(IDENTIFIER_FIELD_NAME, "Identifier", 2048);
+ needsUpdate |= bclass.addTextField(PASSWORD_FIELD_NAME, "Password", 255);
+
+ if (needsUpdate) {
+ xwiki.saveDocument(doc, context);
+ }
+
+ return bclass;
+ }
+}
Index: src/main/java/com/xpn/xwiki/web/Utils.java
===================================================================
--- src/main/java/com/xpn/xwiki/web/Utils.java (revision 27619)
+++ src/main/java/com/xpn/xwiki/web/Utils.java (working copy)
@@ -110,6 +110,7 @@
} else {
response.setContentType("text/html; charset=" + context.getWiki().getEncoding());
}
+
String action = context.getAction();
if ((!"download".equals(action)) && (!"skin".equals(action))) {
@@ -134,6 +135,10 @@
}
}
}
+
+ if("openid_xrds".equals(action)) {
+ response.setContentType("application/xrds+xml");
+ }
if (("download".equals(action)) || ("skin".equals(action))) {
// Set a nocache to make sure these files are not cached by proxies
Index: src/main/java/com/xpn/xwiki/web/RegisterAction.java
===================================================================
--- src/main/java/com/xpn/xwiki/web/RegisterAction.java (revision 27619)
+++ src/main/java/com/xpn/xwiki/web/RegisterAction.java (working copy)
@@ -20,41 +20,284 @@
*/
package com.xpn.xwiki.web;
+import java.util.List;
+
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
-import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.plugin.openid.OpenIdHelper;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
import org.apache.velocity.VelocityContext;
-public class RegisterAction extends XWikiAction {
- public boolean action(XWikiContext context) throws XWikiException {
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.consumer.VerificationResult;
+import org.openid4java.discovery.Identifier;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.message.ax.AxMessage;
+import org.openid4java.message.ax.FetchRequest;
+import org.openid4java.message.ax.FetchResponse;
+import org.openid4java.message.sreg.SRegMessage;
+import org.openid4java.message.sreg.SRegRequest;
+import org.openid4java.message.sreg.SRegResponse;
+import org.openid4java.message.*;
+import org.openid4java.OpenIDException;
+
+/**
+ * Action used to register new users. This action implements both, registration for classical user accounts with user
+ * name and password and users using OpenID.
+ */
+public class RegisterAction extends XWikiAction
+{
+ private static final Log log = LogFactory.getLog(RegisterAction.class);
+
+ private String template = "register";
+
+ public boolean action(XWikiContext context) throws XWikiException
+ {
XWiki xwiki = context.getWiki();
XWikiRequest request = context.getRequest();
XWikiResponse response = context.getResponse();
- XWikiDocument doc = context.getDoc();
+ template = "register";
+
String register = request.getParameter("register");
- if ((register!=null)&&(register.equals("1"))) {
+ if ((register != null) && (register.equals("1"))) {
int useemail = xwiki.getXWikiPreferenceAsInt("use_email_verification", 0, context);
int result;
- if (useemail==1)
- result = xwiki.createUser(true, "edit", context);
+ if (useemail == 1)
+ result = xwiki.createUser(true, "edit", context);
else
- result = xwiki.createUser(context);
+ result = xwiki.createUser(context);
VelocityContext vcontext = (VelocityContext) context.get("vcontext");
vcontext.put("reg", new Integer(result));
+ } else if ((register != null) && (register.equals("openid-discover"))) {
+ if (discoverOpenId(context))
+ return false;
+ } else if ((register != null) && (register.equals("openid-confirm"))) {
+ String confirm = request.getParameter("register-confirm");
+ if ("1".equals(confirm) == false) {
+ confirmOpenIdRegistration(context);
+ return true;
+ } else {
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ vcontext.put("reg", new Integer(registerOpenId(context)));
+ }
}
String redirect = Utils.getRedirect(request, null);
- if (redirect==null)
+ if (redirect == null)
return true;
else {
sendRedirect(response, redirect);
return false;
}
- }
-
- public String render(XWikiContext context) throws XWikiException {
- return "register";
- }
+ }
+
+ public String render(XWikiContext context) throws XWikiException
+ {
+ return template;
+ }
+
+ /**
+ * Starts registration of an OpenID user. The OpenID provider belonging to the entered OpenID identifier is searched
+ * and the user is redirected to it to authenticate there. This processed is used to assure that the entered OpenID
+ * is valid and in possession of that user. If discovery fails, an error message is shown.
+ *
+ * @param context
+ * @return returns true if a redirect was sent, otherwise false.
+ * @author Markus Lanthaler
+ */
+ protected boolean discoverOpenId(XWikiContext context)
+ {
+ XWikiRequest request = context.getRequest();
+ XWikiResponse response = context.getResponse();
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+
+ // Check for empty OpenID identifier
+ String openid_identifier = request.getParameter("openid_identifier");
+ if (openid_identifier == null || openid_identifier.equals("")) {
+ vcontext.put("reg", new Integer(-14));
+ return false;
+ }
+
+ try {
+ ConsumerManager manager = OpenIdHelper.getConsumerManager();
+
+ String return_to_url =
+ context.getWiki().getExternalURL("Register", "register", "register=openid-confirm", context);
+
+ // perform discovery on the user-supplied identifier
+ List discoveries = manager.discover(openid_identifier);
+
+ // attempt to associate with the OpenID provider and retrieve one service endpoint for authentication
+ DiscoveryInformation discovered = manager.associate(discoveries);
+ request.getSession().setAttribute("openid-discovery", discovered);
+
+ // obtain a AuthRequest message to be sent to the OpenID provider
+ AuthRequest auth_request = manager.authenticate(discovered, return_to_url);
+
+ // set the realm
+ auth_request.setRealm(((XWikiServletURLFactory) context.getURLFactory()).getServerURL(context).toString()
+ + ((XWikiServletURLFactory) context.getURLFactory()).getContextPath());
+
+ // attribute exchange (request user data from the OP to speed-up the registration process)
+ FetchRequest att_exchange = FetchRequest.createFetchRequest();
+ att_exchange.addAttribute("email", "http://schema.openid.net/contact/email", true);
+ att_exchange.addAttribute("firstname", "http://axschema.org/namePerson/first", true);
+ att_exchange.addAttribute("lastname", "http://axschema.org/namePerson/last", true);
+
+ SRegRequest simple_reg_req = SRegRequest.createFetchRequest();
+ simple_reg_req.addAttribute("fullname", true);
+ simple_reg_req.addAttribute("firstname", true);
+ simple_reg_req.addAttribute("lastname", true);
+ simple_reg_req.addAttribute("nickname", true);
+ simple_reg_req.addAttribute("email", true);
+
+ auth_request.addExtension(att_exchange);
+ auth_request.addExtension(simple_reg_req);
+
+ if (discovered.isVersion2()) {
+ // OpenID 2.0 supports HTML form redirection which allows payloads >2048 bytes
+ vcontext.put("op_endpoint", auth_request.getDestinationUrl(false));
+ vcontext.put("openid_parameters", auth_request.getParameterMap());
+ template = "openid_form_redirect";
+ return false;
+ } else {
+ // the only method supported in OpenID 1.x is a HTTP-redirect (GET) to the OpenID Provider endpoint (the
+ // redirect-URL usually limited ~2048 bytes)
+ sendRedirect(response, auth_request.getDestinationUrl(true));
+ return true;
+ }
+ } catch (OpenIDException e) {
+ context.put("message", "register_openid_discovery_failed");
+ } catch (Exception e) {
+ context.put("message", "register_openid_discovery_failed");
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks the response of the OpenID provider (OP) and outputs a confirmation form. If the OP returns an error or
+ * the user cancelled at its site, an error message is shown to the user.
+ *
+ * @param context
+ * @throws XWikiException
+ * @author Markus Lanthaler
+ */
+ protected void confirmOpenIdRegistration(XWikiContext context) throws XWikiException
+ {
+ XWikiRequest request = context.getRequest();
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+
+ try {
+ ConsumerManager manager = OpenIdHelper.getConsumerManager();
+
+ // extract the parameters from the authentication response
+ // (which comes in as a HTTP request from the OpenID provider)
+ ParameterList openid_response = new ParameterList(request.getParameterMap());
+
+ // retrieve the previously stored discovery information
+ DiscoveryInformation discovered = (DiscoveryInformation) request.getSession().getAttribute("openid-discovery");
+
+ // extract the receiving URL from the HTTP request
+ StringBuffer receiving_url = request.getRequestURL();
+ String query_string = request.getQueryString();
+ if (query_string != null && query_string.length() > 0)
+ receiving_url.append("?").append(request.getQueryString());
+
+ // verify the response; ConsumerManager needs to be the same
+ // (static) instance used to place the authentication request
+ VerificationResult verification = manager.verify(receiving_url.toString(), openid_response, discovered);
+
+ // examine the verification result and extract the verified identifier
+ Identifier verified = verification.getVerifiedId();
+
+ if (verified != null) {
+ // check if this OpenID is already registered
+ if (OpenIdHelper.findUser(verified.getIdentifier(), context) != null) {
+ vcontext.put("reg", new Integer(-13));
+ vcontext.put("openid_identifier", verified.getIdentifier());
+ return;
+ }
+
+ // OpenID not used yet, continue with registration (ask the user for confirmation)
+ vcontext.put("reg", new Integer(2));
+ vcontext.put("openid_identifier", verified.getIdentifier());
+
+ AuthSuccess auth_success = (AuthSuccess) verification.getAuthResponse();
+
+ String email = null;
+ String firstname = null;
+ String lastname = null;
+
+ if (auth_success.hasExtension(AxMessage.OPENID_NS_AX)) {
+ FetchResponse fetch_resp = (FetchResponse) auth_success.getExtension(AxMessage.OPENID_NS_AX);
+ email = (String) fetch_resp.getAttributeValues("email").get(0);
+ firstname = (String) fetch_resp.getAttributeValues("firstname").get(0);
+ lastname = (String) fetch_resp.getAttributeValues("lastname").get(0);
+ }
+
+ if (auth_success.getExtension(SRegMessage.OPENID_NS_SREG) instanceof SRegResponse) {
+ SRegResponse simple_reg_resp = (SRegResponse) auth_success.getExtension(SRegMessage.OPENID_NS_SREG);
+
+ if (email == null)
+ email = simple_reg_resp.getAttributeValue("email");
+
+ if (firstname == null && lastname == null)
+ firstname = simple_reg_resp.getAttributeValue("fullname");
+ }
+
+ vcontext.put("email", email);
+ vcontext.put("first_name", firstname);
+ vcontext.put("last_name", lastname);
+ } else {
+ // authentication failed, show and log error message
+ if (openid_response.getParameter("openid.mode") != null
+ && openid_response.getParameter("openid.mode").getValue().equals("cancel")) {
+ context.put("message", "register_openid_discovery_cancelled");
+ } else {
+ if (log.isInfoEnabled() && openid_response.getParameter("error") != null) {
+ log.info("OpenID login failed (error: "
+ + openid_response.getParameter("openid.error").getValue() + ")");
+ }
+ context.put("message", "register_openid_discovery_failed");
+ }
+ }
+ } catch (OpenIDException e) {
+ context.put("message", "register_openid_discovery_failed");
+ }
+ }
+
+ /**
+ * Completes the registration of an OpenID user. The user name is created automatically based on the OpenID
+ * identifier by {@link OpenIdHelper#openIdIdentifierToUsername}.
+ *
+ * @param context
+ * @return an status code describing the success or failure of the registration
+ * @throws XWikiException
+ * @author Markus Lanthaler
+ * @see OpenIdHelper#openIdIdentifierToUsername
+ */
+ protected int registerOpenId(XWikiContext context) throws XWikiException
+ {
+ XWikiRequest request = context.getRequest();
+ String openid_identifier = request.getParameter("openid_identifier");
+ String firstname = request.getParameter("register_first_name");
+ String lastname = request.getParameter("register_last_name");
+ String email = request.getParameter("register_email");
+
+ int result = OpenIdHelper.createUser(openid_identifier, firstname, lastname, email, context);
+ if (result == 3) {
+ // user registration successful
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ vcontext.put("username", context.getWiki().getUserName(OpenIdHelper.findUser(openid_identifier, context),
+ context));
+ vcontext.put("openid_identifier", openid_identifier);
+ }
+
+ return result;
+ }
}
Index: src/main/java/com/xpn/xwiki/web/AttachOpenIdAction.java
===================================================================
--- src/main/java/com/xpn/xwiki/web/AttachOpenIdAction.java (revision 0)
+++ src/main/java/com/xpn/xwiki/web/AttachOpenIdAction.java (revision 0)
@@ -0,0 +1,305 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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 com.xpn.xwiki.web;
+
+import java.util.List;
+
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.plugin.openid.OpenIdHelper;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.VelocityContext;
+
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.consumer.VerificationResult;
+import org.openid4java.discovery.Identifier;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.message.AuthRequest;
+import org.openid4java.message.ParameterList;
+import org.openid4java.OpenIDException;
+
+/**
+ * Action used to attach an OpenID to already existing users.
+ *
+ * @since 2.3-milestone-2
+ * @version $Id$
+ */
+public class AttachOpenIdAction extends XWikiAction
+{
+ /** The name of the OpenId parameter representing the Open Id mode. */
+ private static final String OPENID_MODE_PARAMETER = "openid.mode";
+
+ /** The fullName of the document holding the class definition of an Open ID identifier. */
+ private static final String XWIKI_OPENID_IDENTIFIER_CLASSNAME = "XWiki.OpenIdIdentifier";
+
+ /** The name of the velocity context key for the username variable. */
+ private static final String USERNAME_VCONTEXT_KEY = "username";
+
+ /** The name of the velocity context key for the Open Id identifier variable. */
+ private static final String OPENID_IDENTIFIER_VCONTEXT_KEY = "openid_identifier";
+
+ /** The context key for an error or information message. */
+ private static final String MESSAGE_CONTEXT_KEY = "message";
+
+ /** The message when Open ID discovery failed. */
+ private static final String REGISTER_OPENID_DISCOVERY_FAILED_MESSAGE_VALUE = "register_openid_discovery_failed";
+
+ /** The name of the session attribute representing OpenId discovery information. */
+ private static final String OPENID_DISCOVERY_SESSION_KEY = "openid-discovery";
+
+ /** The key associated with the velocity context in the xwiki context map. */
+ private static final String VCONTEXT_CONTEXT_KEY = "vcontext";
+
+ /** The name of the 'status' variable pushed in the velocity context. */
+ private static final String STATUS_VCONTEXT_KEY = "status";
+
+ /** The name of the velocity template for attaching OpenId. */
+ private static final String ATTACH_OPENID_TEMPLATE_NAME = "attach_openid";
+
+ /** The log used by this class. */
+ private static final Log LOG = LogFactory.getLog(AttachOpenIdAction.class);
+
+ /** The name of the velocity template rendered by this servlet. */
+ private String template = ATTACH_OPENID_TEMPLATE_NAME;
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean action(XWikiContext context) throws XWikiException
+ {
+ // Only the user itself or an administrator is allowed to attach an OpenID
+ if (!(context.getDoc().getFullName().equals(context.getXWikiUser().getUser()))
+ && !context.getWiki().getRightService().hasAdminRights(context)) {
+ template = "accessdenied";
+ return true;
+ }
+
+ XWikiRequest request = context.getRequest();
+ XWikiResponse response = context.getResponse();
+ VelocityContext vcontext = (VelocityContext) context.get(VCONTEXT_CONTEXT_KEY);
+
+ template = ATTACH_OPENID_TEMPLATE_NAME;
+
+ String attachOpenid = request.getParameter(ATTACH_OPENID_TEMPLATE_NAME);
+ if (StringUtils.equals("discover-openid", attachOpenid)) {
+ if (discoverOpenID(context)) {
+ return false;
+ }
+ } else if (StringUtils.equals("confirm", attachOpenid)) {
+ vcontext.put(STATUS_VCONTEXT_KEY, new Integer(confirmAttachingOpenId(context)));
+ } else if (StringUtils.equals("attach-openid", attachOpenid)) {
+ vcontext.put(STATUS_VCONTEXT_KEY, new Integer(attachOpenID(context)));
+ }
+
+ String redirect = Utils.getRedirect(request, null);
+ if (redirect == null) {
+ return true;
+ } else {
+ sendRedirect(response, redirect);
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String render(XWikiContext context) throws XWikiException
+ {
+ return template;
+ }
+
+ /**
+ * Starts the process of attaching an OpenID to an existing user. The OpenID provider belonging to the entered
+ * OpenID identifier is searched and the user is redirected to it to authenticate there. This processed is used to
+ * assure that the entered OpenID is valid and in possession of that user. If discovery fails, an error message is
+ * shown.
+ *
+ * @param context the XWiki context when discovering OpenId.
+ * @return returns true if a redirect was sent, otherwise false.
+ */
+ protected boolean discoverOpenID(XWikiContext context)
+ {
+ XWikiRequest request = context.getRequest();
+ XWikiResponse response = context.getResponse();
+ VelocityContext vcontext = (VelocityContext) context.get(VCONTEXT_CONTEXT_KEY);
+
+ // Check for empty OpenID identifier
+ String openidIdentifier = request.getParameter(OPENID_IDENTIFIER_VCONTEXT_KEY);
+ if (openidIdentifier == null || openidIdentifier.equals("")) {
+ vcontext.put(STATUS_VCONTEXT_KEY, new Integer(-14));
+ return false;
+ }
+
+ try {
+ ConsumerManager manager = OpenIdHelper.getConsumerManager();
+
+ String returnToUrl = context.getDoc().getExternalURL("attachopenid", "attach_openid=confirm", context);
+
+ // perform discovery on the user-supplied identifier
+ List discoveries = manager.discover(openidIdentifier);
+
+ // attempt to associate with the OpenID provider and retrieve one service endpoint for authentication
+ DiscoveryInformation discovered = manager.associate(discoveries);
+ request.getSession().setAttribute(OPENID_DISCOVERY_SESSION_KEY, discovered);
+
+ // obtain a AuthRequest message to be sent to the OpenID provider
+ AuthRequest authRequest = manager.authenticate(discovered, returnToUrl);
+
+ // set the realm
+ authRequest.setRealm(((XWikiServletURLFactory) context.getURLFactory()).getServerURL(context).toString()
+ + ((XWikiServletURLFactory) context.getURLFactory()).getContextPath());
+
+ if (discovered.isVersion2()) {
+ // OpenID 2.0 supports HTML form redirection which allows payloads >2048 bytes
+ vcontext.put("op_endpoint", authRequest.getDestinationUrl(false));
+ vcontext.put("openid_parameters", authRequest.getParameterMap());
+ template = "openid_form_redirect";
+ return false;
+ } else {
+ // the only method supported in OpenID 1.x is a HTTP-redirect (GET) to the OpenID Provider endpoint (the
+ // redirect-URL usually limited ~2048 bytes)
+ sendRedirect(response, authRequest.getDestinationUrl(true));
+ return true;
+ }
+ } catch (OpenIDException e) {
+ context.put(MESSAGE_CONTEXT_KEY, REGISTER_OPENID_DISCOVERY_FAILED_MESSAGE_VALUE);
+ } catch (Exception e) {
+ context.put(MESSAGE_CONTEXT_KEY, REGISTER_OPENID_DISCOVERY_FAILED_MESSAGE_VALUE);
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks the response of the OpenID provider (OP) and outputs a confirmation form. If the OP returns an error or
+ * the user cancelled at its site, an error message is shown to the user.
+ *
+ * @param context the XWiki context when confirming attaching open ID.
+ *
+ * @return a code associated with the result of the operation.
+ */
+ protected int confirmAttachingOpenId(XWikiContext context)
+ {
+ XWikiRequest request = context.getRequest();
+ VelocityContext vcontext = (VelocityContext) context.get(VCONTEXT_CONTEXT_KEY);
+
+ try {
+ ConsumerManager manager = OpenIdHelper.getConsumerManager();
+
+ // extract the parameters from the authentication response
+ // (which comes in as a HTTP request from the OpenID provider)
+ ParameterList openidResponse = new ParameterList(request.getParameterMap());
+
+ // retrieve the previously stored discovery information
+ DiscoveryInformation discovered =
+ (DiscoveryInformation) request.getSession().getAttribute(OPENID_DISCOVERY_SESSION_KEY);
+
+ // extract the receiving URL from the HTTP request
+ StringBuffer receivingUrl = request.getRequestURL();
+ String queryString = request.getQueryString();
+ if (!StringUtils.isBlank(queryString)) {
+ receivingUrl.append("?").append(request.getQueryString());
+ }
+
+ // verify the response; ConsumerManager needs to be the same
+ // (static) instance used to place the authentication request
+ VerificationResult verification = manager.verify(receivingUrl.toString(), openidResponse, discovered);
+
+ // examine the verification result and extract the verified identifier
+ Identifier verified = verification.getVerifiedId();
+
+ if (verified != null) {
+ vcontext.put(USERNAME_VCONTEXT_KEY,
+ context.getWiki().getUserName(context.getDoc().getFullName(), context));
+
+ // check if this OpenID is already registered
+ if (OpenIdHelper.findUser(verified.getIdentifier(), context) != null) {
+ vcontext.put(OPENID_IDENTIFIER_VCONTEXT_KEY, verified.getIdentifier());
+ return -3;
+ }
+ vcontext.put(OPENID_IDENTIFIER_VCONTEXT_KEY, verified.getIdentifier());
+
+ // Check if the user has already attached an OpenID
+ if (context.getDoc().getObject(XWIKI_OPENID_IDENTIFIER_CLASSNAME) != null) {
+ vcontext.put("current_openid_identifier",
+ context.getDoc().getStringValue(XWIKI_OPENID_IDENTIFIER_CLASSNAME, "identifier"));
+ return -1;
+ }
+
+ // Ask the user for confirmation
+ return 1;
+ } else {
+ // authentication failed, show and log error message
+ if (openidResponse.getParameter(OPENID_MODE_PARAMETER) != null
+ && StringUtils.equals("cancel", openidResponse.getParameter(OPENID_MODE_PARAMETER).getValue())) {
+ // OpenID discovery cancelled
+ return -4;
+ } else {
+ if (LOG.isInfoEnabled() && openidResponse.getParameter("error") != null) {
+ LOG.info("OpenID login failed (error: "
+ + openidResponse.getParameter("openid.error").getValue() + ")");
+ }
+ // OpenID discovery failed
+ return -5;
+ }
+ }
+ } catch (OpenIDException e) {
+ // Internal error
+ return -6;
+ }
+ }
+
+ /**
+ * Attaches an OpenID to an existing user. The method checks if the password is correct and if so the OpenID is
+ * attached to the user.
+ *
+ * @param context the XWiki context when attaching open Id.
+ * @return an status code describing the success or failure of the process
+ * @throws XWikiException when attaching Open Id to user failed.
+ */
+ protected int attachOpenID(XWikiContext context) throws XWikiException
+ {
+ XWikiRequest request = context.getRequest();
+ VelocityContext vcontext = (VelocityContext) context.get(VCONTEXT_CONTEXT_KEY);
+ String openidIdentifier = request.getParameter(OPENID_IDENTIFIER_VCONTEXT_KEY);
+
+ if (openidIdentifier == null || openidIdentifier.length() == 0) {
+ // No OpenID passed
+ return -2;
+ }
+
+ // Attach the OpenID
+ if (!OpenIdHelper.attachOpenIdToUser(context.getDoc(), openidIdentifier, context)) {
+ // Internal error
+ return -6;
+ }
+
+ // Successfully attached the OpenID to the user account
+ vcontext.put(USERNAME_VCONTEXT_KEY, context.getWiki().getUserName(context.getXWikiUser().getUser(), context));
+ vcontext.put(OPENID_IDENTIFIER_VCONTEXT_KEY, openidIdentifier);
+ return 2;
+ }
+}
Index: src/main/java/com/xpn/xwiki/web/OpenIdServerServlet.java
===================================================================
--- src/main/java/com/xpn/xwiki/web/OpenIdServerServlet.java (revision 0)
+++ src/main/java/com/xpn/xwiki/web/OpenIdServerServlet.java (revision 0)
@@ -0,0 +1,456 @@
+/*
+ * See the NOTICE file distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * 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 com.xpn.xwiki.web;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import com.xpn.xwiki.XWiki;
+import com.xpn.xwiki.XWikiContext;
+import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.doc.XWikiDocument;
+import com.xpn.xwiki.plugin.openid.OpenIdHelper;
+import com.xpn.xwiki.user.api.XWikiUser;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.VelocityContext;
+
+import org.openid4java.consumer.ConsumerException;
+import org.openid4java.message.DirectError;
+import org.openid4java.message.Message;
+import org.openid4java.message.ParameterList;
+import org.openid4java.server.ServerManager;
+import org.xwiki.container.Container;
+import org.xwiki.container.servlet.ServletContainerInitializer;
+import org.xwiki.context.Execution;
+import org.xwiki.velocity.VelocityManager;
+
+/**
+ * Action that implements the OpenID server support.
+ *
+ * @since 2.3-milestone-2
+ * @version $Id$
+ */
+public final class OpenIdServerServlet extends HttpServlet
+{
+ /** The log used by this class. */
+ private static final Log log = LogFactory.getLog(OpenIdServerServlet.class);
+
+ /** The name of the velocity template rendererd by this servlet. */
+ private String template = "attach_openid";
+
+ /** The address to use as a home page where the users are redirected. */
+ private String home = "bin/view/Main/";
+
+ /**
+ * {@inheritDoc}
+ *
+ * @see javax.servlet.GenericServlet#init()
+ */
+ @Override
+ public void init() throws ServletException
+ {
+ super.init();
+ // TODO: we cannot use the XWiki API to determine the right URL, because this is a servlet and the core
+ // is reachable mainly from Struts. Getting access to the core requires too much duplication, so for the
+ // moment we're going the easy way: hardcoded values.
+ String homeParameter = getInitParameter("homePage");
+ if (homeParameter != null) {
+ this.home = homeParameter;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ handleRequest(request, response);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ handleRequest(request, response);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void doHead(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ handleRequest(request, response);
+ }
+
+ /**
+ * Sets up a context for the request. It's more or less the same as
+ * {@link com.xpn.xwiki.web.XWikiAction#action(XWikiContext)}.
+ *
+ * @param request
+ * @param response
+ * @throws ServletException
+ * @throws IOException
+ * @see
+ * @link com.xpn.xwiki.web.XWikiAction#action(XWikiContext)
+ */
+ protected void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException,
+ IOException
+ {
+ XWikiRequest xwiki_request = new XWikiServletRequest(request);
+ XWikiResponse xwiki_response = new XWikiServletResponse(response);
+ XWikiContext context = null;
+
+ try {
+ context =
+ Utils.prepareContext("openid", xwiki_request, xwiki_response, new XWikiServletContext(this
+ .getServletContext()));
+
+ // Initialize the Container component which is the new of transporting the Context in the new
+ // component architecture.
+ ServletContainerInitializer containerInitializer =
+ (ServletContainerInitializer) Utils.getComponent(ServletContainerInitializer.class);
+
+ containerInitializer.initializeRequest(context.getRequest().getHttpServletRequest(), context);
+ containerInitializer.initializeResponse(context.getResponse().getHttpServletResponse());
+ containerInitializer.initializeSession(context.getRequest().getHttpServletRequest());
+
+ // Verify that the requested wiki exists
+ XWiki xwiki;
+ try {
+ xwiki = XWiki.getXWiki(context);
+ } catch (XWikiException e) {
+ // Should we output an error message here?
+ return;
+ }
+
+ XWikiURLFactory urlf = xwiki.getURLFactoryService().createURLFactory(context.getMode(), context);
+ context.setURLFactory(urlf);
+
+ context.put("ajax", false);
+
+ VelocityManager velocityManager = (VelocityManager) Utils.getComponent(VelocityManager.class);
+ VelocityContext vcontext = velocityManager.getVelocityContext();
+
+ XWikiDocument doc;
+ context.getWiki().prepareResources(context);
+ context.getWiki().setPhonyDocument("openid", context, vcontext);
+ doc = context.getDoc();
+
+ context.put("doc", doc);
+ vcontext.put("doc", doc.newDocument(context));
+ vcontext.put("cdoc", vcontext.get("doc"));
+ XWikiDocument tdoc = doc.getTranslatedDocument(context);
+ context.put("tdoc", tdoc);
+ vcontext.put("tdoc", tdoc.newDocument(context));
+
+ // Execute business logic
+ if (action(context) == true)
+ render(context);
+
+ // Let's make sure we have flushed content and closed
+ try {
+ context.getResponse().getWriter().flush();
+ } catch (Throwable e) {
+ // This might happen if the connection was closed, for example.
+ // If we can't flush, then there's nothing more we can send to the client.
+ }
+
+ // Make sure we cleanup database connections
+ // There could be cases where we have some
+ if ((context != null) && (xwiki != null)) {
+ xwiki.getStore().cleanUp(context);
+ }
+
+ } catch (Exception e) {
+ // do nothing...
+ } finally {
+
+ if (context != null) {
+ // Cleanup components
+ Container container = (Container) Utils.getComponent(Container.class);
+ Execution execution = (Execution) Utils.getComponent(Execution.class);
+
+ // We must ensure we clean the ThreadLocal variables located in the Container and Execution
+ // components as otherwise we will have a potential memory leak.
+ container.removeRequest();
+ container.removeResponse();
+ container.removeSession();
+ execution.removeContext();
+ }
+ }
+ }
+
+ /**
+ * Executes all actions.
+ *
+ * @param context the used context
+ * @return TRUE
if a template should be rendered, FALSE
if no template should be
+ * rendered or a redirect was sent.
+ * @throws XWikiException
+ */
+ private boolean action(XWikiContext context) throws XWikiException
+ {
+ XWikiRequest request = context.getRequest();
+ XWikiResponse response = context.getResponse();
+ boolean enabled = "1".equals(context.getWiki().Param("xwiki.authentication.openid.server.enabled"));
+
+ // Check if a XRDS document was requested
+ String req_user = request.getRequestURI().substring(request.getContextPath().length() + 8);
+ if ("xrds.xml".equals(req_user)) {
+ outputXRDS(null, context); // global XRDS document
+ return true;
+ } else if ("xrds.xml".equals(req_user.substring(req_user.indexOf("/") + 1))) {
+ outputXRDS(req_user.substring(0, req_user.indexOf("/")), context); // user XRDS document
+ return true;
+ } else if (req_user.length() > 0 && req_user.contains("/") == false) {
+ showUserPage(req_user, context);
+ return true;
+ }
+
+ ServerManager manager = null;
+ try {
+ manager = OpenIdHelper.getServerManager(context);
+ } catch (ConsumerException e) {
+ if (log.isInfoEnabled()) {
+ log.error("Couldn't get a ServerManager instance.");
+ }
+
+ template = "exception";
+ return true;
+ }
+
+ ParameterList request_parameters = new ParameterList(request.getParameterMap());
+ Message response_message;
+ String response_text = null;
+
+ String mode = request.getParameter("openid.mode");
+ if (enabled == false || request_parameters.getParameters().size() == 0) {
+ // No OpenID action, show homepage and publish XRDS location via HTTP header
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ vcontext.put("enabled", enabled);
+ vcontext.put("openid_server_url", OpenIdHelper.getOpenIdServerURL(context));
+
+ response.setHeader("X-XRDS-Location", OpenIdHelper.getOpenIdServerURL(context) + "xrds.xml");
+ template = "openid_server_homepage";
+ return true;
+
+ } else if ("associate".equals(mode)) {
+ // Process an association request
+ response_message = manager.associationResponse(request_parameters);
+ response_text = response_message.keyValueFormEncoding();
+
+ } else if ("checkid_setup".equals(mode)) {
+ // Process an authentication request setup request
+ return processCheckIdSetup(manager, context);
+
+ } else if ("checkid_immediate".equals(mode)) {
+ // Immediate authentication fails always since we don't support it at the moment
+ response_message =
+ manager.authResponse(request_parameters, request.getParameter("openid.claimed_id"), request
+ .getParameter("openid.identity"), false);
+
+ sendRedirect(response, response_message.getDestinationUrl(true));
+ return false;
+
+ } else if ("check_authentication".equals(mode)) {
+ // Processing a verification request
+ response_message = manager.verify(request_parameters);
+ response_text = response_message.keyValueFormEncoding();
+
+ } else {
+ // Error response
+ response_message = DirectError.createDirectError("Unknown request");
+ response_text = response_message.keyValueFormEncoding();
+ }
+
+ // Try to directly output the response
+ try {
+ context.getResponse().getWriter().write(response_text);
+ return false;
+ } catch (IOException e) {
+ if (log.isInfoEnabled()) {
+ log.error("Couldn't output the response.");
+ }
+
+ template = "exception";
+ return true;
+ }
+ }
+
+ /**
+ * Renders the template and outputs it.
+ *
+ * @param context the context
+ * @throws XWikiException
+ */
+ protected void render(XWikiContext context) throws XWikiException
+ {
+ String page = Utils.getPage(context.getRequest(), template);
+ Utils.parseTemplate(page, true, context);
+ }
+
+ /**
+ * Helper method to process checkid_setup requests.
+ *
+ * @param manager the OpenID server manager instance
+ * @param context the context
+ * @return TRUE
if a template should be rendered, FALSE
if no template should be
+ * rendered or a redirect was sent.
+ * @throws XWikiException
+ */
+ private boolean processCheckIdSetup(ServerManager manager, XWikiContext context) throws XWikiException
+ {
+ XWikiRequest request = context.getRequest();
+ XWikiResponse response = context.getResponse();
+ ParameterList request_parameters = new ParameterList(request.getParameterMap());
+ Message response_message;
+
+ // First check if the user is logged in. If not show the login form
+ XWikiUser user = context.getWiki().checkAuth(context);
+ if ((user == null) && ((user = context.getXWikiUser()) == null)) {
+ context.getWiki().getAuthService().showLogin(context);
+ return false;
+ }
+
+ if (request.getParameter("confirmation") == null) {
+ // ask the user to confirm the authentication request
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ vcontext.put("openid_server_url", OpenIdHelper.getOpenIdServerURL(context));
+ vcontext.put("openid_identifier", OpenIdHelper.getUserOpenId(user.getUser(), context));
+ if (request_parameters.hasParameter("openid.realm")) {
+ vcontext.put("site", request_parameters.getParameterValue("openid.realm"));
+ } else {
+ vcontext.put("site", request_parameters.getParameterValue("openid.return_to"));
+ }
+ template = "confirm_openid_authentication_request";
+
+ // store the request parameters because they are needed to create the response afterwards
+ request.getSession().setAttribute("openid-authentication-parameterlist", request_parameters);
+ return true;
+
+ } else if (request.getParameter("login") != null) {
+ // the user confirmed the authentication request
+ String identity = OpenIdHelper.getUserOpenId(user.getUser(), context);
+ String claimed_id = OpenIdHelper.getUserOpenId(user.getUser(), context);
+
+ request_parameters =
+ (ParameterList) request.getSession().getAttribute("openid-authentication-parameterlist");
+ request.getSession().removeAttribute("openid-authentication-parameterlist");
+
+ response_message = manager.authResponse(request_parameters, identity, claimed_id, true);
+ sendRedirect(response, response_message.getDestinationUrl(true));
+ return false;
+
+
+ } else {
+ // the user cancelled the authentication request
+ String identity = OpenIdHelper.getUserOpenId(user.getUser(), context);
+ String claimed_id = OpenIdHelper.getUserOpenId(user.getUser(), context);
+
+ request_parameters =
+ (ParameterList) request.getSession().getAttribute("openid-authentication-parameterlist");
+ request.getSession().removeAttribute("openid-authentication-parameterlist");
+
+ response_message = manager.authResponse(request_parameters, identity, claimed_id, false);
+ sendRedirect(response, response_message.getDestinationUrl(true));
+ return false;
+ }
+ }
+
+ /**
+ * Helper method to output the gloabal or a user specific XRDS document.
+ *
+ * @param req_user username if the XRDS of a specific user is requested, otherwise null
.
+ * @param context
+ * @return
+ */
+ private void outputXRDS(String username, XWikiContext context) throws XWikiException
+ {
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ vcontext.put("openid_server_url", OpenIdHelper.getOpenIdServerURL(context));
+
+ // Set the action to openid_xrds so that com.xpn.xwiki.web.Utils#parseTemplate uses the right content type
+ context.setAction("openid_xrds");
+
+ if (username == null) {
+ // Output XRDS file
+ template = "openid_xrds";
+ } else {
+ // Output user XRDS file
+ template = "openid_user_xrds";
+ vcontext.put("openid_identifier", OpenIdHelper.getUserOpenId("XWiki." + username, context));
+ }
+ }
+
+ /**
+ * Helper method to display an user profile page.
+ *
+ * @param username the username
+ * @param context the context
+ * @throws XWikiException
+ */
+ private void showUserPage(String username, XWikiContext context) throws XWikiException
+ {
+ // Publish the XRDS location via HTTP header
+ context.getResponse().setHeader("X-XRDS-Location",
+ OpenIdHelper.getOpenIdServerURL(context) + username + "/xrds.xml");
+
+
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ username = "XWiki." + username;
+ vcontext.put("username", context.getWiki().getUserName(username, context));
+
+ template = "openid_user_profile";
+ }
+
+ /**
+ * Redirects to another page.
+ *
+ * @param response XWikiResponse object
+ * @param page redirection target
+ * @throws XWikiException
+ */
+ private void sendRedirect(XWikiResponse response, String page) throws XWikiException
+ {
+ try {
+ if (page != null) {
+ response.sendRedirect(page);
+ }
+ } catch (IOException e) {
+ Object[] args = {page};
+ throw new XWikiException(XWikiException.MODULE_XWIKI_APP,
+ XWikiException.ERROR_XWIKI_APP_REDIRECT_EXCEPTION,
+ "Exception while sending redirect to page {0}",
+ e,
+ args);
+ }
+ }
+}
Index: src/main/java/com/xpn/xwiki/user/impl/xwiki/XWikiAuthServiceImpl.java
===================================================================
--- src/main/java/com/xpn/xwiki/user/impl/xwiki/XWikiAuthServiceImpl.java (revision 27619)
+++ src/main/java/com/xpn/xwiki/user/impl/xwiki/XWikiAuthServiceImpl.java (working copy)
@@ -550,12 +550,18 @@
try {
boolean result = false;
XWikiDocument doc = context.getWiki().getDocument(username, context);
- // We only allow empty password from users having a XWikiUsers object.
- if (doc.getObject("XWiki.XWikiUsers") != null) {
+ if (doc.getObject("XWiki.OpenIdIdentifier") != null) {
+ // For users having an OpenID the password doesn't need to be adjusted because it is set to the current
+ // value during the login process
String passwd = doc.getStringValue("XWiki.XWikiUsers", "password");
+ result = (password.equals(passwd));
+ }
+ if (result == false && doc.getObject("XWiki.XWikiUsers") != null) {
+ // We only allow empty password from users having a XWikiUsers object.
+ String passwd = doc.getStringValue("XWiki.XWikiUsers", "password");
password =
- ((PasswordClass) context.getWiki().getClass("XWiki.XWikiUsers", context).getField("password")).getEquivalentPassword(
- passwd, password);
+ ((PasswordClass) context.getWiki().getClass("XWiki.XWikiUsers", context).getField("password"))
+ .getEquivalentPassword(passwd, password);
result = (password.equals(passwd));
}
Index: src/main/java/com/xpn/xwiki/user/impl/xwiki/MyFormAuthenticator.java
===================================================================
--- src/main/java/com/xpn/xwiki/user/impl/xwiki/MyFormAuthenticator.java (revision 27619)
+++ src/main/java/com/xpn/xwiki/user/impl/xwiki/MyFormAuthenticator.java (working copy)
@@ -23,6 +23,7 @@
import java.io.IOException;
import java.security.Principal;
+import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -30,6 +31,14 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.apache.velocity.VelocityContext;
+import org.openid4java.OpenIDException;
+import org.openid4java.consumer.ConsumerManager;
+import org.openid4java.consumer.VerificationResult;
+import org.openid4java.discovery.DiscoveryInformation;
+import org.openid4java.discovery.Identifier;
+import org.openid4java.message.AuthRequest;
+import org.openid4java.message.ParameterList;
import org.securityfilter.authenticator.Authenticator;
import org.securityfilter.authenticator.FormAuthenticator;
import org.securityfilter.filter.SecurityRequestWrapper;
@@ -38,7 +47,9 @@
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
+import com.xpn.xwiki.plugin.openid.OpenIdHelper;
import com.xpn.xwiki.web.SavedRequestRestorerFilter;
+import com.xpn.xwiki.web.XWikiServletURLFactory;
public class MyFormAuthenticator extends FormAuthenticator implements XWikiAuthenticator
{
@@ -124,7 +135,7 @@
}
} catch (Exception e) {
// in case of exception we continue on Form Auth.
- // we don't want this to interfere with the most common behavior
+ // we don't want this to interfere with the most common behaviour
}
// process any persistent login information, if user is not already logged in,
@@ -133,7 +144,6 @@
String username =
convertUsername(this.persistentLoginManager.getRememberedUsername(request, response), context);
String password = this.persistentLoginManager.getRememberedPassword(request, response);
-
Principal principal = request.getUserPrincipal();
// 1) if user is not already authenticated, authenticate
@@ -165,13 +175,27 @@
}
}
- // process login form submittal
+ // process login form data
if ((this.loginSubmitPattern != null) && request.getMatchableURL().endsWith(this.loginSubmitPattern)) {
- String username = convertUsername(request.getParameter(FORM_USERNAME), context);
- String password = request.getParameter(FORM_PASSWORD);
- String rememberme = request.getParameter(FORM_REMEMBERME);
- rememberme = (rememberme == null) ? "false" : rememberme;
- return processLogin(username, password, rememberme, request, response, context);
+ if (request.getParameter("authentication_method") != null
+ && request.getParameter("authentication_method").equalsIgnoreCase("openid")) {
+ // OpenID login
+ String openid_identifier = request.getParameter("openid_identifier");
+ String rememberme = request.getParameter(FORM_REMEMBERME);
+ rememberme = (rememberme == null) ? "false" : rememberme;
+ return processOpenIdLogin(openid_identifier, rememberme, request, response, context);
+ } else if (request.getParameter("authentication_method") != null
+ && request.getParameter("authentication_method").equalsIgnoreCase("useraccount")) {
+ // Normal user account login
+ String username = convertUsername(request.getParameter(FORM_USERNAME), context);
+ String password = request.getParameter(FORM_PASSWORD);
+ String rememberme = request.getParameter(FORM_REMEMBERME);
+ rememberme = (rememberme == null) ? "false" : rememberme;
+ return processLogin(username, password, rememberme, request, response, context);
+ } else if (request.getParameter("openid.mode") != null) {
+ // OpenID: OP response
+ return processOpenIdLoginResponse(request, response, context);
+ }
}
return false;
}
@@ -181,6 +205,9 @@
* abort further processing after the method completes (for example, if a redirect was sent as part of the login
* processing).
*
+ * @param username
+ * @param password
+ * @param rememberme
* @param request
* @param response
* @return true if the filter should return after this method ends, false otherwise
@@ -195,7 +222,8 @@
LOG.info("User " + principal.getName() + " has been logged-in");
}
- // invalidate old session if the user was already authenticated, and they logged in as a different user
+ // invalidate old session if the user was already authenticated, and they logged in as a
+ // different user
if (request.getUserPrincipal() != null && !username.equals(request.getRemoteUser())) {
request.getSession().invalidate();
}
@@ -221,7 +249,8 @@
Boolean bAjax = (Boolean) context.get("ajax");
if ((bAjax == null) || (!bAjax.booleanValue())) {
String continueToURL = getContinueToURL(request);
- // This is the url that the user was initially accessing before being prompted for login.
+ // This is the url that the user was initially accessing before being prompted for
+ // login.
response.sendRedirect(response.encodeRedirectURL(continueToURL));
}
} else {
@@ -247,11 +276,178 @@
}
/**
+ * Processes an OpenID login. It redirects the user as part of a normal OpenID login process to the OpenID provider.
+ * The response is handled by {@link MyFormAuthenticator#processOpenIdLoginResponse processOpenIdLoginResponse}.
+ * Returns true if SecurityFilter should abort further processing after the method completes (for example, if a
+ * redirect was sent as part of the login processing which is the normal behaviour).
+ *
+ * @param openid_identifier the OpenID identifier
+ * @param rememberme "true"
if the login should be persistent, null
or
+ * "false"
otherwise.
+ * @param request the request object
+ * @param response the response object
+ * @return true if the filter should return after this method ends, false otherwise
+ */
+ public boolean processOpenIdLogin(String openid_identifier, String rememberme, SecurityRequestWrapper request,
+ HttpServletResponse response, XWikiContext context) throws Exception
+ {
+ if (openid_identifier == null || openid_identifier.trim().equals("")) {
+ context.put("message", "noopenid");
+ return false;
+ }
+
+ try {
+ ConsumerManager manager = OpenIdHelper.getConsumerManager();
+
+ String return_to_url =
+ context.getWiki().getExternalURL("XWikiLogin", "loginsubmit", "rememberme=" + rememberme, context);
+
+ List discoveries = manager.discover(openid_identifier);
+ DiscoveryInformation discovered = manager.associate(discoveries);
+
+ // store the discovery information in the user's session
+ request.getSession().setAttribute("openid-discovery", discovered);
+
+ AuthRequest auth_request = manager.authenticate(discovered, return_to_url);
+
+ // set the realm
+ auth_request.setRealm(((XWikiServletURLFactory) context.getURLFactory()).getServerURL(context).toString()
+ + ((XWikiServletURLFactory) context.getURLFactory()).getContextPath());
+
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Redirecting user to OP (OpenID identifier: " + openid_identifier + ")");
+ }
+
+ if (discovered.isVersion2()) {
+ // OpenID 2.0 supports HTML FORM Redirection which allows payloads >2048 bytes
+ VelocityContext vcontext = (VelocityContext) context.get("vcontext");
+ vcontext.put("op_endpoint", auth_request.getDestinationUrl(false));
+ vcontext.put("openid_parameters", auth_request.getParameterMap());
+
+ String redirect_form = context.getWiki().parseTemplate("openid_form_redirect.vm", context);
+
+ response.getOutputStream().print(redirect_form);
+
+ // Close the output stream - otherwise the LOGin form documented is also written to it
+ response.getOutputStream().close();
+ } else {
+ // The only method supported in OpenID 1.x is a HTTP-redirect (GET) to the OpenID Provider endpoint (the
+ // redirect-URL usually limited ~2048 bytes)
+ response.sendRedirect(auth_request.getDestinationUrl(true));
+ }
+ } catch (OpenIDException e) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("OpenID discovery failed: " + e.getMessage());
+ }
+
+ // present error to the user
+ context.put("message", "LOGinfailed");
+ }
+
+ return true;
+ }
+
+ /**
+ * Processes the response of an OpenID provider to complete the LOGin process. Checks the response of the OP and in
+ * case of success it LOGs in the user. Otherwise an error message is put into the context and shown to the user
+ * afterwards.
+ *
+ * @param request the request object
+ * @param response the response object
+ * @param context the context
+ * @return true if the filter should return after this method ends, false otherwise
+ */
+ public boolean processOpenIdLoginResponse(SecurityRequestWrapper request, HttpServletResponse response,
+ XWikiContext context) throws Exception
+ {
+ try {
+ ConsumerManager manager = OpenIdHelper.getConsumerManager();
+
+ // extract the parameters from the authentication response which come in as a HTTP request from the OpenID
+ // provider
+ ParameterList openid_response = new ParameterList(request.getParameterMap());
+
+ // retrieve the previously stored discovery information
+ DiscoveryInformation discovered =
+ (DiscoveryInformation) request.getSession().getAttribute("openid-discovery");
+
+ // verify the response
+ StringBuffer receivingURL = request.getRequestURL();
+ String queryString = request.getQueryString();
+ if (queryString != null && queryString.length() > 0)
+ receivingURL.append("?").append(request.getQueryString());
+
+ VerificationResult verification = manager.verify(receivingURL.toString(), openid_response, discovered);
+ Identifier verified = verification.getVerifiedId();
+
+ if (verified != null) {
+ String username = OpenIdHelper.findUser(verified.getIdentifier(), context);
+
+ if (username == null) {
+ // no user was found for this OpenID identifier
+ if (LOG.isInfoEnabled()) {
+ LOG.info("No user for OpenID " + verified.getIdentifier() + " found.");
+ }
+
+ context.put("message", "openid_not_associated");
+ response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+ return true;
+ }
+
+ // The current authentication mechanisms is implemented in a very restrictive manner, we have to
+ // retrieve the user password (which is generated randomly during registration for OpenID users) and use
+ // it in order to authenticate the user.
+ String password = OpenIdHelper.getOpenIdUserPassword(verified.getIdentifier(), context);
+ Principal principal = authenticate(username, password, context);
+ if (principal != null) {
+ // invalidate old session if the user was already authenticated and LOGged in as a different user
+ if (request.getUserPrincipal() != null) {
+ request.getSession().invalidate();
+ }
+
+ // manage persistent LOGin info if persistent LOGin management is enabled
+ String rememberme = request.getParameter(FORM_REMEMBERME);
+ rememberme = (rememberme == null) ? "false" : rememberme;
+
+ if (this.persistentLoginManager != null) {
+ if (rememberme != null) {
+ this.persistentLoginManager.rememberLogin(request, response, username, password);
+ } else {
+ this.persistentLoginManager.forgetLogin(request, response);
+ }
+ }
+
+ request.setUserPrincipal(principal);
+
+ String continueToURL = getContinueToURL(request);
+ response.sendRedirect(response.encodeRedirectURL(continueToURL));
+ }
+ } else {
+ // authentication failed, show and LOG error message
+ if (openid_response.getParameter("openid.mode") != null
+ && openid_response.getParameter("openid.mode").getValue().equals("cancel")) {
+ context.put("message", "openidLOGin_cancelled");
+ } else {
+ if (LOG.isInfoEnabled() && openid_response.getParameter("error") != null) {
+ LOG.info("OpenID LOGin failed (error: "
+ + openid_response.getParameter("openid.error").getValue() + ")");
+ }
+ context.put("message", "LOGinfailed");
+ }
+ }
+ } catch (OpenIDException e) {
+ context.put("message", "LOGinfailed");
+ }
+
+ return true;
+ }
+
+ /**
* FormAuthenticator has a special case where the user should be sent to a default page if the user spontaneously
- * submits a login request.
+ * submits a LOGin request.
*
* @param request
- * @return a URL to send the user to after logging in
+ * @return a URL to send the user to after LOGging in
*/
private String getContinueToURL(HttpServletRequest request)
{
Index: src/main/resources/queries.hbm.xml
===================================================================
--- src/main/resources/queries.hbm.xml (revision 27619)
+++ src/main/resources/queries.hbm.xml (working copy)
@@ -29,4 +29,7 @@
select distinct doc.fullName from XWikiDocument as doc, BaseObject as obj
where obj.name=doc.fullName and obj.className='XWiki.XWikiUsers'
+
+ select doc.fullName from XWikiDocument as doc, BaseObject as obj, StringProperty as prop where doc.fullName=obj.name and obj.className='XWiki.OpenIdIdentifier' and obj.id=prop.id.id and prop.id.name='identifier' and prop.value=:identifier
+
Index: src/main/resources/ApplicationResources.properties
===================================================================
--- src/main/resources/ApplicationResources.properties (revision 27619)
+++ src/main/resources/ApplicationResources.properties (working copy)
@@ -407,11 +407,59 @@
editFullScreen=Full Screen
#login
+login_username_password_desc=with your username and password
+login_openid_desc=... or with your OpenID
nousername=No user name given
+noopenid=Please enter your OpenID identifier to log in
nopassword=No password given
invalidcredentials=Invalid credentials
loginfailed=Internal error
+openidlogin_cancelled=The login process was cancelled at the OpenID provider site
+openid_not_associated=No user was found for this OpenID
+#OpenID
+openid.common.discovery_cancelled=The login process was cancelled at the OpenID provider site
+openid.common.discovery_failed=The discovery of the OpenID failed
+openid.common.identifier_field_label=OpenID
+
+#OpenID form redirect
+openid.form_redirect.title=OpenID login in progress
+openid.form_redirect.description=You are redirected to your OpenID provider in a few seconds. Please wait...
+openid.form_redirect.button=Continue...
+
+#OpenID registration form
+openid.registration.welcome=If you have an OpenID or i-name you can speed-up your registration by entering it here
+openid.registration.discovery_submit=Register with OpenID
+openid.registration.confirmation=Verify the form below and complete your OpenID registration
+openid.registration.confirmation_submit=Create OpenID account
+openid.registration.invalidOpenID=Enter a valid OpenID to continue with registration
+openid.registration.openIdAlreadyRegistered=The OpenID "{0}" is already registered. You can directly log in if it is in your possession.
+openid.registration.successful=Successfully registered user {0} ({1}).
+
+#attach OpenID to existing user
+openid.attach.title=Associate an OpenID with an existing user account
+openid.attach.welcome=Enter the OpenID to attach to the user account.
+openid.attach.confirm=Verify the data below and confirm the action to attach the shown OpenID to the user account.
+openid.attach.user=User
+openid.attach.submit=Attach OpenID
+openid.attach.alreadyAttachedOpenId=This user account has already attached the OpenID "{0}". If you continue it will be replaced.
+openid.attach.replaceAttachedOpenId=Replace the attached OpenID ({0})
+openid.attach.openIdAlreadyRegistered=The OpenID "{0}" is already registered with another user account. You can directly log in if it is in your possession.
+openid.attach.internalError=An internal error occurred and the OpenID could not be associated with you user account. Please contact the administrator.
+openid.attach.successful=Successfully attached the OpenID "{0}" to {1}.
+
+#OpenID server
+openid.server.homepage.title=XWiki OpenID
+openid.server.homepage.enabled_description=This is the homepage of XWiki's OpenID provider support. Every user can login on sites supporting OpenID without the need for yet another passwords.
+openid.server.homepage.disabled_description=OpenID provider support is disabled in this installation.
+openid.server.authentication_request.title=OpenID Authentication
+openid.server.authentication_request.desc=Would you like to log in on the site
{0}
with your OpenID
{1}
+openid.server.authentication_request.site_label=Site
+openid.server.authentication_request.login=Login
+openid.server.authentication_request.cancel=Cancel
+openid.server.user_profile.title=OpenID profile of {0}
+openid.server.user_profile.text=This is the OpenID profile page of {0}.
+
switchto=Switch to
sectionEdit=Sectional Editing
@@ -525,6 +573,8 @@
registerfailed=Registration has failed
registerfailedcode=code
registersuccessful=Registration successful
+register_openid_discovery_cancelled=The registration with OpenID was cancelled at the OpenID provider site
+register_openid_discovery_failed=The registration with OpenID failed due to an internal error
leftPanels=Left Panels
rightPanels=Right Panels
@@ -587,6 +637,7 @@
platform.core.profile.changePassword=Change password
platform.core.profile.changePhoto=Change photo
platform.core.profile.changePhoto.cancel=Cancel and return to profile
+platform.core.profile.attachOpenID=Attach an OpenID
platform.core.profile.firstname=First name
platform.core.profile.lastname=Last name
platform.core.profile.blog=Blog
@@ -704,6 +755,7 @@
core.comment.updateClassPropertyName=Updated class property name
core.comment.createdUser=Created user
core.comment.addedUserToGroup=Added user to group
+core.comment.addedOpenIdIdentifier=Added OpenID identifier
core.comment.rollback=Rollback to version {0}
core.comment.updateContent=Update Content
core.comment.uploadAttachmentComment=Upload new attachment {0}
Index: src/main/resources/JcrQueries.properties
===================================================================
--- src/main/resources/JcrQueries.properties (revision 27619)
+++ src/main/resources/JcrQueries.properties (working copy)
@@ -5,3 +5,4 @@
getAllPublicDocuments=//element(*, xwiki:document)/@fullName
listGroupsForUser=//element(*, xwiki:document)[obj/XWiki/XWikiGroups[@xp:member=:{username} or @xp:member=:{shortname} or @xp:member=:{veryshortname}]/@fullName
getAllUsers=//element(*, xwiki:document)[obj/XWiki/XWikiUsers]/@fullName
+getUserDocByOpenIdIdentifier=//element(*, xwiki:document)[obj/XWiki/OpenIdIdentifier/@xp:identifier=:{identifier}]/@fullName
Index: pom.xml
===================================================================
--- pom.xml (revision 27619)
+++ pom.xml (working copy)
@@ -319,6 +319,13 @@
2.0
+
+
+ org.openid4java
+ openid4java
+ 0.9.3
+
+
dom4j
@@ -1372,6 +1379,8 @@
**/web/Utils.java,
**/web/TexAction.java,
**/web/XWikiResponse.java,
+ **/web/OpenIdServerServlet.java,
+ **/web/AttachOpenIdAction.java,
**/atom/WSSEHttpHeader.java,
**/atom/lifeblog/LifeblogServices.java,
**/atom/lifeblog/UserBlog.java,