/*
* Atricore IDBus
*
* Copyright (c) 2009, Atricore Inc.
*
* 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.atricore.idbus.capabilities.openid.ui.panel;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.form.AjaxFormValidatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.RequiredTextField;
import org.apache.wicket.markup.html.form.StatelessForm;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.request.http.handler.RedirectRequestHandler;
import org.apache.wicket.util.time.Duration;
import org.apache.wicket.util.value.ValueMap;
import org.apache.wicket.validation.validator.UrlValidator;
import org.atricore.idbus.capabilities.sso.main.claims.SSOCredentialClaimsRequest;
import org.atricore.idbus.capabilities.sso.support.auth.AuthnCtxClass;
import org.atricore.idbus.capabilities.sso.support.binding.SSOBinding;
import org.atricore.idbus.capabilities.sso.ui.components.GtFeedbackPanel;
import org.atricore.idbus.capabilities.sso.ui.page.authn.BaseSignInPanel;
import org.atricore.idbus.kernel.main.federation.metadata.EndpointDescriptor;
import org.atricore.idbus.kernel.main.mediation.*;
import org.atricore.idbus.kernel.main.mediation.camel.component.binding.AbstractMediationHttpBinding;
import org.atricore.idbus.kernel.main.mediation.claim.*;
import org.atricore.idbus.kernel.main.util.UUIDGenerator;
/**
* Sign-in panel for OpenID authentication for collecting user's OpenID.
*
* @author <a href="mailto:gbrigandi@atricore.org">Gianluca Brigandi</a>
*/
public class OpenIDSignInPanel extends BaseSignInPanel {
private static final Log logger = LogFactory.getLog(OpenIDSignInPanel.class);
private static final long serialVersionUID = 1L;
/**
* Field for user name.
*/
private RequiredTextField<String> openid;
private AjaxButton submit;
/**
* Error information
*/
protected FeedbackPanel feedbackPanel;
/**
* Error information
*/
protected WebMarkupContainer feedbackBox;
/**
* Sign in form.
*/
public final class OpenIDSignInForm extends StatelessForm<Void> {
private static final long serialVersionUID = 1L;
/**
* El-cheapo model for form.
*/
private final ValueMap properties = new ValueMap();
/**
* Constructor.
*
* @param id id of the form component
*/
public OpenIDSignInForm(final String id) {
super(id);
// Attach textfield components that edit properties map
// in lieu of a formal beans model
add(openid = new RequiredTextField<String>("openid", new PropertyModel<String>(properties,
"openid")));
openid.setType(String.class);
openid.add(new UrlValidator());
openid.add(new AjaxFormComponentUpdatingBehavior("onchange") {
@Override
protected void onUpdate(AjaxRequestTarget target) {
submit.setEnabled(true);
target.add(submit);
}
});
openid.setOutputMarkupId(true);
}
}
/**
* @param id See Component constructor
* @see org.apache.wicket.Component#Component(String)
*/
public OpenIDSignInPanel(final String id, SSOCredentialClaimsRequest credentialClaimsRequest, MessageQueueManager artifactQueueManager,
final IdentityMediationUnitRegistry idsuRegistry) {
super(id);
this.credentialClaimsRequest = credentialClaimsRequest;
this.artifactQueueManager = artifactQueueManager;
this.idsuRegistry = idsuRegistry;
}
@Override
protected void onInitialize() {
super.onInitialize();
// Create feedback panel and add to page
super.onInitialize();
// 1. Feedback Panel
feedbackBox = buildFeedbackBox();
feedbackPanel = (FeedbackPanel) feedbackBox.get("feedback");
// Add sign-in form to page, passing feedback panel as
// validation error handler
OpenIDSignInForm form = new OpenIDSignInForm("signInForm");
AjaxFormValidatingBehavior.addToAllFormComponents(form, "onkeyup", Duration.ONE_SECOND);
form.setOutputMarkupId(true);
// add a button that can be used to submit the form via ajax
submit = new AjaxButton("apply", form)
{
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form)
{
// repaint the feedback panel so that it is hidden
target.add(feedbackPanel);
try {
String claimsConsumerUrl = signIn(getOpenid());
onSignInSucceeded(claimsConsumerUrl);
} catch (Exception e) {
logger.error("Fatal error during signIn : " + e.getMessage(), e);
onSignInFailed();
}
}
@Override
protected void onError(AjaxRequestTarget target, Form<?> form)
{
// repaint the feedback panel so errors are shown
target.add(feedbackPanel);
}
};
submit.setEnabled(false);
form.add(submit);
add(form);
// If the form does not provide a feedbackBox, we should do it !
if (form.get("feedbackBox") == null)
add(feedbackBox);
}
/**
* Removes persisted form data for the signin panel (forget me)
*/
public final void forgetMe() {
// Remove persisted user data. Search for child component
// of type OpenIDSignInForm and remove its related persistence values.
// getPage().removePersistedFormData(OpenIDSignInForm.class, true);
}
/**
* Convenience method to access the openid.
*
* @return The user name
*/
public String getOpenid() {
return openid.getDefaultModelObjectAsString();
}
/**
* Convenience method set persistence for openid and password.
*
* @param enable Whether the fields should be persistent
*/
public void setPersistent(final boolean enable) {
// openid.setPersistent(enable);
}
/**
* Sign in user if possible.
*
* @param openid The openid
* @return True if signin was successful
*/
public String signIn(String openid) throws Exception {
UUIDGenerator idGenerator = new UUIDGenerator();
if (logger.isDebugEnabled()) logger.debug("Claims Request = " + credentialClaimsRequest);
ClaimSet claims = new ClaimSetImpl();
claims.addClaim(new CredentialClaimImpl("openid", openid));
//claims.addClaim(new ClaimImpl("rememberMe", cmd.isRememberMe()));
CredentialClaimsResponse responseCredential = new CredentialClaimsResponseImpl(idGenerator.generateId(),
null,
credentialClaimsRequest.getId(),
claims,
credentialClaimsRequest.getRelayState());
EndpointDescriptor claimsEndpoint = resolveClaimsEndpoint(credentialClaimsRequest, AuthnCtxClass.OPENID_AUTHN_CTX);
if (claimsEndpoint == null) {
logger.error("No claims endpoint found!");
// TODO : Create error and redirect to error view using 'IDBusErrArt'
}
if (!claimsEndpoint.getBinding().equals(SSOBinding.SSO_ARTIFACT.getValue())) {
logger.error("Invalid endpoint binding for claims response : " + claimsEndpoint.getBinding());
// TODO : Create error and redirect to error view using 'IDBusErrArt'
}
String claimsEndpointUrl = claimsEndpoint.getResponseLocation() != null ?
claimsEndpoint.getResponseLocation() : claimsEndpoint.getLocation();
if (logger.isDebugEnabled())
logger.debug("Using claims endpoint URL [" + claimsEndpointUrl + "]");
Artifact a = artifactQueueManager.pushMessage(responseCredential);
claimsEndpointUrl += "?SSOArt=" + a.getContent();
if (logger.isDebugEnabled())
logger.debug("Returning claims to " + claimsEndpointUrl);
return claimsEndpointUrl;
}
protected void onSignInFailed() {
// Try the component based localizer first. If not found try the
// application localizer. Else use the default
error(getLocalizer().getString("signInFailed", this, "Sign in failed"));
}
protected void onSignInSucceeded(String claimsConsumerUrl) {
getRequestCycle().scheduleRequestHandlerAfterCurrent(new RedirectRequestHandler(claimsConsumerUrl));
}
/**
* Build the container for error messages
*/
protected WebMarkupContainer buildFeedbackBox() {
// Create feedback panel and add it to page
feedbackBox = new WebMarkupContainer("feedbackBox");
feedbackPanel = new GtFeedbackPanel("feedback");
feedbackPanel.setOutputMarkupId(true);
feedbackBox.add(feedbackPanel);
return feedbackBox;
}
}