blob: ab999f21da788bc9c63d3dc026252d7ddc0ebe73 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.run.osgi
import org.apache.felix.connect.launch.PojoServiceRegistry
import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters
import org.apache.jackrabbit.oak.spi.security.Context
import org.apache.jackrabbit.oak.spi.security.SecurityProvider
import org.apache.jackrabbit.oak.spi.security.authentication.token.TokenConfiguration
import org.apache.jackrabbit.oak.spi.security.authorization.AuthorizationConfiguration
import org.apache.jackrabbit.oak.spi.security.authorization.restriction.RestrictionProvider
import org.apache.jackrabbit.oak.spi.security.principal.PrincipalConfiguration
import org.apache.jackrabbit.oak.spi.security.user.AuthorizableNodeName
import org.apache.jackrabbit.oak.spi.security.user.UserAuthenticationFactory
import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration
import org.apache.jackrabbit.oak.spi.security.user.action.AccessControlAction
import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableAction
import org.apache.jackrabbit.oak.spi.security.user.action.AuthorizableActionProvider
import org.junit.Before
import org.junit.Test
import org.osgi.framework.ServiceEvent
import org.osgi.framework.ServiceReference
import org.osgi.service.cm.ConfigurationAdmin
import static org.mockito.Mockito.mock
import static org.mockito.Mockito.when
import static org.osgi.service.component.runtime.ServiceComponentRuntime.*
class SecurityProviderRegistrationTest extends AbstractRepositoryFactoryTest {
private PojoServiceRegistry registry;
@Before
public void initializeRegistry() {
registry = repositoryFactory.initializeServiceRegistry(config)
}
@Override
protected PojoServiceRegistry getRegistry() {
return registry
}
/**
* Test that, without any additional configuration, a SecurityProvider
* service is registered by default.
*/
@Test
public void testDefaultSetup() {
assert securityProviderServiceReferences != null
}
/**
* A SecurityProvider shouldn't start without a required
* AuthorizationConfiguration service.
*/
@Test
public void testRequiredAuthorizationConfigurationNotAvailable() {
def m = mock(AuthorizationConfiguration)
when(m.getParameters()).thenReturn(ConfigurationParameters.EMPTY)
when(m.getContext()).thenReturn(Context.DEFAULT)
testRequiredService(AuthorizationConfiguration, m)
}
/**
* A SecurityProvider shouldn't start without a required
* PrincipalConfiguration service.
*/
@Test
public void testRequiredPrincipalConfigurationNotAvailable() {
def m = mock(PrincipalConfiguration)
when(m.getParameters()).thenReturn(ConfigurationParameters.EMPTY)
when(m.getContext()).thenReturn(Context.DEFAULT)
testRequiredService(PrincipalConfiguration, m)
}
/**
* A SecurityProvider shouldn't start without a required TokenConfiguration
* service.
*/
@Test
public void testRequiredTokenConfigurationNotAvailable() {
def m = mock(TokenConfiguration)
when(m.getParameters()).thenReturn(ConfigurationParameters.EMPTY)
when(m.getContext()).thenReturn(Context.DEFAULT)
testRequiredService(TokenConfiguration, m)
}
/**
* A SecurityProvider shouldn't start without a required
* AuthorizableNodeName service.
*/
@Test
public void testRequiredAuthorizableNodeNameNotAvailable() {
testRequiredService(AuthorizableNodeName, mock(AuthorizableNodeName))
}
/**
* A SecurityProvider shouldn't start without a required
* AuthorizableActionProvider service.
*/
@Test
public void testRequiredAuthorizableActionProviderNotAvailable() {
testRequiredService(AuthorizableActionProvider, mock(AuthorizableActionProvider))
}
/**
* A SecurityProvider shouldn't start without a required RestrictionProvider
* service.
*/
@Test
public void testRequiredRestrictionProviderNotAvailable() {
testRequiredService(RestrictionProvider, mock(RestrictionProvider))
}
/**
* A SecurityProvider shouldn't start without a required
* UserAuthenticationFactory service.
*/
@Test
public void testRequiredUserAuthenticationFactoryNotAvailable() {
testRequiredService(UserAuthenticationFactory, mock(UserAuthenticationFactory))
}
/**
* A SecurityProvider should be registered only if every every prerequisite
* is satisfied.
*/
@Test
public void testMultipleRequiredServices() {
// Set up the SecurityProvider to require 4 services
awaitServiceEvent({
setRequiredServicePids(
"test.RequiredAuthorizationConfiguration",
"test.RequiredPrincipalConfiguration",
"test.RequiredTokenConfiguration",
"test.RestrictionProvider")
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.UNREGISTERING
)
assert securityProviderServiceReferences == null
// Start the services and verify that only at the end the
// SecurityProvider registers itself
def ac = mock(AuthorizationConfiguration)
when(ac.getParameters()).thenReturn(ConfigurationParameters.EMPTY)
when(ac.getContext()).thenReturn(Context.DEFAULT)
registry.registerService(AuthorizationConfiguration.class.name, ac, dict("service.pid": "test.RequiredAuthorizationConfiguration"))
assert securityProviderServiceReferences == null
def pc = mock(PrincipalConfiguration)
when(pc.getParameters()).thenReturn(ConfigurationParameters.EMPTY)
when(pc.getContext()).thenReturn(Context.DEFAULT)
registry.registerService(PrincipalConfiguration.class.name, pc, dict("service.pid": "test.RequiredPrincipalConfiguration"))
assert securityProviderServiceReferences == null
def tc = mock(TokenConfiguration)
when(tc.getParameters()).thenReturn(ConfigurationParameters.EMPTY)
when(tc.getContext()).thenReturn(Context.DEFAULT)
registry.registerService(TokenConfiguration.class.name, tc, dict("service.pid": "test.RequiredTokenConfiguration"))
assert securityProviderServiceReferences == null
registry.registerService(RestrictionProvider.class.name, mock(RestrictionProvider), dict("service.pid": "test.RestrictionProvider"))
assert securityProviderServiceReferences != null
}
@Test
public void testSecurityConfigurations() {
SecurityProvider securityProvider = registry.getService(registry.getServiceReference(SecurityProvider.class.name))
assertAuthorizationConfig(securityProvider)
assertUserConfig(securityProvider)
//Keep a dummy reference to UserConfiguration such that SCR does not deactivate it
//once SecurityProviderRegistration gets deactivated. Otherwise if SecurityProviderRegistration is
//the only component that refers it then upon its deactivation UserConfiguration would also be
//deactivate and its internal state would be reset
UserConfiguration userConfiguration = getServiceWithWait(UserConfiguration.class)
def authComponentName = 'org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl'
// 1. Disable AuthenticationConfiguration such that SecurityProvider is unregistered
awaitServiceEvent({
disableComponent(authComponentName)
},
'(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)',
ServiceEvent.UNREGISTERING,
5000
)
assert securityProviderServiceReferences == null
// 2. Modify the config for AuthorizableActionProvider. It's expected that this config change is picked up
awaitServiceEvent({
setConfiguration([
"org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider": [
"groupPrivilegeNames":"jcr:read"
]
])
},
classNameFilter('org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider'),
ServiceEvent.MODIFIED | ServiceEvent.REGISTERED
)
// 3. Enable component again such that SecurityProvider gets reactivated
awaitServiceEvent({
enableComponent(authComponentName)
},
'(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)',
ServiceEvent.REGISTERED
)
securityProvider = getServiceWithWait(SecurityProvider.class)
assertAuthorizationConfig(securityProvider)
assertUserConfig(securityProvider, "jcr:read")
}
@Test
public void testSecurityConfigurations2() {
SecurityProvider securityProvider = registry.getService(registry.getServiceReference(SecurityProvider.class.name))
assertAuthorizationConfig(securityProvider)
assertUserConfig(securityProvider)
//Keep a dummy reference to UserConfiguration such that SCR does not deactivate it
//once SecurityProviderRegistration gets deactivated. Otherwise if SecurityProviderRegistration is
//the only component that refers it then upon its deactivation UserConfiguration would also be
//deactivate and its internal state would be reset
UserConfiguration userConfiguration = getServiceWithWait(UserConfiguration.class)
//1. Modify the config for AuthorizableActionProvider. It's expected that this config change is picked up
def servicePid = "org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider"
awaitServiceEvent({
setConfiguration([
(servicePid): [
"groupPrivilegeNames":"jcr:read"
]
]
)},
"(service.pid=${servicePid})",
ServiceEvent.MODIFIED | ServiceEvent.REGISTERED
)
securityProvider = getServiceWithWait(SecurityProvider.class)
assertAuthorizationConfig(securityProvider)
assertUserConfig(securityProvider, "jcr:read")
String authComponentName = 'org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl'
// 2. Disable AuthenticationConfiguration such that SecurityProvider is unregistered
awaitServiceEvent({
disableComponent(authComponentName)
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.UNREGISTERING
)
assert securityProviderServiceReferences == null
// 3. Enable component again such that SecurityProvider gets reactivated
awaitServiceEvent({
enableComponent(authComponentName)
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.REGISTERED
)
securityProvider = getServiceWithWait(SecurityProvider.class)
assertAuthorizationConfig(securityProvider)
assertUserConfig(securityProvider, "jcr:read")
}
private <T> void testRequiredService(Class<T> serviceClass, T service) {
// Adding a new precondition on a missing service PID forces the
// SecurityProvider to unregister.
awaitServiceEvent({
setRequiredServicePids("test.Required" + serviceClass.simpleName)
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.UNREGISTERING
)
assert securityProviderServiceReferences == null
// If a service is registered, and if the PID of the service matches the
// precondition, the SecurityProvider is registered again.
def registration;
awaitServiceEvent({
registration = registry.registerService(serviceClass.name, service, dict("service.pid": "test.Required" + serviceClass.simpleName))
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.REGISTERED
)
assert securityProviderServiceReferences != null
// If the service is unregistered, but the precondition is still in
// place, the SecurityProvider unregisters again.
awaitServiceEvent({
registration.unregister()
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.UNREGISTERING
)
assert securityProviderServiceReferences == null
// Removing the precondition allows the SecurityProvider to register.
awaitServiceEvent({
setRequiredServicePids()
},
"(objectClass=org.apache.jackrabbit.oak.spi.security.SecurityProvider)",
ServiceEvent.REGISTERED
)
assert securityProviderServiceReferences != null
}
private ServiceReference<?>[] getSecurityProviderServiceReferences() {
return registry.getServiceReferences(SecurityProvider.class.name, "(type=default)")
}
private void setRequiredServicePids(String... pids) {
setConfiguration([
"org.apache.jackrabbit.oak.security.internal.SecurityProviderRegistration": [
"requiredServicePids": pids
]
])
}
private void setConfiguration(Map<String, Map<String, Object>> configuration) {
getConfigurationInstaller().installConfigs(configuration)
}
private ConfigInstaller getConfigurationInstaller() {
return new ConfigInstaller(getConfigurationAdmin(), registry.bundleContext)
}
private ConfigurationAdmin getConfigurationAdmin() {
return registry.getService(registry.getServiceReference(ConfigurationAdmin.class.name)) as ConfigurationAdmin
}
private static <K, V> Dictionary<K, V> dict(Map<K, V> map) {
return new Hashtable<K, V>(map);
}
private static void assertAuthorizationConfig(SecurityProvider securityProvider, String... adminPrincipals) {
AuthorizationConfiguration ac = securityProvider.getConfiguration(AuthorizationConfiguration.class)
assert ac.getParameters().containsKey("restrictionProvider")
assert ac.getRestrictionProvider() != null
assert ac.getParameters().get("restrictionProvider") == ac.getRestrictionProvider()
String[] found = ac.getParameters().getConfigValue("administrativePrincipals", new String[0])
assert Arrays.equals(adminPrincipals, found)
}
private static void assertUserConfig(SecurityProvider securityProvider, String... groupPrivilegeNames) {
UserConfiguration uc = securityProvider.getConfiguration(UserConfiguration.class)
assert uc.getParameters().containsKey("authorizableActionProvider")
AuthorizableActionProvider ap = uc.getParameters().get("authorizableActionProvider")
assert ap != null;
List<AuthorizableAction> actionList = ap.getAuthorizableActions(securityProvider)
assert actionList.size() == 1
AuthorizableAction action = actionList.get(0)
assert Arrays.equals(groupPrivilegeNames, ((AccessControlAction) action).groupPrivilegeNames)
}
}