added preferences support to user store; see bug 2429

This commit is contained in:
Axel Uhl committed 2014-10-27 20:22:06 +01:00
1 parent 8b45395fcc
commit 2052941172
15 files changed
+207 -19

No files matched your search

@@ -50,7 +50,7 @@
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/Search.html com.sap.sailing.gwt.ui.Search -startupUrl /security/ui/Login.html com.sap.sse.security.ui.Login -startupUrl /security/ui/EditProfile.html com.sap.sse.security.ui.EditProfile -startupUrl /security/ui/EmailValidation.html com.sap.sse.security.ui.EmailValidation"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/TvView.html com.sap.sailing.gwt.ui.TvView -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/Search.html com.sap.sailing.gwt.ui.Search -startupUrl /security/ui/Login.html com.sap.sse.security.ui.Login -startupUrl /security/ui/EditProfile.html com.sap.sse.security.ui.EditProfile -startupUrl /security/ui/EmailValidation.html com.sap.sse.security.ui.EmailValidation com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.TvView com.sap.sailing.gwt.ui.UserManagement com.sap.sailing.gwt.ui.Search com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m"/>
</launchConfiguration>
@@ -45,11 +45,11 @@ import com.sap.sse.common.Util;
public class StoreGPSFixTrackTest extends AbstractMongoDBTest {
public StoreGPSFixTrackTest() throws UnknownHostException, MongoException {
super();
}
super();
}
@After
public void dropCollection() {
public void dropCollection() {
DB db = getMongoService().getDB();
db.getCollection(CollectionNames.GPS_FIXES.name()).drop();
}
@@ -7,4 +7,6 @@ Bundle-Vendor: SAP
Fragment-Host: com.sap.sse.security
Bundle-RequiredExecutionEnvironment: JavaSE-1.7
Require-Bundle: org.junit4;bundle-version="4.8.2",
com.sap.sse.security.userstore.mongodb
com.sap.sse.security.userstore.mongodb,
com.mongodb.driver,
com.sap.sailing.mongodb
@@ -1,23 +1,38 @@
package com.sap.sse.security.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import java.net.UnknownHostException;
import java.util.Properties;
import org.apache.shiro.SecurityUtils;
import org.junit.BeforeClass;
import org.junit.Before;
import org.junit.Test;
import com.mongodb.DB;
import com.mongodb.MongoException;
import com.sap.sailing.mongodb.MongoDBConfiguration;
import com.sap.sailing.mongodb.MongoDBService;
import com.sap.sse.security.SecurityServiceImpl;
import com.sap.sse.security.UserStore;
import com.sap.sse.security.UsernamePasswordRealm;
import com.sap.sse.security.impl.Activator;
import com.sap.sse.security.userstore.mongodb.UserStoreImpl;
import com.sap.sse.security.userstore.mongodb.impl.CollectionNames;
public class LoginTest {
@BeforeClass
public static void setUp() {
UserStore store = new UserStoreImpl();
private UserStoreImpl store;
@Before
public void setUp() throws UnknownHostException, MongoException {
final MongoDBConfiguration dbConfiguration = MongoDBConfiguration.getDefaultTestConfiguration();
final MongoDBService service = dbConfiguration.getService();
DB db = service.getDB();
db.getCollection(CollectionNames.SETTINGS.name()).drop();
db.getCollection(CollectionNames.PREFERENCES.name()).drop();
db.getCollection(CollectionNames.PREFERENCES.name()).drop();
store = new UserStoreImpl();
UsernamePasswordRealm.setTestUserStore(store);
Activator.setTestUserStore(store);
new SecurityServiceImpl(store, /* mailProperties */ new Properties());
@@ -27,4 +42,19 @@ public class LoginTest {
public void testGetUser() {
assertNotNull("Subject should not be null: ", SecurityUtils.getSubject());
}
@Test
public void setPreferencesTest() {
store.setPreference("me", "key", "value");
UserStoreImpl store2 = new UserStoreImpl();
assertEquals("value", store2.getPreference("me", "key"));
}
@Test
public void setAndUnsetPreferencesTest() {
store.setPreference("me", "key", "value");
store.unsetPreference("me", "key");
UserStoreImpl store2 = new UserStoreImpl();
assertNull(store2.getPreference("me", "key"));
}
}
@@ -12,4 +12,5 @@ Bundle-ClassPath: .
Require-Bundle: com.sap.sse.security;bundle-version="1.0.0",
com.mongodb.driver;bundle-version="2.6.2",
com.sap.sailing.mongodb
Export-Package: com.sap.sse.security.userstore.mongodb
Export-Package: com.sap.sse.security.userstore.mongodb,
com.sap.sse.security.userstore.mongodb.impl;x-friends:="com.sap.sse.security.test"
@@ -13,4 +13,6 @@ public interface DomainObjectFactory {
Map<String, Class<?>> loadSettingTypes();
User loadUser(String name);
Map<String, Map<String, String>> loadPreferences();
}
@@ -13,4 +13,6 @@ public interface MongoObjectFactory {
public void storeSettings(Map<String, Object> settings);
public void storeSettingTypes(Map<String, Class<?>> settingTypes);
public void storePreferences(String username, Map<String, String> userMap);
}
@@ -22,6 +22,7 @@ public class UserStoreImpl implements UserStore {
private final ConcurrentHashMap<String, User> users;
private final ConcurrentHashMap<String, Object> settings;
private final ConcurrentHashMap<String, Class<?>> settingTypes;
private final ConcurrentHashMap<String, Map<String, String>> preferences;
private final DomainObjectFactory domainObjectFactory;
private final MongoObjectFactory mongoObjectFactory;
@@ -29,6 +30,7 @@ public class UserStoreImpl implements UserStore {
users = new ConcurrentHashMap<>();
settings = new ConcurrentHashMap<>();
settingTypes = new ConcurrentHashMap<String, Class<?>>();
preferences = new ConcurrentHashMap<>();
domainObjectFactory = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory();
mongoObjectFactory = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory();
for (Entry<String, Class<?>> e : domainObjectFactory.loadSettingTypes().entrySet()) {
@@ -37,6 +39,9 @@ public class UserStoreImpl implements UserStore {
for (Entry<String, Object> e : domainObjectFactory.loadSettings().entrySet()) {
settings.put(e.getKey(), e.getValue());
}
for (Entry<String, Map<String, String>> e : domainObjectFactory.loadPreferences().entrySet()) {
preferences.put(e.getKey(), e.getValue());
}
boolean changed = false;
changed = changed || initSocialSettingsIfEmpty();
if (changed) {
@@ -165,6 +170,44 @@ public class UserStoreImpl implements UserStore {
return result;
}
@Override
public void setPreference(String username, String key, String value) {
Map<String, String> userMap = preferences.get(username);
if (userMap == null) {
synchronized (preferences) {
// only synchronize when necessary
userMap = preferences.get(username);
if (userMap == null) {
userMap = new ConcurrentHashMap<>();
preferences.put(username, userMap);
}
}
}
userMap.put(key, value);
mongoObjectFactory.storePreferences(username, userMap);
}
@Override
public void unsetPreference(String username, String key) {
Map<String, String> userMap = preferences.get(username);
if (userMap != null) {
userMap.remove(key);
mongoObjectFactory.storePreferences(username, userMap);
}
}
@Override
public String getPreference(String username, String key) {
final String result;
Map<String, String> userMap = preferences.get(username);
if (userMap != null) {
result = userMap.get(key);
} else {
result = null;
}
return result;
}
@Override
public Map<String, Object> getAllSettings() {
return settings;
@@ -118,7 +118,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
@Override
public Map<String, Object> loadSettings() {
Map<String, Object> result = new HashMap<String, Object>();
Map<String, Object> result = new HashMap<>();
DBCollection settingsCollection = db.getCollection(CollectionNames.SETTINGS.name());
try {
@@ -139,6 +139,35 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
return result;
}
@Override
public Map<String, Map<String, String>> loadPreferences() {
Map<String, Map<String, String>> result = new HashMap<>();
DBCollection settingsCollection = db.getCollection(CollectionNames.PREFERENCES.name());
try {
for (Object o : settingsCollection.find()) {
DBObject usernameAndPreferencesMap = (DBObject) o;
Map<String, String> userMap = loadPreferencesMap((BasicDBList) usernameAndPreferencesMap.get(FieldNames.Preferences.KEYS_AND_VALUES.name()));
String username = (String) usernameAndPreferencesMap.get(FieldNames.Preferences.USERNAME.name());
result.put(username, userMap);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Error connecting to MongoDB, unable to load settings.");
logger.log(Level.SEVERE, "loadSettings", e);
}
return result;
}
private Map<String, String> loadPreferencesMap(BasicDBList preferencesDBObject) {
Map<String, String> result = new HashMap<>();
for (Object o : preferencesDBObject) {
DBObject keyValue = (DBObject) o;
String key = (String) keyValue.get(FieldNames.Preferences.KEY.name());
String value = (String) keyValue.get(FieldNames.Preferences.VALUE.name());
result.put(key, value);
}
return result;
}
private Map<String, Object> loadSettingMap(DBObject settingDBObject) {
Map<String, Object> result = new HashMap<>();
Map<?, ?> map = ((DBObject) settingDBObject.get(FieldNames.Settings.MAP.name())).toMap();
@@ -18,6 +18,13 @@ public class FieldNames {
VALUES;
}
public static enum Preferences {
USERNAME,
KEYS_AND_VALUES,
KEY,
VALUE;
}
public static enum Account {
NAME;
}
@@ -5,6 +5,7 @@ import java.util.Map.Entry;
import org.apache.shiro.util.SimpleByteSource;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
@@ -13,9 +14,9 @@ import com.mongodb.WriteConcern;
import com.sap.sse.security.Social;
import com.sap.sse.security.User;
import com.sap.sse.security.shared.Account;
import com.sap.sse.security.shared.Account.AccountType;
import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UsernamePasswordAccount;
import com.sap.sse.security.shared.Account.AccountType;
import com.sap.sse.security.userstore.mongodb.MongoObjectFactory;
public class MongoObjectFactoryImpl implements MongoObjectFactory {
@@ -87,6 +88,23 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
settingCollection.update(query, dbSettings, /* upsrt */true, /* multi */false, WriteConcern.SAFE);
}
@Override
public void storePreferences(String username, Map<String, String> userMap) {
DBCollection settingCollection = db.getCollection(CollectionNames.PREFERENCES.name());
settingCollection.ensureIndex(FieldNames.Preferences.USERNAME.name());
BasicDBList dbSettings = new BasicDBList();
for (Entry<String, String> e : userMap.entrySet()) {
DBObject entry = new BasicDBObject();
entry.put(FieldNames.Preferences.KEY.name(), e.getKey());
entry.put(FieldNames.Preferences.VALUE.name(), e.getValue());
dbSettings.add(entry);
}
DBObject query = new BasicDBObject(FieldNames.Preferences.USERNAME.name(), username);
DBObject update = new BasicDBObject(FieldNames.Preferences.KEYS_AND_VALUES.name(), dbSettings);
update.put(FieldNames.Preferences.USERNAME.name(), username);
settingCollection.update(query, update, /* upsrt */true, /* multi */false, WriteConcern.SAFE);
}
@Override
public void storeSettingTypes(Map<String, Class<?>> settingTypes) {
DBCollection settingCollection = db.getCollection(CollectionNames.SETTINGS.name());
@@ -10,6 +10,7 @@ import javax.servlet.ServletContext;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.mgt.SecurityManager;
import com.sap.sse.security.shared.DefaultRoles;
import com.sap.sse.security.shared.MailException;
import com.sap.sse.security.shared.SocialUserAccount;
import com.sap.sse.security.shared.UserManagementException;
@@ -107,4 +108,17 @@ public interface SecurityService {
boolean validateEmail(String username, String validationSecret) throws UserManagementException;
/**
* Permitted only for users with role {@link DefaultRoles#ADMIN} or when the subject's user name matches
* <code>username</code>.
*/
void setPreference(String username, String key, String value);
/**
* Permitted only for users with role {@link DefaultRoles#ADMIN} or when the subject's user name matches
* <code>username</code>.
*/
void unsetPreference(String username, String key);
String getPreference(String username, String key);
}
@@ -718,4 +718,38 @@ public class SecurityServiceImpl extends RemoteServiceServlet implements Securit
public CacheManager getCacheManager() {
return cacheManager;
}
@Override
public void setPreference(String username, String key, String value) {
Subject subject = SecurityUtils.getSubject();
if (subject.hasRole(DefaultRoles.ADMIN.name()) || username.equals(SessionUtils.loadUsername())) {
store.setPreference(username, key, value);
} else {
throw new SecurityException("User " + SessionUtils.loadUsername()
+ " does not have permission to set preference for user " + username);
}
}
@Override
public void unsetPreference(String username, String key) {
Subject subject = SecurityUtils.getSubject();
if (subject.hasRole(DefaultRoles.ADMIN.name()) || username.equals(SessionUtils.loadUsername())) {
store.unsetPreference(username, key);
} else {
throw new SecurityException("User " + SessionUtils.loadUsername()
+ " does not have permission to unset preference for user " + username);
}
}
@Override
public String getPreference(String username, String key) {
Subject subject = SecurityUtils.getSubject();
if (subject.hasRole(DefaultRoles.ADMIN.name()) || username.equals(SessionUtils.loadUsername())) {
return store.getPreference(username, key);
} else {
throw new SecurityException("User " + SessionUtils.loadUsername()
+ " does not have permission to read preferences of user " + username);
}
}
}
@@ -34,7 +34,13 @@ public interface UserStore {
* is compatible with <code>type</code>. Note that the store implementation may impose constraints on the types supported.
* All store implementations are required to support at least {@link String} and {@link UUID} as types.
*/
public void addSetting(String key, Class<?> type);
void addSetting(String key, Class<?> type);
void setPreference(String username, String key, String value);
void unsetPreference(String username, String key);
String getPreference(String username, String key);
/**
* Sets a value for a key if that key was previously added to this store using {@link #addSetting(String, Class)}.
@@ -51,12 +57,12 @@ public interface UserStore {
* setting because either the key was not registered before by {@link #addSetting(String, Class)} or the type of the
* <code>setting</code> object does not conform to the type used in {@link #addSetting(String, Class)}
*/
public boolean setSetting(String key, Object setting);
boolean setSetting(String key, Object setting);
public <T> T getSetting(String key, Class<T> clazz);
<T> T getSetting(String key, Class<T> clazz);
public Map<String, Object> getAllSettings();
Map<String, Object> getAllSettings();
public Map<String, Class<?>> getAllSettingTypes();
Map<String, Class<?>> getAllSettingTypes();
}
@@ -47,7 +47,7 @@ public class SecurityResource extends AbstractSecurityResource {
@Path("/logout")
public Response logout() {
getService().logout();
return Response.ok("Logger out", MediaType.TEXT_PLAIN).build();
return Response.ok("Logged out", MediaType.TEXT_PLAIN).build();
}
}