/* * Jitsi, the OpenSource Java VoIP and Instant Messaging client. * * Copyright @ 2015 Atlassian Pty Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.java.sip.communicator.slick.protocol.icq; import java.util.*; import junit.framework.*; import net.java.sip.communicator.service.protocol.*; import net.java.sip.communicator.service.protocol.event.*; import net.java.sip.communicator.util.*; import org.osgi.framework.*; /** * Tests basic provider factory functionalitites * @author Emil Ivov * @author Damian Minkov */ public class TestAccountInstallation extends TestCase { private static final Logger logger = Logger.getLogger(TestAccountInstallation.class); /** * The lock that we wait on until registration is finalized. */ private Object registrationLock = new Object(); ProtocolProviderFactory icqProviderFactory = null; /** * Creates the test with the specified method name. * @param name the name of the method to execute. */ public TestAccountInstallation(String name) { super(name); } /** * JUnit setup method. * @throws Exception in case anything goes wrong. */ @Override protected void setUp() throws Exception { super.setUp(); } /** * JUnit teardown method. * @throws Exception in case anything goes wrong. */ @Override protected void tearDown() throws Exception { super.tearDown(); } public static Test suite() { TestSuite suite = new TestSuite(); if (!IcqSlickFixture.onlineTestingDisabled) { suite.addTest( new TestAccountInstallation("testRegisterWrongUsername")); suite.addTest( new TestAccountInstallation("testRegisterWrongPassword")); } suite.addTest( new TestAccountInstallation("testInstallAccount")); return suite; } /** * We try to register with wrong uin which must fire event with status * AUTHENTICATION_FAILED. As the uin is new (not existing and not * registered) * we first install this account, then try to register and wait for the * supposed event. After all we unregister this account * * @throws OperationFailedException if register () fails for a provider. */ public void testRegisterWrongUsername() throws OperationFailedException { ServiceReference[] serRefs = null; String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL + "="+ProtocolNames.ICQ+")"; try{ serRefs = IcqSlickFixture.bc.getServiceReferences( ProtocolProviderFactory.class.getName(), osgiFilter); } catch (InvalidSyntaxException ex) { //this really shouldhn't occur as the filter expression is static. fail(osgiFilter + " is not a valid osgi filter"); } assertTrue( "Failed to find a provider factory service for protocol ICQ", (serRefs != null) && (serRefs.length > 0)); //Keep the reference for later usage. ProtocolProviderFactory icqProviderFactory = (ProtocolProviderFactory)IcqSlickFixture.bc.getService(serRefs[0]); //Prepare the properties of the icq account. String passwd = System.getProperty( IcqProtocolProviderSlick .TESTED_IMPL_PWD_PROP_NAME, null ); String uin = System.getProperty( IcqProtocolProviderSlick .TESTED_IMPL_USER_ID_PROP_NAME, null); // make this uin an invalid one uin = uin + "1234"; Hashtable<String, String> icqAccountProperties = new Hashtable<String, String>(); icqAccountProperties.put(ProtocolProviderFactory.PASSWORD, passwd); AccountID icqAccountID = icqProviderFactory.installAccount( uin, icqAccountProperties); //find the protocol provider service ServiceReference[] icqProviderRefs = null; try { icqProviderRefs = IcqSlickFixture.bc.getServiceReferences( ProtocolProviderService.class.getName(), "(&" + "(" + ProtocolProviderFactory.PROTOCOL + "=" + ProtocolNames.ICQ + ")" + "(" + ProtocolProviderFactory.USER_ID + "=" + uin + ")" + ")"); } catch(InvalidSyntaxException ex) { logger.debug("Our filter seems to be messed up.", ex); } //make sure we found a service assertNotNull("No Protocol Provider was found for ICQ UIN:"+ icqAccountID, icqProviderRefs); assertTrue("No Protocol Provider was found for ICQ UIN:"+ icqAccountID, icqProviderRefs.length > 0); //save the service for other tests to use. ServiceReference icqServiceRef = icqProviderRefs[0]; ProtocolProviderService provider = (ProtocolProviderService)IcqSlickFixture.bc.getService(icqServiceRef); RegistrationFailedEventCollector regFailedEvtCollector = new RegistrationFailedEventCollector(); logger.debug("install " + regFailedEvtCollector); provider.addRegistrationStateChangeListener(regFailedEvtCollector); provider.register(new SecurityAuthorityImpl(passwd.toCharArray())); //give it enough time to register. We won't really have to wait all this //time since the registration event collector would notify us the moment //we get signed on. try{ synchronized(registrationLock) { logger.debug("Waiting for registration to complete ..."); registrationLock.wait(40000); logger.debug("Registration was completed or we lost patience."); } } catch (InterruptedException ex) { logger.debug("Interrupted while waiting for registration", ex); } catch(Throwable t) { logger.debug("We got thrown out while waiting for registration", t); } assertTrue( "No registration event notifying of registration has failed. " +"All events were: " + regFailedEvtCollector.collectedNewStates ,regFailedEvtCollector.collectedNewStates .contains(RegistrationState.AUTHENTICATION_FAILED)); assertEquals( "Registration status must be auth failed as we are logging in " +"with wrong uin", regFailedEvtCollector.failedCode, RegistrationStateChangeEvent.REASON_NON_EXISTING_USER_ID); assertNotNull("We must have reason for auth failed", regFailedEvtCollector.failedReason); provider.removeRegistrationStateChangeListener(regFailedEvtCollector); icqProviderFactory.uninstallAccount(icqAccountID); } /** * Will try to register with wrong password and wait for triggered event * with status AUTHENTICATION_FAILED. * We get the already installed account. Change the password and try to * register. After all tests we must return the original password so we * don't break the other tests * * @throws OperationFailedException if register fails for a provider. */ public void testRegisterWrongPassword() throws OperationFailedException { ServiceReference[] serRefs = null; String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL + "="+ProtocolNames.ICQ+")"; try{ serRefs = IcqSlickFixture.bc.getServiceReferences( ProtocolProviderFactory.class.getName(), osgiFilter); } catch (InvalidSyntaxException ex) { //this really shouldhn't occur as the filter expression is static. fail(osgiFilter + " is not a valid osgi filter"); } assertTrue( "Failed to find a provider factory service for protocol ICQ", (serRefs != null) && (serRefs.length > 0)); //Keep the reference for later usage. ProtocolProviderFactory icqProviderFactory = (ProtocolProviderFactory)IcqSlickFixture.bc.getService(serRefs[0]); //Prepare the properties of the icq account. String passwd = System.getProperty( IcqProtocolProviderSlick .TESTED_IMPL_PWD_PROP_NAME, null ); String uin = System.getProperty( IcqProtocolProviderSlick .TESTED_IMPL_USER_ID_PROP_NAME, null); passwd = "dam1234"; Hashtable<String, String> icqAccountProperties = new Hashtable<String, String>(); icqAccountProperties.put(ProtocolProviderFactory.PASSWORD, passwd); AccountID icqAccountID = icqProviderFactory.installAccount( uin, icqAccountProperties); //find the protocol provider service ServiceReference[] icqProviderRefs = null; try { icqProviderRefs = IcqSlickFixture.bc.getServiceReferences( ProtocolProviderService.class.getName(), "(&" + "(" + ProtocolProviderFactory.PROTOCOL + "=" + ProtocolNames.ICQ + ")" + "(" + ProtocolProviderFactory.USER_ID + "=" + icqAccountID.getUserID() + ")" + ")"); } catch(InvalidSyntaxException ex1) { logger.error("", ex1); } //make sure we found a service assertNotNull("No Protocol Provider was found for ICQ UIN:" + icqAccountID, icqProviderRefs); assertTrue("No Protocol Provider was found for ICQ UIN:" + icqAccountID, icqProviderRefs.length > 0); //save the service for other tests to use. ServiceReference icqServiceRef = icqProviderRefs[0]; ProtocolProviderService provider = (ProtocolProviderService)IcqSlickFixture. bc.getService(icqServiceRef); RegistrationFailedEventCollector regFailedEvtCollector = new RegistrationFailedEventCollector(); logger.debug("install " + regFailedEvtCollector); provider.addRegistrationStateChangeListener(regFailedEvtCollector); provider.register(new SecurityAuthorityImpl(passwd.toCharArray())); //give it enough time to register. We won't really have to wait all this //time since the registration event collector would notify us the moment //we get signed on. try{ synchronized(registrationLock) { logger.debug("Waiting for registration to complete ..."); registrationLock.wait(40000); logger.debug("Registration was completed or we lost patience."); } } catch (InterruptedException ex) { logger.debug("Interrupted while waiting for registration", ex); } catch(Throwable t) { logger.debug("We got thrown out while waiting for registration", t); } assertTrue( "No registration event notifying of registration has failed. " +"All events were: " + regFailedEvtCollector.collectedNewStates ,regFailedEvtCollector.collectedNewStates .contains(RegistrationState.AUTHENTICATION_FAILED)); assertEquals( "Registration status must be auth failed as we are logging in with wrong pass", regFailedEvtCollector.failedCode, RegistrationStateChangeEvent.REASON_AUTHENTICATION_FAILED); assertNotNull("We must have reason for auth failed", regFailedEvtCollector.failedReason); provider.removeRegistrationStateChangeListener(regFailedEvtCollector); icqProviderFactory.uninstallAccount(icqAccountID); } /** * Installs an account and verifies whether the installation has gone well. */ public void testInstallAccount() { // first obtain a reference to the provider factory ServiceReference[] serRefs = null; String osgiFilter = "(" + ProtocolProviderFactory.PROTOCOL + "="+ProtocolNames.ICQ+")"; try{ serRefs = IcqSlickFixture.bc.getServiceReferences( ProtocolProviderFactory.class.getName(), osgiFilter); } catch (InvalidSyntaxException ex) { //this really shouldhn't occur as the filter expression is static. fail(osgiFilter + " is not a valid osgi filter"); } assertTrue( "Failed to find a provider factory service for protocol ICQ", (serRefs != null) && (serRefs.length > 0)); //Keep the reference for later usage. icqProviderFactory = (ProtocolProviderFactory) IcqSlickFixture.bc.getService(serRefs[0]); //make sure the account is empty assertTrue("There was an account registered with the account mananger " +"before we've installed any", icqProviderFactory.getRegisteredAccounts().size() == 0); //Prepare the properties of the icq account. String passwd = System.getProperty( IcqProtocolProviderSlick .TESTED_IMPL_PWD_PROP_NAME, null ); String uin = System.getProperty( IcqProtocolProviderSlick .TESTED_IMPL_USER_ID_PROP_NAME, null); assertNotNull( "In the " + IcqProtocolProviderSlick.TESTED_IMPL_USER_ID_PROP_NAME +" system property, you need to provide a valid icq UIN for the " +" slick to use when signing on icq. It's passwd must be set in " + IcqProtocolProviderSlick.TESTED_IMPL_PWD_PROP_NAME, uin); assertNotNull( "In the " + IcqProtocolProviderSlick.TESTED_IMPL_PWD_PROP_NAME +" system property, you need to provide a password for the " + uin +" account.", passwd); Hashtable<String, String> icqAccountProperties = new Hashtable<String, String>(); icqAccountProperties.put(ProtocolProviderFactory.PASSWORD, passwd); //try to install an account with a null account id try{ icqProviderFactory.installAccount( null, icqAccountProperties); fail("installing an account with a null account id must result " +"in a NullPointerException"); }catch(NullPointerException exc) { //that's what had to happen } //now really install the account IcqSlickFixture.icqAccountID = icqProviderFactory.installAccount( uin, icqAccountProperties); //try to install the account one more time and verify that an excepion //is thrown. try{ IcqSlickFixture.icqAccountID = icqProviderFactory.installAccount( uin, icqAccountProperties); fail("An IllegalStateException must be thrown when trying to "+ "install a duplicate account"); }catch(IllegalStateException exc) { //that's what supposed to happen. } //Verify that the provider factory is aware of our installation assertTrue( "The newly installed account was not in the acc man's " +"registered accounts!", icqProviderFactory.getRegisteredAccounts().size() == 1); //Verify that the protocol provider corresponding to the new account has //been properly registered with the osgi framework. osgiFilter = "(&("+ProtocolProviderFactory.PROTOCOL +"="+ProtocolNames.ICQ+")" +"(" + ProtocolProviderFactory.USER_ID + "=" + IcqSlickFixture.icqAccountID.getUserID() + "))"; try { serRefs = IcqSlickFixture.bc.getServiceReferences( ProtocolProviderService.class.getName(), osgiFilter); } catch (InvalidSyntaxException ex) { //this really shouldhn't occur as the filter expression is static. fail(osgiFilter + "is not a valid osgi filter"); } assertTrue("An ICQ protocol provider was apparently not installed as " + "requested." , serRefs != null && serRefs.length > 0); Object icqProtocolProvider = IcqSlickFixture.bc.getService(serRefs[0]); assertTrue("The installed protocol provider does not implement " + "the protocol provider service." ,icqProtocolProvider instanceof ProtocolProviderService); } /** * A blocking registration listener that would allow us to wait until * registration is completed. */ public class RegistrationFailedEventCollector implements RegistrationStateChangeListener { public List<RegistrationState> collectedNewStates = new LinkedList<RegistrationState>(); public int failedCode; public String failedReason = null; /** * Notifies the registration lock once a new * RegistrationStateChangeEvent is received. * @param evt the RegistrationStateChangeEvent that we'll have to store. */ public void registrationStateChanged(RegistrationStateChangeEvent evt) { collectedNewStates.add(evt.getNewState()); if(evt.getNewState().equals( RegistrationState.AUTHENTICATION_FAILED)) { failedCode = evt.getReasonCode(); failedReason = evt.getReason(); logger.debug("Our registration failed - " + failedCode + " = " + failedReason); synchronized(registrationLock) { logger.debug("."); registrationLock.notifyAll(); logger.debug("."); } } } } /** * A very simple straight forward implementation of a security authority * that would always return the same password (the one specified upon * construction) when asked for credentials. */ public class SecurityAuthorityImpl implements SecurityAuthority { /** * The password to return when asked for credentials */ private char[] passwd = null; private boolean isUserNameEditable = false; /** * Creates an instance of this class that would always return "passwd" * when asked for credentials. * * @param passwd the password that this class should return when * asked for credentials. */ public SecurityAuthorityImpl(char[] passwd) { this.passwd = passwd; } /** * Returns a Credentials object associated with the specified realm. * <p> * @param realm The realm that the credentials are needed for. * @param defaultValues the values to propose the user by default * @param reasonCode the reason for which we're obtaining the * credentials. * @return The credentials associated with the specified realm or null if * none could be obtained. */ public UserCredentials obtainCredentials(String realm, UserCredentials defaultValues, int reasonCode) { return obtainCredentials(realm, defaultValues); } /** * Returns a Credentials object associated with the specified realm. * <p> * @param realm The realm that the credentials are needed for. * @param defaultValues the values to propose the user by default * * @return The credentials associated with the specified realm or null if * none could be obtained. */ public UserCredentials obtainCredentials(String realm, UserCredentials defaultValues) { defaultValues.setPassword(passwd); return defaultValues; } /** * Sets the userNameEditable property, which should indicate if the * user name could be changed by user or not. * * @param isUserNameEditable indicates if the user name could be changed */ public void setUserNameEditable(boolean isUserNameEditable) { this.isUserNameEditable = isUserNameEditable; } /** * Indicates if the user name is currently editable, i.e. could be changed * by user or not. * * @return <code>true</code> if the user name could be changed, * <code>false</code> - otherwise. */ public boolean isUserNameEditable() { return isUserNameEditable; } } }