001package 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. 002 003import java.sql.SQLException; 004import javax.mail.internet.*; 005import org.apache.wicket.AttributeModifier; 006import org.apache.wicket.markup.html.basic.*; 007import org.apache.wicket.markup.html.form.*; 008import org.apache.wicket.model.*; 009import org.apache.wicket.request.cycle.*; 010import org.apache.wicket.validation.*; 011import org.openid4java.discovery.*; 012import votorola.a.*; 013import votorola.g.locale.*; 014import votorola.a.trust.*; 015import votorola.a.voter.*; 016import votorola.a.web.wic.*; 017import votorola.g.*; 018import votorola.g.lang.*; 019import votorola.g.logging.*; 020import votorola.g.mail.*; 021import votorola.g.web.wic.*; 022 023 024/** A page for the authentication of a user's email address, step 1. On this page, the 025 * user inputs the email address to authenticate. 026 * 027 * @see <a href='../../../../../../../a/web/wic/authen/WP_EmailAuthen1.html' target='_top' 028 * >WP_EmailAuthen1.html</a> 029 */ 030@ThreadRestricted("wicket") final class WP_EmailAuthen1 extends VPageHTML 031{ 032 033 034 /** Either logs the user in following on a successful OpenID authentication, or 035 * constructs a WP_EmailAuthen1 in order to continue the login attempt. 036 * 037 * @param verifiedID the {@linkplain 038 * org.openid4java.consumer.VerificationResult#getVerifiedId() verified 039 * identifier} from the positive authentication result, per {@linkplain 040 * UserSettings#setOpenID(Identifier) setOpenID}(verifiedID). 041 * @param runner the request cycle runner to handle any post-authentication 042 * processing. 043 * 044 * @return null if the user is logged in as a result; otherwise a new instance of 045 * WP_EmailAuthen1. 046 * 047 * @see WP_OpenIDLogin#isPersistent() 048 * @see WP_OpenIDLogin#isReauthenticationRequested() 049 */ 050 static WP_EmailAuthen1 login( final Identifier verifiedID, final boolean persistent, 051 final boolean reauthenticationRequested, final RequestCycleRunner runner ) 052 { 053 final String openID = verifiedID.getIdentifier(); 054 if( !reauthenticationRequested ) 055 { 056 final VRequestCycle cycle = VRequestCycle.get(); 057 final VoteServer.Run vsRun = VOWicket.get().vsRun(); 058 final UserSettings settings; 059 try 060 { 061 settings = UserSettings.forOpenID( openID, vsRun.userTable() ); 062 } 063 catch( Exception x ) { throw VotorolaRuntimeException.castOrWrapped( x ); } 064 065 if( settings != null ) 066 { 067 WP_OpenIDLogin.setUserInSession( settings.voterEmail(), "OpenID", persistent, 068 cycle ); 069 return null; 070 } 071 // else OpenID not yet associated, proceed with email authentication: 072 } 073 074 return new WP_EmailAuthen1( openID, persistent, new OpenIDSetter( verifiedID, runner )); 075 } 076 077 078 079 private WP_EmailAuthen1( final String openID, boolean _persistent, final RequestCycleRunner rcr ) 080 { 081 persistent = _persistent; 082 runner = rcr; 083 final VRequestCycle cycle = VRequestCycle.get(); 084 final BundleFormatter bun = cycle.bunW(); 085 086 add( new Label( "title", bun.l( "a.web.wic.authen.WP_EmailAuthen1" ) )); 087 add( new Label( "explanation", bun.l( "a.web.wic.authen.WP_EmailAuthen1.explanation" ))); 088 089 final Form<Void> y = new EmailAddressForm(); 090 add( y ); 091 092 // OpenID 093 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 094 y.add( new Label( "openIDLabel", 095 bun.l( "a.web.wic.authen.WP_Login.openid_identifier" ))); 096 final TextField<String> openidField = new TextField<String>( "openID", new Model<String>( openID )); 097 openidField.setEnabled( false ); 098 y.add( openidField ); 099 100 y.add( new Label( "openIDDescription", 101 bun.l( "a.web.wic.authen.WP_EmailAuthen1.openIDDescription" ))); 102 103 // Email 104 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 105 y.add( new Label( "userEmailLabel", bun.l( "a.web.wic.authen.WP_EmailAuthen1.userEmail" ))); 106 final TextField<String> emailField = new TextField<String>( "userEmail", new PropertyModel<String>( 107 WP_EmailAuthen1.this, "userEmailInput" )); 108 invalidStyled( inputLengthConstrained( emailField )); 109 emailField.setRequired( true ); 110 emailField.add( new WicEmailAddressValidator() // extracting conversions from a validator / this is improper / consider instead using a converter / cf. votorola.g.util.regex.WicPatternConverter 111 { 112 public @Override void validate( IValidatable<String> v ) 113 { 114 claimedUserIAddress = null; // till proven otherwise 115 super.validate( v ); 116 } 117 public @Override void onSuccess( InternetAddress iAddress ) 118 { 119 claimedUserIAddress = iAddress; 120 InternetAddressX.canonicalize( claimedUserIAddress ); 121 } 122 }); 123 y.add( emailField ); 124 125 y.add( new Label( "userEmailDescription", 126 bun.l( "a.web.wic.authen.WP_EmailAuthen1.userEmailDescription" ))); 127 128 // Buttons 129 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 130 { 131 final Button button = new Button( "submit" ); 132 button.add( AttributeModifier.replace( "value", 133 bun.l( "a.web.wic.authen.WP_EmailAuthen1.submit" ))); 134 y.add( button ); 135 } 136 { 137 final Button button = new Button( "submit-cancel" ) 138 { 139 public @Override void onSubmit() 140 { 141 super.onSubmit(); 142 runner.run( VRequestCycle.get() ); 143 } 144 }; 145 button.add( AttributeModifier.replace( "value", 146 bun.l( "a.web.wic.authen.WP_EmailAuthen1.submit-cancel" ))); 147 button.setDefaultFormProcessing( false ); 148 y.add( button ); 149 } 150 151 // Feedback Messages 152 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 153 add( new WC_Feedback( "feedback" )); 154 155 } 156 157 158 159//// P r i v a t e /////////////////////////////////////////////////////////////////////// 160 161 162 private transient InternetAddress claimedUserIAddress; // from field validator, in canonical form 163 164 165 166 private final boolean persistent; 167 168 169 170 private final RequestCycleRunner runner; 171 172 173 174 private String userEmailInput; // PropertyModel accesses it by java.lang.reflect.Field.setAccessible() 175 176 177 178 // ==================================================================================== 179 180 181 private class EmailAddressForm extends Form<Void> 182 { 183 184 EmailAddressForm() { super( "form" ); } 185 186 187 protected @Override void onSubmit() 188 { 189 super.onSubmit(); 190 if( claimedUserIAddress == null ) throw new IllegalStateException(); 191 192 final VRequestCycle cycle = VRequestCycle.get(); 193 final WP_EmailAuthen2 authenticationPage = 194 new WP_EmailAuthen2( claimedUserIAddress, persistent, cycle, runner ); 195 cycle.setResponsePage( authenticationPage.sendMessage( cycle )? 196 authenticationPage: new WP_Message() ); 197 } 198 199 200 } 201 202 203 204 // ==================================================================================== 205 206 207 private static final class OpenIDSetter extends RequestCycleRunnerW 208 { 209 210 private static final long serialVersionUID = 1L; 211 212 213 private OpenIDSetter( final Identifier verifiedID, RequestCycleRunner runner ) 214 { 215 super( runner ); 216 this.verifiedID = verifiedID; 217 } 218 219 220 private final VSession.User userAtAutheticationTime = VSession.get().user(); 221 // null, unless user already logged in 222 223 224 private final Identifier verifiedID; 225 226 227 public @Override void run( RequestCycle wCycle ) 228 { 229 final VRequestCycle cycle = (VRequestCycle)wCycle; 230 final VSession.User user = VSession.get().user(); 231 if( user != null && user != userAtAutheticationTime ) // != intended 232 { 233 // login went to completion, not cancelled 234 final UserSettings.Table table = VOWicket.get().vsRun().userTable(); 235 try 236 { 237 UserSettings settings = UserSettings.forOpenID( 238 verifiedID.getIdentifier(), table ); 239 if( settings != null && !settings.voterEmail().equals( user.email() )) 240 { 241 table.delete( settings.voterEmail() ); // remove association with old email address 242 settings = null; 243 } 244 245 if( settings == null ) 246 { 247 settings = new UserSettings( user.email(), table ); 248 settings.setOpenID( verifiedID ); // associate this authenticated OpenID with the user, for next login 249 settings.write( table, VSession.get() ); 250 } 251 } 252 catch( Exception x ) { throw VotorolaRuntimeException.castOrWrapped( x ); } 253 } 254 super.run( wCycle ); 255 } 256 } 257 258 259 260}