mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-03 01:29:33 +00:00
bug6101: added persistence to CORS filter configuration
This commit is contained in:
1 parent
9e69f505dc
commit
c1922e8068
21 files changed
+244
-56
No files matched your search
+2
-2
@@ -25,8 +25,8 @@ public class SecurityBundleTestWrapper {
|
||||
store.ensureServerGroupExists();
|
||||
final AccessControlStoreImpl accessControlStoreImpl = new AccessControlStoreImpl(store);
|
||||
Activator.setTestStores(store, accessControlStoreImpl);
|
||||
securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, store, accessControlStoreImpl,
|
||||
new MockedHasPermissionProvider(), SSESubscriptionPlan::getAllInstances);
|
||||
securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null, store,
|
||||
accessControlStoreImpl, new MockedHasPermissionProvider(), SSESubscriptionPlan::getAllInstances);
|
||||
((SecurityServiceImpl) securityService).clearState();
|
||||
securityService.initialize();
|
||||
SecurityUtils.setSecurityManager(securityService.getSecurityManager());
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.sap.sse.security.persistence.test;
|
||||
|
||||
import org.junit.Before;
|
||||
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
import com.sap.sse.security.persistence.DomainObjectFactory;
|
||||
import com.sap.sse.security.persistence.MongoObjectFactory;
|
||||
import com.sap.sse.security.persistence.PersistenceFactory;
|
||||
|
||||
public abstract class AbstractSecurityPersistenceTest {
|
||||
protected DomainObjectFactory dof;
|
||||
protected MongoObjectFactory mof;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MongoDBService.INSTANCE.getDB().drop();
|
||||
dof = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory();
|
||||
mof = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory();
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.sap.sse.security.persistence.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
public class CORSFilterConfigurationsPersistenceTest extends AbstractSecurityPersistenceTest {
|
||||
@Test
|
||||
public void testStoringAndLoadingCORSFilterWithWildcard() {
|
||||
final Map<String, Pair<Boolean, Set<String>>> configs = new HashMap<>();
|
||||
final String TESTSERVER = "testserver";
|
||||
configs.put(TESTSERVER, new Pair<>(true, Collections.emptySet()));
|
||||
mof.storeCORSFilterConfigurationIsWildcard(TESTSERVER);
|
||||
assertEquals(configs, dof.loadCORSFilterConfigurationsForReplicaSetNames());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringAndLoadingCORSFilterOneWithWildcardAnotherWithOriginList() {
|
||||
final Map<String, Pair<Boolean, Set<String>>> configs = new HashMap<>();
|
||||
final String TESTSERVER1 = "testserver1";
|
||||
final String TESTSERVER2 = "testserver2";
|
||||
configs.put(TESTSERVER1, new Pair<>(true, Collections.emptySet()));
|
||||
configs.put(TESTSERVER2, new Pair<>(false, Util.asSet(Arrays.asList("https://www.example.com", "https://www.anotherexample.de"))));
|
||||
mof.storeCORSFilterConfigurationIsWildcard(TESTSERVER1);
|
||||
mof.storeCORSFilterConfigurationAllowedOrigins(TESTSERVER2, Util.toArray(configs.get(TESTSERVER2).getB(), new String[0]));
|
||||
assertEquals(configs, dof.loadCORSFilterConfigurationsForReplicaSetNames());
|
||||
}
|
||||
}
|
||||
+1
-17
@@ -12,25 +12,9 @@ import org.apache.shiro.session.Session;
|
||||
import org.apache.shiro.session.mgt.SimpleSession;
|
||||
import org.apache.shiro.subject.PrincipalCollection;
|
||||
import org.apache.shiro.subject.SimplePrincipalCollection;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.mongodb.MongoDBService;
|
||||
import com.sap.sse.security.persistence.DomainObjectFactory;
|
||||
import com.sap.sse.security.persistence.MongoObjectFactory;
|
||||
import com.sap.sse.security.persistence.PersistenceFactory;
|
||||
|
||||
public class SessionPersistenceTest {
|
||||
private DomainObjectFactory dof;
|
||||
private MongoObjectFactory mof;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MongoDBService.INSTANCE.getDB().drop();
|
||||
dof = PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory();
|
||||
mof = PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory();
|
||||
}
|
||||
|
||||
public class SessionPersistenceTest extends AbstractSecurityPersistenceTest {
|
||||
@Test
|
||||
public void testLoadAndStoreAndRemoveSimpleSession() {
|
||||
final String cacheName = "shiroSessionCache";
|
||||
|
||||
+11
@@ -5,6 +5,17 @@ import java.util.Set;
|
||||
|
||||
import org.apache.shiro.session.Session;
|
||||
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
public interface DomainObjectFactory {
|
||||
Map<String, Set<Session>> loadSessionsByCacheName();
|
||||
|
||||
/**
|
||||
* Loads the CORS filter configurations; as we cannot reference the {@code CORSFilterConfiguration} interface from
|
||||
* here without introducing a cyclic dependency, we return the configuration data in the form of a {@link Pair}
|
||||
* whose {@link Pair#getA() first} component is a boolean telling whether the filter uses the "wildcard" (*) to
|
||||
* allow REST requests from all possible origins, and the {@link Pair#getB() second} component lists the allowed
|
||||
* origins in case it's not a wildcard configuration. For wildcard configurations, the second component is ignored.
|
||||
*/
|
||||
Map<String, Pair<Boolean, Set<String>>> loadCORSFilterConfigurationsForReplicaSetNames();
|
||||
}
|
||||
+11
-1
@@ -9,5 +9,15 @@ public interface MongoObjectFactory {
|
||||
void removeSession(String cacheName, Session session);
|
||||
|
||||
void removeAllSessions(String cacheName);
|
||||
|
||||
|
||||
void storeCORSFilterConfigurationIsWildcard(String serverName);
|
||||
|
||||
/**
|
||||
* Makes the CORS filter configuration for the application replica set identified by {@code serverName}
|
||||
* a non-wildcard configuration that accepts REST requests only from those origins listed in
|
||||
* {@code allowedOrigins}.
|
||||
*
|
||||
* @param allowedOrigins {@code null} is allowed and handled like an empty array
|
||||
*/
|
||||
void storeCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins);
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
package com.sap.sse.security.persistence.impl;
|
||||
|
||||
public enum CollectionNames {
|
||||
CACHES, SESSIONS;
|
||||
CACHES, SESSIONS, CORS_FILTER_CONFIGURATIONS;
|
||||
}
|
||||
+17
@@ -1,8 +1,10 @@
|
||||
package com.sap.sse.security.persistence.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
@@ -15,6 +17,7 @@ import org.bson.Document;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.MongoDatabase;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.security.persistence.DomainObjectFactory;
|
||||
@@ -22,9 +25,11 @@ import com.sap.sse.security.persistence.DomainObjectFactory;
|
||||
public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
private static final Logger logger = Logger.getLogger(DomainObjectFactoryImpl.class.getName());
|
||||
private final MongoCollection<Document> sessionsCollection;
|
||||
private final MongoCollection<Document> corsFilterConfigurationsCollection;
|
||||
|
||||
public DomainObjectFactoryImpl(MongoDatabase mongoDatabase) {
|
||||
sessionsCollection = mongoDatabase.getCollection(CollectionNames.SESSIONS.name());
|
||||
corsFilterConfigurationsCollection = mongoDatabase.getCollection(CollectionNames.CORS_FILTER_CONFIGURATIONS.name());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,4 +89,16 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Pair<Boolean, Set<String>>> loadCORSFilterConfigurationsForReplicaSetNames() {
|
||||
final Map<String, Pair<Boolean, Set<String>>> result = new HashMap<>();
|
||||
for (final Document d : corsFilterConfigurationsCollection.find()) {
|
||||
final String serverName = d.getString(FieldNames.CORS_FILTER_CONFIGURATION_SERVER_NAME.name());
|
||||
final boolean isWildcard = d.getBoolean(FieldNames.CORS_FILTER_CONFIGURATION_IS_WILDCARD.name());
|
||||
final List<String> allowedOrigins = isWildcard ? Collections.emptyList() : d.getList(FieldNames.CORS_FILTER_CONFIGURATION_ALLOWED_ORIGINS.name(), String.class);
|
||||
result.put(serverName, new Pair<>(isWildcard, Util.asNewSet(allowedOrigins)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -5,5 +5,7 @@ public enum FieldNames {
|
||||
|
||||
SESSION_ID, SESSION_HOST, SESSION_LAST_ACCESS_TIME, SESSION_START_TIMESTAMP, SESSION_TIMEOUT,
|
||||
SESSION_ATTRIBUTES, SESSION_ATTRIBUTE_NAME, SESSION_ATTRIBUTE_VALUE, SESSION_PRINCIPAL_REALM_NAME,
|
||||
SESSION_PRINCIPAL_REALM_VALUE;
|
||||
SESSION_PRINCIPAL_REALM_VALUE,
|
||||
|
||||
CORS_FILTER_CONFIGURATION_SERVER_NAME, CORS_FILTER_CONFIGURATION_IS_WILDCARD, CORS_FILTER_CONFIGURATION_ALLOWED_ORIGINS;
|
||||
}
|
||||
+26
@@ -2,6 +2,8 @@ package com.sap.sse.security.persistence.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -18,12 +20,16 @@ import com.sap.sse.security.persistence.MongoObjectFactory;
|
||||
public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
private static final Logger logger = Logger.getLogger(MongoObjectFactoryImpl.class.getName());
|
||||
private final MongoCollection<Document> sessionCollection;
|
||||
private final MongoCollection<Document> corsFilterConfigurationsCollection;
|
||||
|
||||
public MongoObjectFactoryImpl(MongoDatabase mongoDatabase) {
|
||||
sessionCollection = mongoDatabase.getCollection(CollectionNames.SESSIONS.name());
|
||||
sessionCollection.createIndex(new Document().
|
||||
append(FieldNames.CACHE_NAME.name(), 1).
|
||||
append(FieldNames.SESSION_ID.name(), 1), new IndexOptions().name("cachenameandsessionid").background(false));
|
||||
corsFilterConfigurationsCollection = mongoDatabase.getCollection(CollectionNames.CORS_FILTER_CONFIGURATIONS.name());
|
||||
corsFilterConfigurationsCollection.createIndex(new Document().
|
||||
append(FieldNames.CORS_FILTER_CONFIGURATION_SERVER_NAME.name(), 1), new IndexOptions().name("servername").background(false));
|
||||
}
|
||||
|
||||
private Document getKey(String cacheName, Session session) {
|
||||
@@ -93,4 +99,24 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
public void removeSession(String cacheName, Session session) {
|
||||
sessionCollection.deleteOne(getKey(cacheName, session));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeCORSFilterConfigurationIsWildcard(String serverName) {
|
||||
final Document filter = new Document().append(FieldNames.CORS_FILTER_CONFIGURATION_SERVER_NAME.name(), serverName);
|
||||
final Document d = new Document()
|
||||
.append(FieldNames.CORS_FILTER_CONFIGURATION_SERVER_NAME.name(), serverName)
|
||||
.append(FieldNames.CORS_FILTER_CONFIGURATION_IS_WILDCARD.name(), true)
|
||||
.append(FieldNames.CORS_FILTER_CONFIGURATION_ALLOWED_ORIGINS.name(), Collections.emptyList());
|
||||
corsFilterConfigurationsCollection.replaceOne(filter, d, new ReplaceOptions().upsert(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins) {
|
||||
final Document filter = new Document().append(FieldNames.CORS_FILTER_CONFIGURATION_SERVER_NAME.name(), serverName);
|
||||
final Document d = new Document()
|
||||
.append(FieldNames.CORS_FILTER_CONFIGURATION_SERVER_NAME.name(), serverName)
|
||||
.append(FieldNames.CORS_FILTER_CONFIGURATION_IS_WILDCARD.name(), false)
|
||||
.append(FieldNames.CORS_FILTER_CONFIGURATION_ALLOWED_ORIGINS.name(), allowedOrigins == null ? Collections.emptyList() : Arrays.asList(allowedOrigins));
|
||||
corsFilterConfigurationsCollection.replaceOne(filter, d, new ReplaceOptions().upsert(true));
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -39,8 +39,8 @@ public abstract class AbstractSecurityReplicationTest extends AbstractServerWith
|
||||
userStore.ensureDefaultRolesExist();
|
||||
userStore.loadAndMigrateUsers();
|
||||
final AccessControlStore accessControlStore = new AccessControlStoreImpl(userStore);
|
||||
SecurityServiceImpl result = new SecurityServiceImpl(null, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
SecurityServiceImpl result = new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ public abstract class AbstractSecurityReplicationTest extends AbstractServerWith
|
||||
userStore.ensureDefaultRolesExist();
|
||||
userStore.loadAndMigrateUsers();
|
||||
final AccessControlStore accessControlStore = new AccessControlStoreImpl(userStore);
|
||||
return new SecurityServiceImpl(/* mailServiceTracker */ null, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
return new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-4
@@ -63,8 +63,8 @@ public class SecurityReplicationLeadingToEmailReplicationTest extends AbstractSe
|
||||
userStore.ensureDefaultRolesExist();
|
||||
userStore.loadAndMigrateUsers();
|
||||
final AccessControlStore accessControlStore = new AccessControlStoreImpl(userStore);
|
||||
SecurityServiceImpl result = new SecurityServiceImpl(trackerMock, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
SecurityServiceImpl result = new SecurityServiceImpl(trackerMock, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
result.initialize();
|
||||
return result;
|
||||
}
|
||||
@@ -77,8 +77,8 @@ public class SecurityReplicationLeadingToEmailReplicationTest extends AbstractSe
|
||||
doReturn(replicaMailService).when(trackerMock).getService();
|
||||
final UserStoreImpl userStore = new UserStoreImpl("TestDefaultTenant");
|
||||
final AccessControlStore accessControlStore = new AccessControlStoreImpl(userStore);
|
||||
SecurityServiceImpl result = new SecurityServiceImpl(trackerMock, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
SecurityServiceImpl result = new SecurityServiceImpl(trackerMock, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
userStore.ensureDefaultRolesExist();
|
||||
userStore.ensureServerGroupExists();
|
||||
result.initialize();
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ public class SecurityServiceInitialLoadTest extends AbstractServerWithSingleServ
|
||||
final AccessControlStore accessControlStore = new AccessControlStoreImpl(
|
||||
PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory(),
|
||||
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory(), userStore);
|
||||
final SecurityServiceImpl newMaster = new SecurityServiceImpl(null, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
final SecurityServiceImpl newMaster = new SecurityServiceImpl(null, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
newMaster.createSimpleUser(username, email, password, fullName, company,
|
||||
/* validationBaseURL */ Locale.ENGLISH, null, null);
|
||||
accessToken = newMaster.createAccessToken(username);
|
||||
|
||||
+3
-3
@@ -66,9 +66,9 @@ public class SecurityResourceTest {
|
||||
store.ensureServerGroupExists();
|
||||
accessControlStore = new AccessControlStoreImpl(store);
|
||||
Activator.setTestStores(store, accessControlStore);
|
||||
service = new SecurityServiceImpl(/* mailServiceTracker */ null, store, accessControlStore,
|
||||
/* hasPermissionsProvider */SecuredSecurityTypes::getAllInstances,
|
||||
SSESubscriptionPlan::getAllInstances);
|
||||
service = new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null, store,
|
||||
accessControlStore,
|
||||
/* hasPermissionsProvider */SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
service.initialize();
|
||||
Activator.setSecurityService(service);
|
||||
SecurityUtils.setSecurityManager(service.getSecurityManager());
|
||||
|
||||
@@ -81,8 +81,8 @@ public class LoginTest {
|
||||
Activator.setTestStores(userStore, accessControlStore);
|
||||
// enables shiro to find classes from com.sap.sse.security
|
||||
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
|
||||
securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
Activator.setSecurityService(securityService);
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -71,8 +71,8 @@ public class PrivilegeEscalationTest {
|
||||
userGroup.add(user2);
|
||||
userStore.updateUserGroup(userGroup);
|
||||
accessControlStore = new AccessControlStoreImpl(userStore);
|
||||
securityService = new SecurityServiceImpl(null, userStore, accessControlStore,
|
||||
SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
securityService = new SecurityServiceImpl(null, /* corsFilterConfigurationTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
securityService.initialize();
|
||||
rd = new RoleDefinitionImpl(UUID.randomUUID(), "some_role",
|
||||
Collections.singleton(type1.getPermission(DefaultActions.READ, DefaultActions.UPDATE)));
|
||||
|
||||
+4
-4
@@ -51,15 +51,15 @@ public class SecurityServiceAndHasPermissionsProviderTest {
|
||||
}
|
||||
|
||||
private SecurityService createSecurityServiceWithoutHasPermissionsProvider() {
|
||||
SecurityService securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, userStore,
|
||||
accessControlStore, /* HasPermissionsProvider */ null, SSESubscriptionPlan::getAllInstances);
|
||||
SecurityService securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null,
|
||||
userStore, accessControlStore, /* HasPermissionsProvider */ null, SSESubscriptionPlan::getAllInstances);
|
||||
securityService.initialize();
|
||||
return securityService;
|
||||
}
|
||||
|
||||
private SecurityService createSecurityServiceWithHasPermissionsProvider() {
|
||||
final SecurityService securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, userStore,
|
||||
accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
final SecurityService securityService = new SecurityServiceImpl(/* mailServiceTracker */ null, /* corsFilterConfigurationTracker */ null,
|
||||
userStore, accessControlStore, SecuredSecurityTypes::getAllInstances, SSESubscriptionPlan::getAllInstances);
|
||||
securityService.initialize();
|
||||
return securityService;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.sap.sse.security.interfaces.PreferenceConverter;
|
||||
import com.sap.sse.security.interfaces.UserImpl;
|
||||
import com.sap.sse.security.interfaces.UserStore;
|
||||
import com.sap.sse.security.operations.SecurityOperation;
|
||||
import com.sap.sse.security.persistence.PersistenceFactory;
|
||||
import com.sap.sse.security.shared.AccessControlListAnnotation;
|
||||
import com.sap.sse.security.shared.BasicUserStore;
|
||||
import com.sap.sse.security.shared.HasPermissions;
|
||||
@@ -831,4 +832,23 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
* that follows the {@link #lockSubscriptionsForUser(User)} call, so a lock can never hang.
|
||||
*/
|
||||
void unlockSubscriptionsForUser(User user);
|
||||
|
||||
/**
|
||||
* For the application replica set identified by {@code serverName} sets the CORS filter to allow REST requests from
|
||||
* all origins (*). The change is made persistent through the {@link PersistenceFactory} and will hence be restored
|
||||
* when this process instance is a primary/master node upon initialization.
|
||||
*/
|
||||
void setCORSFilterConfigurationToWildcard(String serverName);
|
||||
|
||||
/**
|
||||
* For the application replica set identified by {@code serverName} sets the CORS filter to allow REST requests from
|
||||
* the origins named in {@code allowedOrigins}. Passing {@code null} for {@code allowedOrigins} is equivalent to
|
||||
* passing an empty array. The change is made persistent through the {@link PersistenceFactory} and will hence be
|
||||
* restored when this process instance is a primary/master node upon initialization.
|
||||
*
|
||||
* @param allowedOrigins
|
||||
* the origins, including their scheme (e.g., {@code https://www.example.com}) from which REST requests
|
||||
* coming from that browser origin are to be permitted; {@code null} is handled like an empty array
|
||||
*/
|
||||
void setCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins);
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.sap.sse.classloading.ServiceTrackerCustomizerForClassLoaderSupplierRe
|
||||
import com.sap.sse.mail.MailService;
|
||||
import com.sap.sse.replication.Replicable;
|
||||
import com.sap.sse.replication.ReplicationMasterDescriptor;
|
||||
import com.sap.sse.rest.CORSFilterConfiguration;
|
||||
import com.sap.sse.security.SecurityInitializationCustomizer;
|
||||
import com.sap.sse.security.SecurityService;
|
||||
import com.sap.sse.security.SecurityServiceInitialLoadClassLoaderSupplier;
|
||||
@@ -209,9 +210,10 @@ public class Activator implements BundleActivator {
|
||||
bundleContext, HasPermissionsProvider.class, /* customizer */ null);
|
||||
hasPermissionsProviderTracker.open();
|
||||
SecurityService initialSecurityService = new SecurityServiceImpl(
|
||||
ServiceTrackerFactory.createAndOpen(context, MailService.class), userStore, accessControlStore,
|
||||
new OSGIHasPermissionsProvider(hasPermissionsProviderTracker), subscriptionPlanProvider,
|
||||
sharedAcrossSubdomainsOf, baseUrlForCrossDomainStorage);
|
||||
ServiceTrackerFactory.createAndOpen(context, MailService.class),
|
||||
ServiceTrackerFactory.createAndOpen(context, CORSFilterConfiguration.class), userStore,
|
||||
accessControlStore, new OSGIHasPermissionsProvider(hasPermissionsProviderTracker),
|
||||
subscriptionPlanProvider, sharedAcrossSubdomainsOf, baseUrlForCrossDomainStorage);
|
||||
initialSecurityService.initialize();
|
||||
securityService.complete(initialSecurityService);
|
||||
registration = context.registerService(SecurityService.class, initialSecurityService, null);
|
||||
|
||||
@@ -117,4 +117,7 @@ public interface ReplicableSecurityService extends SecurityService {
|
||||
|
||||
Void internalUpdateSubscriptionPlanPrices(Map<String, BigDecimal> updatedItemPrices);
|
||||
|
||||
Void internalSetCORSFilterConfigurationToWildcard(String serverName);
|
||||
|
||||
Void internalSetCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins);
|
||||
}
|
||||
@@ -90,6 +90,7 @@ import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
import com.sap.sse.i18n.impl.ResourceBundleStringMessagesImpl;
|
||||
import com.sap.sse.mail.MailService;
|
||||
import com.sap.sse.replication.interfaces.impl.AbstractReplicableWithObjectInputStream;
|
||||
import com.sap.sse.rest.CORSFilterConfiguration;
|
||||
import com.sap.sse.security.Action;
|
||||
import com.sap.sse.security.ClientUtils;
|
||||
import com.sap.sse.security.GithubApi;
|
||||
@@ -209,6 +210,14 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
*/
|
||||
private final ReplicatingCacheManager cacheManager;
|
||||
|
||||
/**
|
||||
* Keys are the replica set's server names. Values are {@link Pair}s whose {@link Pair#getA() first} component is a
|
||||
* boolean telling whether the CORS filter for the replica set identified by the key uses the "wildcard" (*) to
|
||||
* allow REST requests from all possible origins, and the {@link Pair#getB() second} component lists the allowed
|
||||
* origins in case it's not a wildcard configuration. For wildcard configurations, the second component is ignored.
|
||||
*/
|
||||
private final ConcurrentMap<String, Pair<Boolean, Set<String>>> corsFilterConfigurationsByReplicaSetName;
|
||||
|
||||
private final UserStore store;
|
||||
private final AccessControlStore accessControlStore;
|
||||
|
||||
@@ -216,6 +225,8 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
private boolean isNewServer;
|
||||
|
||||
private final ServiceTracker<MailService, MailService> mailServiceTracker;
|
||||
|
||||
private final ServiceTracker<CORSFilterConfiguration, CORSFilterConfiguration> corsFilterConfigurationTracker;
|
||||
|
||||
private ThreadLocal<UserGroup> temporaryDefaultTenant = new InheritableThreadLocal<>();
|
||||
|
||||
@@ -255,7 +266,6 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
* Creates a security service that is not shared across subdomains, therefore leading to the use of the full
|
||||
* domain through which its services are requested for {@code Document.domain} and hence for the browser local
|
||||
* storage, session storage and the Shiro {@code JSESSIONID} cookie's domain.
|
||||
*
|
||||
* @param setAsActivatorSecurityService
|
||||
* when <code>true</code>, the {@link Activator#setSecurityService(com.sap.sse.security.SecurityService)}
|
||||
* will be called with this new instance as argument so that the cache manager can already be accessed
|
||||
@@ -263,10 +273,12 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
* activator's security service and passes it to the cache entries created. They need it, in turn, for
|
||||
* replication.
|
||||
*/
|
||||
public SecurityServiceImpl(ServiceTracker<MailService, MailService> mailServiceTracker, UserStore userStore,
|
||||
AccessControlStore accessControlStore, HasPermissionsProvider hasPermissionsProvider, SubscriptionPlanProvider subscriptionPlanProvider) {
|
||||
this(mailServiceTracker, userStore, accessControlStore, hasPermissionsProvider, subscriptionPlanProvider,
|
||||
/* sharedAcrossSubdomainsOf */ null, /* baseUrlForCrossDomainStorage */ null);
|
||||
public SecurityServiceImpl(ServiceTracker<MailService, MailService> mailServiceTracker,
|
||||
ServiceTracker<CORSFilterConfiguration, CORSFilterConfiguration> corsFilterConfigurationTracker,
|
||||
UserStore userStore, AccessControlStore accessControlStore, HasPermissionsProvider hasPermissionsProvider,
|
||||
SubscriptionPlanProvider subscriptionPlanProvider) {
|
||||
this(mailServiceTracker, corsFilterConfigurationTracker, userStore, accessControlStore, hasPermissionsProvider,
|
||||
subscriptionPlanProvider, /* sharedAcrossSubdomainsOf */ null, /* baseUrlForCrossDomainStorage */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -275,9 +287,9 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
* the browser local and session store shall be shared and for which sessions identified by the {@code JSESSIONID} cookie shall
|
||||
* be shared as well.
|
||||
*/
|
||||
public SecurityServiceImpl(ServiceTracker<MailService, MailService> mailServiceTracker, UserStore userStore,
|
||||
AccessControlStore accessControlStore, HasPermissionsProvider hasPermissionsProvider, SubscriptionPlanProvider subscriptionPlanProvider,
|
||||
String sharedAcrossSubdomainsOf, String baseUrlForCrossDomainStorage) {
|
||||
public SecurityServiceImpl(ServiceTracker<MailService, MailService> mailServiceTracker, ServiceTracker<CORSFilterConfiguration, CORSFilterConfiguration> corsFilterConfigurationTracker,
|
||||
UserStore userStore, AccessControlStore accessControlStore, HasPermissionsProvider hasPermissionsProvider,
|
||||
SubscriptionPlanProvider subscriptionPlanProvider, String sharedAcrossSubdomainsOf, String baseUrlForCrossDomainStorage) {
|
||||
initialLoadClassLoaderRegistry.addClassLoader(getClass().getClassLoader());
|
||||
if (hasPermissionsProvider == null) {
|
||||
throw new IllegalArgumentException("No HasPermissionsProvider defined");
|
||||
@@ -290,8 +302,10 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
this.store = userStore;
|
||||
this.accessControlStore = accessControlStore;
|
||||
this.mailServiceTracker = mailServiceTracker;
|
||||
this.corsFilterConfigurationTracker = corsFilterConfigurationTracker;
|
||||
this.hasPermissionsProvider = hasPermissionsProvider;
|
||||
cacheManager = loadReplicationCacheManagerContents();
|
||||
this.cacheManager = loadReplicationCacheManagerContents();
|
||||
this.corsFilterConfigurationsByReplicaSetName = loadCORSFilterConfigurations();
|
||||
logger.info("Loaded shiro.ini file from: classpath:shiro.ini");
|
||||
final StringBuilder logMessage = new StringBuilder("[urls] section from Shiro configuration:");
|
||||
final Section urlsSection = shiroConfiguration.getSection("urls");
|
||||
@@ -338,6 +352,13 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
return result;
|
||||
}
|
||||
|
||||
private ConcurrentMap<String, Pair<Boolean, Set<String>>> loadCORSFilterConfigurations() {
|
||||
logger.info("Loading CORS filter configurations");
|
||||
final ConcurrentMap<String, Pair<Boolean, Set<String>>> result = new ConcurrentHashMap<>();
|
||||
result.putAll(PersistenceFactory.INSTANCE.getDefaultDomainObjectFactory().loadCORSFilterConfigurationsForReplicaSetNames());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends HasPermissions> getAllHasPermissions() {
|
||||
return hasPermissionsProvider.getAllHasPermissions();
|
||||
@@ -447,6 +468,10 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
return mailServiceTracker == null ? null : mailServiceTracker.getService();
|
||||
}
|
||||
|
||||
private CORSFilterConfiguration getCORSFilterConfiguration() {
|
||||
return corsFilterConfigurationTracker == null ? null : corsFilterConfigurationTracker.getService();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendMail(String username, String subject, String body) throws MailException {
|
||||
final User user = getUserByName(username);
|
||||
@@ -2184,6 +2209,37 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCORSFilterConfigurationToWildcard(String serverName) {
|
||||
apply(s->s.internalSetCORSFilterConfigurationToWildcard(serverName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalSetCORSFilterConfigurationToWildcard(String serverName) {
|
||||
if (Util.equalsWithNull(serverName, ServerInfo.getName())) {
|
||||
getCORSFilterConfiguration().setWildcard();
|
||||
}
|
||||
corsFilterConfigurationsByReplicaSetName.put(serverName, new Pair<>(true, Collections.emptySet()));
|
||||
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory().storeCORSFilterConfigurationIsWildcard(serverName);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins) {
|
||||
apply(s->s.internalSetCORSFilterConfigurationAllowedOrigins(serverName, allowedOrigins));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Void internalSetCORSFilterConfigurationAllowedOrigins(String serverName, String... allowedOrigins) {
|
||||
final Iterable<String> allowedOriginsAsList = allowedOrigins == null ? Collections.emptyList() : Arrays.asList(allowedOrigins);
|
||||
if (Util.equalsWithNull(serverName, ServerInfo.getName())) {
|
||||
getCORSFilterConfiguration().setOrigins(allowedOriginsAsList);
|
||||
}
|
||||
corsFilterConfigurationsByReplicaSetName.put(serverName, new Pair<>(false, Util.asNewSet(allowedOriginsAsList)));
|
||||
PersistenceFactory.INSTANCE.getDefaultMongoObjectFactory().storeCORSFilterConfigurationAllowedOrigins(serverName, allowedOrigins);
|
||||
return null;
|
||||
}
|
||||
|
||||
// ----------------- Replication -------------
|
||||
@Override
|
||||
@@ -3022,7 +3078,7 @@ implements ReplicableSecurityService, ClearStateTestSupport {
|
||||
for (SubscriptionPlan subscriptionPlan : allSubscriptionPlans.values()) {
|
||||
for (SubscriptionPrice subscriptionPrice : subscriptionPlan.getPrices()) {
|
||||
final BigDecimal updatedPrice = updatedItemPrices.get(subscriptionPrice.getPriceId());
|
||||
if(updatedPrice != null) {
|
||||
if (updatedPrice != null) {
|
||||
logger.log(Level.INFO, "Setting ItemPrice for SubscriptionPrice " + subscriptionPrice.getPriceId());
|
||||
subscriptionPrice.setPrice(updatedPrice);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user