bug6101: added persistence to CORS filter configuration

This commit is contained in:
Axel Uhl committed 2025-03-12 11:25:46 +01:00
1 parent 9e69f505dc
commit c1922e8068
21 files changed
+244 -56

No files matched your search

@@ -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());
@@ -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();
}
}
@@ -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());
}
}
@@ -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";
@@ -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();
}
@@ -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,5 +1,5 @@
package com.sap.sse.security.persistence.impl;
public enum CollectionNames {
CACHES, SESSIONS;
CACHES, SESSIONS, CORS_FILTER_CONFIGURATIONS;
}
@@ -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;
}
}
@@ -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;
}
@@ -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));
}
}
@@ -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);
}
}
}
@@ -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();
@@ -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);
@@ -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);
}
@@ -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)));
@@ -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);
}