From d7b9872847e2eb524335ef0ef9a0d24ebdeeca64 Mon Sep 17 00:00:00 2001 From: brinn <brinn> Date: Sat, 19 Jan 2013 16:53:22 +0000 Subject: [PATCH] Add memory caching to FileAuthenticationService; add authentication by email to FileAuthenticationService. SVN: 28132 --- .../DummyAuthenticationService.java | 6 + .../IAuthenticationService.java | 6 + .../NullAuthenticationService.java | 6 + .../crowd/CrowdAuthenticationService.java | 6 + .../file/FileAuthenticationService.java | 68 +++++---- .../file/FileBasedLineStore.java | 13 +- .../cisd/authentication/file/ILineStore.java | 6 + .../cisd/authentication/file/IUserStore.java | 10 +- .../file/LineBasedUserStore.java | 136 +++++++++++------- .../file/PasswordEditorCommand.java | 8 +- .../ldap/LDAPAuthenticationService.java | 6 + .../stacked/StackedAuthenticationService.java | 11 ++ .../file/FileAuthenticationServiceTest.java | 4 +- .../file/FileBasedLineStoreTest.java | 5 + .../file/LineBasedUserStoreTest.java | 116 ++++++++++++++- 15 files changed, 313 insertions(+), 94 deletions(-) diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/DummyAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/DummyAuthenticationService.java index 7c79d8f6885..91cabbd442a 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/DummyAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/DummyAuthenticationService.java @@ -174,6 +174,12 @@ public final class DummyAuthenticationService implements IAuthenticationService return false; } + @Override + public boolean supportsAuthenticatingByEmail() + { + return true; + } + @Override public final void check() { diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/IAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/IAuthenticationService.java index c57f12e5fb8..f5d376ab488 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/IAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/IAuthenticationService.java @@ -57,6 +57,12 @@ public interface IAuthenticationService extends ISelfTestable */ public Principal getPrincipal(String user) throws IllegalArgumentException; + /** + * Returns <code>true</code> if this authentication service supports authenticating users by + * their email address. + */ + public boolean supportsAuthenticatingByEmail(); + /** * Returns <code>true</code> if this authentication service supports listing of principals by * user id. diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/NullAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/NullAuthenticationService.java index dbc53147503..431ad693858 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/NullAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/NullAuthenticationService.java @@ -107,6 +107,12 @@ public class NullAuthenticationService implements IAuthenticationService return false; } + @Override + public boolean supportsAuthenticatingByEmail() + { + return true; + } + @Override public boolean authenticateUser(String user, String password) { diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdAuthenticationService.java index a7870206c8f..fad6abe0552 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/crowd/CrowdAuthenticationService.java @@ -637,4 +637,10 @@ public class CrowdAuthenticationService implements IAuthenticationService return false; } + @Override + public boolean supportsAuthenticatingByEmail() + { + return false; + } + } diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/FileAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/FileAuthenticationService.java index 14afe485e08..08951e79806 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/file/FileAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/FileAuthenticationService.java @@ -79,9 +79,9 @@ public class FileAuthenticationService implements IAuthenticationService } @Override - public boolean authenticateUser(String user, String password) + public boolean authenticateUser(String userId, String password) { - return userStore.isPasswordCorrect(user, password); + return userStore.isPasswordCorrect(userId, password); } @Override @@ -90,54 +90,58 @@ public class FileAuthenticationService implements IAuthenticationService { return tryGetAndAuthenticateUser(user, passwordOrNull); } - + @Override public Principal tryGetAndAuthenticateUser(String user, String passwordOrNull) { - final UserEntry userOrNull = userStore.tryGetUser(user); - if (userOrNull != null) - { - final Principal principal = userOrNull.asPrincipal(); - if (passwordOrNull != null) - { - principal - .setAuthenticated(authenticateUser(user, passwordOrNull)); - } - return principal; - } else - { - return null; - } + return tryAuthenticateUser(userStore.tryGetUserById(user), passwordOrNull); } @Override - public Principal getPrincipal(String applicationToken, String user) + public Principal tryGetAndAuthenticateUserByEmail(String applicationToken, String email, + String passwordOrNull) { - return getPrincipal(user); + return tryGetAndAuthenticateUserByEmail(email, passwordOrNull); } - + @Override - public Principal getPrincipal(String user) + public Principal tryGetAndAuthenticateUserByEmail(String email, String passwordOrNull) { - final Principal principalOrNull = tryGetAndAuthenticateUser(user, null); - if (principalOrNull == null) + return tryAuthenticateUser(userStore.tryGetUserByEmail(email), passwordOrNull); + } + + private Principal tryAuthenticateUser(final UserEntry userOrNull, + String passwordOrNull) + { + if (userOrNull == null) { - throw new IllegalArgumentException("Cannot find user '" + user + "'."); + return null; } - return principalOrNull; + final Principal principal = userOrNull.asPrincipal(); + if (passwordOrNull != null) + { + principal + .setAuthenticated(authenticateUser(principal.getUserId(), passwordOrNull)); + } + return principal; } @Override - public Principal tryGetAndAuthenticateUserByEmail(String applicationToken, String email, String passwordOrNull) + public Principal getPrincipal(String applicationToken, String userId) { - throw new UnsupportedOperationException(); + return getPrincipal(userId); } @Override - public Principal tryGetAndAuthenticateUserByEmail(String email, String passwordOrNull) + public Principal getPrincipal(String userId) { - throw new UnsupportedOperationException(); + final Principal principalOrNull = tryGetAndAuthenticateUser(userId, null); + if (principalOrNull == null) + { + throw new IllegalArgumentException("Cannot find user '" + userId + "'."); + } + return principalOrNull; } @Override @@ -194,6 +198,12 @@ public class FileAuthenticationService implements IAuthenticationService return false; } + @Override + public boolean supportsAuthenticatingByEmail() + { + return true; + } + @Override public void check() throws EnvironmentFailureException, ConfigurationFailureException { diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/FileBasedLineStore.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/FileBasedLineStore.java index 08db566b516..e626810b6e7 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/file/FileBasedLineStore.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/FileBasedLineStore.java @@ -50,6 +50,8 @@ final class FileBasedLineStore implements ILineStore private final File newFile; private final String fileDescription; + + private long lastReadTimestamp; FileBasedLineStore(File file, String fileDescription) { @@ -135,7 +137,9 @@ final class FileBasedLineStore implements ILineStore } try { - return primReadLines(file); + final List<String> lines = primReadLines(file); + lastReadTimestamp = file.lastModified(); + return lines; } catch (IOException ex) { final String msg = @@ -182,6 +186,13 @@ final class FileBasedLineStore implements ILineStore oldFile.delete(); file.renameTo(oldFile); newFile.renameTo(file); + lastReadTimestamp = file.lastModified(); + } + + @Override + public boolean hasChanged() + { + return file.lastModified() != lastReadTimestamp; } } diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/ILineStore.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/ILineStore.java index de6f1494861..30076e20d42 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/file/ILineStore.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/ILineStore.java @@ -53,4 +53,10 @@ interface ILineStore * Writes the <var>lines</var> to the store. */ void writeLines(List<String> lines) throws EnvironmentFailureException; + + /** + * Returns <code>true</code> if the lines store has changed on disk since the last time it was + * read or written. + */ + boolean hasChanged(); } diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/IUserStore.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/IUserStore.java index 3bc5fb5b7d5..df400c927b2 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/file/IUserStore.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/IUserStore.java @@ -34,10 +34,16 @@ interface IUserStore extends ISelfTestable String getId(); /** - * Returns the {@link UserEntry} of <var>user</var>, or <code>null</code>, if this user does + * Returns the {@link UserEntry} of <var>userId</var>, or <code>null</code>, if this user does * not exist. */ - UserEntry tryGetUser(String user) throws EnvironmentFailureException; + UserEntry tryGetUserById(String userId) throws EnvironmentFailureException; + + /** + * Returns the {@link UserEntry} of <var>email</var>, or <code>null</code>, if this user does + * not exist. + */ + UserEntry tryGetUserByEmail(String email) throws EnvironmentFailureException; /** * Adds the <var>user</var> if it exists, otherwise updates (replaces) the entry with the given diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/LineBasedUserStore.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/LineBasedUserStore.java index 505f86c7c82..270f2d92cef 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/file/LineBasedUserStore.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/LineBasedUserStore.java @@ -17,9 +17,14 @@ package ch.systemsx.cisd.authentication.file; import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; +import ch.systemsx.cisd.common.exceptions.EnvironmentFailureException; +import ch.systemsx.cisd.common.shared.basic.string.StringUtils; /** * A class to read and write {@link UserEntry}. @@ -28,28 +33,69 @@ import ch.systemsx.cisd.common.exceptions.ConfigurationFailureException; */ final class LineBasedUserStore implements IUserStore { - private final ILineStore lineStore; - + + private Map<String, UserEntry> idToEntryMap; + + private Map<String, UserEntry> emailToEntryMap; + LineBasedUserStore(final ILineStore lineStore) { this.lineStore = lineStore; + this.idToEntryMap = new LinkedHashMap<String, UserEntry>(); + this.emailToEntryMap = new LinkedHashMap<String, UserEntry>(); } - private UserEntry tryFindUserEntry(String user, List<String> passwordLines) + private synchronized Map<String, UserEntry> getIdToEntryMap() { - assert user != null; - assert passwordLines != null; + return idToEntryMap; + } - for (String line : passwordLines) + private synchronized Map<String, UserEntry> getEmailToEntryMap() + { + return emailToEntryMap; + } + + synchronized void setEntryMaps(Map<String, UserEntry> idToEntryMap, + Map<String, UserEntry> emailToEntryMap) + { + this.idToEntryMap = idToEntryMap; + this.emailToEntryMap = emailToEntryMap; + } + + private void updateMaps() + { + if (lineStore.hasChanged()) { - final UserEntry entry = new UserEntry(line); - if (user.equals(entry.getUserId())) + final Map<String, UserEntry> newIdToEntryMap = new LinkedHashMap<String, UserEntry>(); + final Map<String, UserEntry> newEmailToEntryMap = + new LinkedHashMap<String, UserEntry>(); + for (String line : lineStore.readLines()) { - return entry; + final UserEntry entry = new UserEntry(line); + newIdToEntryMap.put(entry.getUserId(), entry); + if (StringUtils.isNotBlank(entry.getEmail())) + { + if (newEmailToEntryMap.put(entry.getEmail().toLowerCase(), entry) != null) + { + // Multiple users with the same email + emailToEntryMap.remove(entry.getEmail().toLowerCase()); + } + } } + setEntryMaps(newIdToEntryMap, newEmailToEntryMap); } - return null; + } + + private List<String> asPasswordLines() + { + final Collection<UserEntry> users = getIdToEntryMap().values(); + final List<String> lines = new ArrayList<String>(users.size()); + for (UserEntry user : users) + { + lines.add(user.asPasswordLine()); + } + return lines; } @Override @@ -59,59 +105,54 @@ final class LineBasedUserStore implements IUserStore } @Override - public UserEntry tryGetUser(String user) + public UserEntry tryGetUserById(String user) + { + updateMaps(); + return getIdToEntryMap().get(user); + } + + @Override + public UserEntry tryGetUserByEmail(String email) throws EnvironmentFailureException { - return tryFindUserEntry(user, lineStore.readLines()); + updateMaps(); + return getEmailToEntryMap().get(email.toLowerCase()); } @Override - public void addOrUpdateUser(UserEntry user) + public synchronized void addOrUpdateUser(UserEntry user) { assert user != null; - final List<String> passwordLines = lineStore.readLines(); - boolean found = false; - for (int i = 0; i < passwordLines.size(); ++i) + updateMaps(); + idToEntryMap.put(user.getUserId(), user); + if (StringUtils.isNotBlank(user.getEmail())) { - final String line = passwordLines.get(i); - final UserEntry entry = new UserEntry(line); - if (entry.getUserId().equals(user.getUserId())) + if (emailToEntryMap.put(user.getEmail().toLowerCase(), user) != null) { - passwordLines.set(i, user.asPasswordLine()); - found = true; - break; + // Multiple users with the same email + emailToEntryMap.remove(user.getEmail().toLowerCase()); } } - if (found == false) - { - passwordLines.add(user.asPasswordLine()); - } - lineStore.writeLines(passwordLines); + lineStore.writeLines(asPasswordLines()); } @Override - public boolean removeUser(String userId) + public synchronized boolean removeUser(String userId) { assert userId != null; - final List<String> passwordLines = lineStore.readLines(); - boolean found = false; - for (int i = 0; i < passwordLines.size(); ++i) + updateMaps(); + final UserEntry oldEntryOrNull = idToEntryMap.remove(userId); + if (oldEntryOrNull != null) { - final String line = passwordLines.get(i); - final UserEntry entry = new UserEntry(line); - if (userId.equals(entry.getUserId())) + if (StringUtils.isNotBlank(oldEntryOrNull.getEmail())) { - passwordLines.remove(i); - found = true; - break; + emailToEntryMap.remove(oldEntryOrNull.getEmail().toLowerCase()); } + lineStore.writeLines(asPasswordLines()); + return true; } - if (found) - { - lineStore.writeLines(passwordLines); - } - return found; + return false; } @Override @@ -120,7 +161,7 @@ final class LineBasedUserStore implements IUserStore assert user != null; assert password != null; - final UserEntry userEntryOrNull = tryFindUserEntry(user, lineStore.readLines()); + final UserEntry userEntryOrNull = tryGetUserById(user); if (userEntryOrNull == null) { return false; @@ -131,13 +172,8 @@ final class LineBasedUserStore implements IUserStore @Override public List<UserEntry> listUsers() { - final List<UserEntry> list = new ArrayList<UserEntry>(); - for (String line : lineStore.readLines()) - { - final UserEntry user = new UserEntry(line); - list.add(user); - } - return list; + updateMaps(); + return new ArrayList<UserEntry>(getIdToEntryMap().values()); } /** diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/file/PasswordEditorCommand.java b/authentication/source/java/ch/systemsx/cisd/authentication/file/PasswordEditorCommand.java index 71f0fe5a659..7d8f7082b19 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/file/PasswordEditorCommand.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/file/PasswordEditorCommand.java @@ -106,7 +106,7 @@ public class PasswordEditorCommand case ADD: { final String userId = params.getUserId(); - final UserEntry userOrNull = userStore.tryGetUser(userId); + final UserEntry userOrNull = userStore.tryGetUserById(userId); if (userOrNull != null) { System.err.printf("User '%s' already exists.\n", userId); @@ -129,7 +129,7 @@ public class PasswordEditorCommand case CHANGE: { final String userId = params.getUserId(); - final UserEntry userOrNull = userStore.tryGetUser(userId); + final UserEntry userOrNull = userStore.tryGetUserById(userId); if (userOrNull == null) { System.err.printf("User '%s' does not exist.\n", userId); @@ -180,7 +180,7 @@ public class PasswordEditorCommand case SHOW: { final String userId = params.getUserId(); - final UserEntry userOrNull = userStore.tryGetUser(userId); + final UserEntry userOrNull = userStore.tryGetUserById(userId); if (userOrNull == null) { System.err.printf("User '%s' does not exist.\n", userId); @@ -194,7 +194,7 @@ public class PasswordEditorCommand case TEST: { final String userId = params.getUserId(); - final UserEntry userOrNull = userStore.tryGetUser(userId); + final UserEntry userOrNull = userStore.tryGetUserById(userId); if (userOrNull == null) { System.err.printf("User '%s' does not exist.\n", userId); diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java index 158e248f2bb..aee60a5a933 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/ldap/LDAPAuthenticationService.java @@ -157,6 +157,12 @@ public class LDAPAuthenticationService implements IAuthenticationService return true; } + @Override + public boolean supportsAuthenticatingByEmail() + { + return true; + } + @Override public void check() throws EnvironmentFailureException, ConfigurationFailureException { diff --git a/authentication/source/java/ch/systemsx/cisd/authentication/stacked/StackedAuthenticationService.java b/authentication/source/java/ch/systemsx/cisd/authentication/stacked/StackedAuthenticationService.java index 14a0600152e..4a7ec39f6a6 100644 --- a/authentication/source/java/ch/systemsx/cisd/authentication/stacked/StackedAuthenticationService.java +++ b/authentication/source/java/ch/systemsx/cisd/authentication/stacked/StackedAuthenticationService.java @@ -41,6 +41,8 @@ public class StackedAuthenticationService implements IAuthenticationService private final boolean supportsListingByEmail; private final boolean supportsListingByLastName; + + private final boolean supportsAuthenticatingByEmail; public StackedAuthenticationService(List<IAuthenticationService> authenticationServices) { @@ -49,17 +51,20 @@ public class StackedAuthenticationService implements IAuthenticationService boolean foundSupportsListingByUserId = false; boolean foundSupportsListingByEmail = false; boolean foundSupportsListingByLastName = false; + boolean foundSupportsAuthenticateByEmail = false; for (IAuthenticationService service : delegates) { foundRemote |= service.isRemote(); foundSupportsListingByUserId |= service.supportsListingByUserId(); foundSupportsListingByEmail |= service.supportsListingByEmail(); foundSupportsListingByLastName |= service.supportsListingByLastName(); + foundSupportsAuthenticateByEmail |= service.supportsAuthenticatingByEmail(); } this.remote = foundRemote; this.supportsListingByUserId = foundSupportsListingByUserId; this.supportsListingByEmail = foundSupportsListingByEmail; this.supportsListingByLastName = foundSupportsListingByLastName; + this.supportsAuthenticatingByEmail = foundSupportsAuthenticateByEmail; } @Override @@ -234,6 +239,12 @@ public class StackedAuthenticationService implements IAuthenticationService return supportsListingByUserId; } + @Override + public boolean supportsAuthenticatingByEmail() + { + return supportsAuthenticatingByEmail; + } + @Override public void check() throws EnvironmentFailureException, ConfigurationFailureException { diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileAuthenticationServiceTest.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileAuthenticationServiceTest.java index cf365373799..8559772c229 100644 --- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileAuthenticationServiceTest.java +++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileAuthenticationServiceTest.java @@ -92,7 +92,7 @@ public class FileAuthenticationServiceTest context.checking(new Expectations() { { - one(userStore).tryGetUser(uid); + one(userStore).tryGetUserById(uid); will(returnValue(user)); } }); @@ -107,7 +107,7 @@ public class FileAuthenticationServiceTest context.checking(new Expectations() { { - one(userStore).tryGetUser(uid); + one(userStore).tryGetUserById(uid); will(returnValue(null)); } }); diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileBasedLineStoreTest.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileBasedLineStoreTest.java index a0647c61453..52728ef9604 100644 --- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileBasedLineStoreTest.java +++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/FileBasedLineStoreTest.java @@ -82,11 +82,16 @@ public class FileBasedLineStoreTest file.delete(); assertFalse(file.exists()); + assertFalse(store.hasChanged()); final List<String> lines1 = Arrays.asList("1", "2", "3"); store.writeLines(lines1); assertTrue(file.exists()); + assertFalse(store.hasChanged()); + file.setLastModified(System.currentTimeMillis() + 1000); + assertTrue(store.hasChanged()); assertEquals(StringUtils.join(lines1, '\n') + "\n", FileUtils.readFileToString(file)); final List<String> linesRead1 = store.readLines(); + assertFalse(store.hasChanged()); assertEquals(lines1, linesRead1); assertTrue(svFile.exists()); assertEquals(0, svFile.length()); diff --git a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/LineBasedUserStoreTest.java b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/LineBasedUserStoreTest.java index 585a4434323..35ad13bf17b 100644 --- a/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/LineBasedUserStoreTest.java +++ b/authentication/sourceTest/java/ch/systemsx/cisd/authentication/file/LineBasedUserStoreTest.java @@ -119,6 +119,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } @@ -138,11 +140,27 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } }); - assertNull(userStore.tryGetUser("uid")); + assertNull(userStore.tryGetUserById("uid")); + context.assertIsSatisfied(); + } + + @Test + public void testTryGetUserFailedNoStoreFile() + { + context.checking(new Expectations() + { + { + one(lineStore).hasChanged(); + will(returnValue(false)); + } + }); + assertNull(userStore.tryGetUserById("uid")); context.assertIsSatisfied(); } @@ -157,11 +175,13 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } }); - assertNull(userStore.tryGetUser("non-existent")); + assertNull(userStore.tryGetUserById("non-existent")); context.assertIsSatisfied(); } @@ -176,11 +196,13 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } }); - assertEquals(u1, userStore.tryGetUser("uid1")); + assertEquals(u1, userStore.tryGetUserById("uid1")); context.assertIsSatisfied(); } @@ -197,6 +219,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } @@ -218,6 +242,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } @@ -234,6 +260,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(new ArrayList<String>())); one(lineStore).writeLines(Collections.singletonList(userLine)); @@ -253,10 +281,10 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { - final List<String> lines = new ArrayList<String>(); - lines.add(oldUserLine); + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); - will(returnValue(lines)); + will(returnValue(Arrays.asList(oldUserLine))); one(lineStore).writeLines(Arrays.asList(oldUserLine, newUserLine)); } }); @@ -280,6 +308,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); one(lineStore).writeLines(linesUpdated); @@ -289,6 +319,40 @@ public class LineBasedUserStoreTest context.assertIsSatisfied(); } + @Test + public void testUpdateUserStoreChanged() + { + final UserEntry u1 = new UserEntry("uid1", "email1", "first1", "last1", "pwd1"); + final UserEntry u2 = new UserEntry("uid2", "email2", "first2", "last2", "pwd2"); + final UserEntry u3 = new UserEntry("uid3", "email3", "first3", "last3", "pwd3"); + final List<String> linesOld = + Arrays.asList(u1.asPasswordLine(), u3.asPasswordLine()); + final List<String> linesNew = + Arrays.asList(u1.asPasswordLine(), u2.asPasswordLine(), u3.asPasswordLine()); + + final UserEntry u3Updated = new UserEntry("uid3", "email3U", "first3U", "last3U", "pwd3U"); + final List<String> linesUpdated = + Arrays.asList(u1.asPasswordLine(), u2.asPasswordLine(), u3Updated.asPasswordLine()); + + context.checking(new Expectations() + { + { + one(lineStore).hasChanged(); + will(returnValue(true)); + one(lineStore).readLines(); + will(returnValue(linesOld)); + one(lineStore).hasChanged(); + will(returnValue(true)); + one(lineStore).readLines(); + will(returnValue(linesNew)); + one(lineStore).writeLines(linesUpdated); + } + }); + assertEquals(Arrays.asList(u1, u3), userStore.listUsers()); + userStore.addOrUpdateUser(u3Updated); + context.assertIsSatisfied(); + } + @Test public void testRemoveUser() { @@ -305,6 +369,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); one(lineStore).writeLines(linesUpdated); @@ -314,6 +380,40 @@ public class LineBasedUserStoreTest context.assertIsSatisfied(); } + @Test + public void testRemoveUserStoreChanged() + { + final String uid1 = "uid1"; + final UserEntry u1 = new UserEntry(uid1, "email1", "first1", "last1", "pwd1"); + final UserEntry u2 = new UserEntry("uid2", "email2", "first2", "last2", "pwd2"); + final UserEntry u3 = new UserEntry("uid3", "email3", "first3", "last3", "pwd3"); + final List<String> linesOld = + new ArrayList<String>(Arrays.asList(u1.asPasswordLine(), u3.asPasswordLine())); + final List<String> linesNew = + new ArrayList<String>(Arrays.asList(u1.asPasswordLine(), u2.asPasswordLine(), u3 + .asPasswordLine())); + + final List<String> linesUpdated = Arrays.asList(u2.asPasswordLine(), u3.asPasswordLine()); + + context.checking(new Expectations() + { + { + one(lineStore).hasChanged(); + will(returnValue(true)); + one(lineStore).readLines(); + will(returnValue(linesOld)); + one(lineStore).hasChanged(); + will(returnValue(true)); + one(lineStore).readLines(); + will(returnValue(linesNew)); + one(lineStore).writeLines(linesUpdated); + } + }); + assertEquals(Arrays.asList(u1, u3), userStore.listUsers()); + userStore.removeUser(uid1); + context.assertIsSatisfied(); + } + @Test public void testRemoveNonExistingUser() { @@ -326,6 +426,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); } @@ -344,6 +446,8 @@ public class LineBasedUserStoreTest context.checking(new Expectations() { { + one(lineStore).hasChanged(); + will(returnValue(true)); one(lineStore).readLines(); will(returnValue(lines)); one(lineStore).writeLines(Collections.<String> emptyList()); -- GitLab