/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* 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.picketlink.test.idm.credential;
import org.junit.Test;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.credential.Credentials;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.TOTPCredential;
import org.picketlink.idm.credential.TOTPCredentials;
import org.picketlink.idm.credential.storage.OTPCredentialStorage;
import org.picketlink.idm.credential.util.CredentialUtils;
import org.picketlink.idm.credential.util.TimeBasedOTP;
import org.picketlink.idm.model.Account;
import org.picketlink.test.idm.AbstractPartitionManagerTestCase;
import org.picketlink.test.idm.Configuration;
import org.picketlink.test.idm.testers.IdentityConfigurationTester;
import org.picketlink.test.idm.testers.LDAPStoreConfigurationTester;
import java.util.Calendar;
import java.util.Date;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Pedro Igor
*/
public abstract class AbstractTOTPCredentialTestCase extends AbstractPartitionManagerTestCase {
public static final String DEFAULT_TOTP_SECRET = "my_secret";
public static final String DEFAULT_PASSWORD = "passwd";
public AbstractTOTPCredentialTestCase(IdentityConfigurationTester visitor) {
super(visitor);
}
@Test
public void testSuccessfulValidation() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, credential);
TOTPCredentials credentials = new TOTPCredentials();
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
String token = totp.generate(DEFAULT_TOTP_SECRET);
credentials.setToken(token);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
assertNotNull(credentials.getValidatedAccount());
}
@Test
public void testSuccessfulNullDevice() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential defaultDevice = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, defaultDevice);
String iphoneSecret = "iphone_secret";
TOTPCredential iphoneDevice = new TOTPCredential(DEFAULT_PASSWORD, iphoneSecret);
String iphoneDeviceName = "My IPhone #SN-121212121";
iphoneDevice.setDevice(iphoneDeviceName);
identityManager.updateCredential(user, iphoneDevice);
String androidSecret = "android_secret";
TOTPCredential androidDevice = new TOTPCredential(DEFAULT_PASSWORD, androidSecret);
String androidDeviceName = "My Android #SN-56757554";
androidDevice.setDevice(androidDeviceName);
identityManager.updateCredential(user, androidDevice);
TOTPCredentials credentials = new TOTPCredentials();
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
// validate default device credentials
credentials.setToken(totp.generate(DEFAULT_TOTP_SECRET));
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
// validate iphone device credentials
credentials.setToken(totp.generate(iphoneSecret));
credentials.setDevice(null);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
// validate iphone device credentials
credentials.setToken(totp.generate(androidSecret));
credentials.setDevice(null);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
credentials.setToken(totp.generate("bad_secret"));
credentials.setDevice(null);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.INVALID, credentials.getStatus());
}
@Test
public void testMultipleDevices() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential defaultDevice = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, defaultDevice);
String iphoneSecret = "iphone_secret";
TOTPCredential iphoneDevice = new TOTPCredential(DEFAULT_PASSWORD, iphoneSecret);
String iphoneDeviceName = "My IPhone #SN-121212121";
iphoneDevice.setDevice(iphoneDeviceName);
identityManager.updateCredential(user, iphoneDevice);
String androidSecret = "android_secret";
TOTPCredential androidDevice = new TOTPCredential(DEFAULT_PASSWORD, androidSecret);
String androidDeviceName = "My Android #SN-56757554";
androidDevice.setDevice(androidDeviceName);
identityManager.updateCredential(user, androidDevice);
TOTPCredentials credentials = new TOTPCredentials();
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
// validate default device credentials
credentials.setToken(totp.generate(DEFAULT_TOTP_SECRET));
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
// validate iphone device credentials
credentials.setToken(totp.generate(iphoneSecret));
credentials.setDevice(iphoneDeviceName);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
// validate android device credentials
credentials.setToken(totp.generate(androidSecret));
credentials.setDevice(androidDeviceName);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
// should fail, trying to use a iphone token in a android device
credentials.setToken(totp.generate(iphoneSecret));
credentials.setDevice(androidDeviceName);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.INVALID, credentials.getStatus());
assertNull(credentials.getValidatedAccount());
// should fail, trying to use a android token in a iphone device
credentials.setToken(totp.generate(androidSecret));
credentials.setDevice(iphoneDeviceName);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.INVALID, credentials.getStatus());
assertNull(credentials.getValidatedAccount());
}
@Test
public void testDelayWindow() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, credential);
TOTPCredentials credentials = new TOTPCredentials();
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, -30);
totp.setCalendar(calendar);
String token = totp.generate("my_secret");
credentials.setToken(token);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.VALID, credentials.getStatus());
}
@Test
public void testUpdatePasswordAndSecret() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, credential);
TOTPCredentials validatingCredential = new TOTPCredentials();
validatingCredential.setUsername(accountName);
validatingCredential.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
validatingCredential.setToken(totp.generate(DEFAULT_TOTP_SECRET));
identityManager.validateCredentials(validatingCredential);
assertEquals(Credentials.Status.VALID, validatingCredential.getStatus());
credential = new TOTPCredential("new_password", DEFAULT_TOTP_SECRET);
Thread.sleep(1000);
// update only the password
identityManager.updateCredential(user, credential);
validatingCredential.setPassword(new Password("new_password"));
validatingCredential.setToken(totp.generate(DEFAULT_TOTP_SECRET));
identityManager.validateCredentials(validatingCredential);
assertEquals(Credentials.Status.VALID, validatingCredential.getStatus());
credential = new TOTPCredential("new_password", "new_secret");
Thread.sleep(1000);
// now we update only the secret
identityManager.updateCredential(user, credential);
validatingCredential.setPassword(new Password("new_password"));
validatingCredential.setToken(totp.generate("new_secret"));
identityManager.validateCredentials(validatingCredential);
assertEquals(Credentials.Status.VALID, validatingCredential.getStatus());
validatingCredential.setPassword(new Password(DEFAULT_PASSWORD));
validatingCredential.setToken(totp.generate(DEFAULT_TOTP_SECRET));
identityManager.validateCredentials(validatingCredential);
assertEquals(Credentials.Status.INVALID, validatingCredential.getStatus());
assertNull(validatingCredential.getValidatedAccount());
}
@Test
public void testUpdateSecret() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, credential);
TOTPCredentials validatingCredential = new TOTPCredentials();
validatingCredential.setUsername(accountName);
validatingCredential.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
validatingCredential.setToken(totp.generate(DEFAULT_TOTP_SECRET));
identityManager.validateCredentials(validatingCredential);
assertEquals(Credentials.Status.VALID, validatingCredential.getStatus());
credential = new TOTPCredential("new_secret");
// update only the secret
identityManager.updateCredential(user, credential);
validatingCredential.setPassword(new Password(DEFAULT_PASSWORD));
validatingCredential.setToken(totp.generate("new_secret"));
identityManager.validateCredentials(validatingCredential);
assertEquals(Credentials.Status.VALID, validatingCredential.getStatus());
}
@Test
public void testInvalidToken() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
identityManager.updateCredential(user, credential);
TOTPCredentials credentials = new TOTPCredentials();
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, -60);
totp.setCalendar(calendar);
String token = totp.generate("my_secret");
credentials.setToken(token);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.INVALID, credentials.getStatus());
assertNull(credentials.getValidatedAccount());
}
@Test
public void testResetPassword() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
Calendar expirationDate = Calendar.getInstance();
expirationDate.add(Calendar.MINUTE, -5);
identityManager.updateCredential(user, credential, new Date(), expirationDate.getTime());
TOTPCredentials credentials = new TOTPCredentials();
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
TimeBasedOTP totp = new TimeBasedOTP();
String token = totp.generate(DEFAULT_TOTP_SECRET);
credentials.setToken(token);
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.EXPIRED, credentials.getStatus());
credentials.setUsername(accountName);
credentials.setPassword(new Password(DEFAULT_PASSWORD));
credentials.setToken("12345678");
identityManager.validateCredentials(credentials);
assertEquals(Credentials.Status.INVALID, credentials.getStatus());
}
@Test
@Configuration(exclude = LDAPStoreConfigurationTester.class)
public void testRetrieveCurrentCredential() throws Exception {
IdentityManager identityManager = getIdentityManager();
String accountName = "someUser";
Account user = createAccount(accountName);
TOTPCredential credential = new TOTPCredential(DEFAULT_PASSWORD, DEFAULT_TOTP_SECRET);
credential.setDevice("My Android");
identityManager.updateCredential(user, credential);
OTPCredentialStorage currentStorage = identityManager.retrieveCurrentCredential(user, OTPCredentialStorage.class);
assertNotNull(currentStorage);
assertTrue(CredentialUtils.isCurrentCredential(currentStorage));
assertNotNull(currentStorage.getEffectiveDate());
assertNotNull(currentStorage.getDevice());
assertNotNull(currentStorage.getSecretKey());
}
protected abstract Account createAccount(String accountName);
}