package votorola.a.web.wic.authen; // Copyright 2008-2010, 2012, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. import java.sql.SQLException; import javax.mail.internet.*; import org.apache.wicket.AttributeModifier; import org.apache.wicket.markup.html.basic.*; import org.apache.wicket.markup.html.form.*; import org.apache.wicket.model.*; import org.apache.wicket.request.cycle.*; import org.apache.wicket.validation.*; import org.openid4java.discovery.*; import votorola.a.*; import votorola.g.locale.*; import votorola.a.trust.*; import votorola.a.voter.*; import votorola.a.web.wic.*; import votorola.g.*; import votorola.g.lang.*; import votorola.g.logging.*; import votorola.g.mail.*; import votorola.g.web.wic.*; /** A page for the authentication of a user's email address, step 1. On this page, the * user inputs the email address to authenticate. * * @see WP_EmailAuthen1.html */ @ThreadRestricted("wicket") final class WP_EmailAuthen1 extends VPageHTML { /** Either logs the user in following on a successful OpenID authentication, or * constructs a WP_EmailAuthen1 in order to continue the login attempt. * * @param verifiedID the {@linkplain * org.openid4java.consumer.VerificationResult#getVerifiedId() verified * identifier} from the positive authentication result, per {@linkplain * UserSettings#setOpenID(Identifier) setOpenID}(verifiedID). * @param runner the request cycle runner to handle any post-authentication * processing. * * @return null if the user is logged in as a result; otherwise a new instance of * WP_EmailAuthen1. * * @see WP_OpenIDLogin#isPersistent() * @see WP_OpenIDLogin#isReauthenticationRequested() */ static WP_EmailAuthen1 login( final Identifier verifiedID, final boolean persistent, final boolean reauthenticationRequested, final RequestCycleRunner runner ) { final String openID = verifiedID.getIdentifier(); if( !reauthenticationRequested ) { final VRequestCycle cycle = VRequestCycle.get(); final VoteServer.Run vsRun = VOWicket.get().vsRun(); final UserSettings settings; try { settings = UserSettings.forOpenID( openID, vsRun.userTable() ); } catch( Exception x ) { throw VotorolaRuntimeException.castOrWrapped( x ); } if( settings != null ) { WP_OpenIDLogin.setUserInSession( settings.voterEmail(), "OpenID", persistent, cycle ); return null; } // else OpenID not yet associated, proceed with email authentication: } return new WP_EmailAuthen1( openID, persistent, new OpenIDSetter( verifiedID, runner )); } private WP_EmailAuthen1( final String openID, boolean _persistent, final RequestCycleRunner rcr ) { persistent = _persistent; runner = rcr; final VRequestCycle cycle = VRequestCycle.get(); final BundleFormatter bun = cycle.bunW(); add( new Label( "title", bun.l( "a.web.wic.authen.WP_EmailAuthen1" ) )); add( new Label( "explanation", bun.l( "a.web.wic.authen.WP_EmailAuthen1.explanation" ))); final Form y = new EmailAddressForm(); add( y ); // OpenID // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - y.add( new Label( "openIDLabel", bun.l( "a.web.wic.authen.WP_Login.openid_identifier" ))); final TextField openidField = new TextField( "openID", new Model( openID )); openidField.setEnabled( false ); y.add( openidField ); y.add( new Label( "openIDDescription", bun.l( "a.web.wic.authen.WP_EmailAuthen1.openIDDescription" ))); // Email // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - y.add( new Label( "userEmailLabel", bun.l( "a.web.wic.authen.WP_EmailAuthen1.userEmail" ))); final TextField emailField = new TextField( "userEmail", new PropertyModel( WP_EmailAuthen1.this, "userEmailInput" )); invalidStyled( inputLengthConstrained( emailField )); emailField.setRequired( true ); emailField.add( new WicEmailAddressValidator() // extracting conversions from a validator / this is improper / consider instead using a converter / cf. votorola.g.util.regex.WicPatternConverter { public @Override void validate( IValidatable v ) { claimedUserIAddress = null; // till proven otherwise super.validate( v ); } public @Override void onSuccess( InternetAddress iAddress ) { claimedUserIAddress = iAddress; InternetAddressX.canonicalize( claimedUserIAddress ); } }); y.add( emailField ); y.add( new Label( "userEmailDescription", bun.l( "a.web.wic.authen.WP_EmailAuthen1.userEmailDescription" ))); // Buttons // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - { final Button button = new Button( "submit" ); button.add( AttributeModifier.replace( "value", bun.l( "a.web.wic.authen.WP_EmailAuthen1.submit" ))); y.add( button ); } { final Button button = new Button( "submit-cancel" ) { public @Override void onSubmit() { super.onSubmit(); runner.run( VRequestCycle.get() ); } }; button.add( AttributeModifier.replace( "value", bun.l( "a.web.wic.authen.WP_EmailAuthen1.submit-cancel" ))); button.setDefaultFormProcessing( false ); y.add( button ); } // Feedback Messages // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - add( new WC_Feedback( "feedback" )); } //// P r i v a t e /////////////////////////////////////////////////////////////////////// private transient InternetAddress claimedUserIAddress; // from field validator, in canonical form private final boolean persistent; private final RequestCycleRunner runner; private String userEmailInput; // PropertyModel accesses it by java.lang.reflect.Field.setAccessible() // ==================================================================================== private class EmailAddressForm extends Form { EmailAddressForm() { super( "form" ); } protected @Override void onSubmit() { super.onSubmit(); if( claimedUserIAddress == null ) throw new IllegalStateException(); final VRequestCycle cycle = VRequestCycle.get(); final WP_EmailAuthen2 authenticationPage = new WP_EmailAuthen2( claimedUserIAddress, persistent, cycle, runner ); cycle.setResponsePage( authenticationPage.sendMessage( cycle )? authenticationPage: new WP_Message() ); } } // ==================================================================================== private static final class OpenIDSetter extends RequestCycleRunnerW { private static final long serialVersionUID = 1L; private OpenIDSetter( final Identifier verifiedID, RequestCycleRunner runner ) { super( runner ); this.verifiedID = verifiedID; } private final VSession.User userAtAutheticationTime = VSession.get().user(); // null, unless user already logged in private final Identifier verifiedID; public @Override void run( RequestCycle wCycle ) { final VRequestCycle cycle = (VRequestCycle)wCycle; final VSession.User user = VSession.get().user(); if( user != null && user != userAtAutheticationTime ) // != intended { // login went to completion, not cancelled final UserSettings.Table table = VOWicket.get().vsRun().userTable(); try { UserSettings settings = UserSettings.forOpenID( verifiedID.getIdentifier(), table ); if( settings != null && !settings.voterEmail().equals( user.email() )) { table.delete( settings.voterEmail() ); // remove association with old email address settings = null; } if( settings == null ) { settings = new UserSettings( user.email(), table ); settings.setOpenID( verifiedID ); // associate this authenticated OpenID with the user, for next login settings.write( table, VSession.get() ); } } catch( Exception x ) { throw VotorolaRuntimeException.castOrWrapped( x ); } } super.run( wCycle ); } } }