/*
* Licensed to Jasig under one or more contributor license
* agreements. See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Jasig 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 the following location:
*
* 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.jasig.cas.services;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
import com.github.inspektr.audit.annotation.Audit;
import org.jasig.cas.authentication.principal.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.constraints.NotNull;
/**
* Default implementation of the {@link ServicesManager} interface. If there are
* no services registered with the server, it considers the ServicecsManager
* disabled and will not prevent any service from using CAS.
*
* @author Scott Battaglia
* @since 3.1
*/
public final class DefaultServicesManagerImpl implements ReloadableServicesManager {
private final Logger logger = LoggerFactory.getLogger(getClass());
/** Instance of ServiceRegistryDao. */
@NotNull
private ServiceRegistryDao serviceRegistryDao;
/** Map to store all services. */
private ConcurrentHashMap<Long, RegisteredService> services = new ConcurrentHashMap<Long, RegisteredService>();
public DefaultServicesManagerImpl(
final ServiceRegistryDao serviceRegistryDao) {
this(serviceRegistryDao, new ArrayList<String>());
}
/**
* Constructs an instance of the {@link DefaultServicesManagerImpl} where the default RegisteredService
* can include a set of default attributes to use if no services are defined in the registry.
*
* @param serviceRegistryDao the Service Registry Dao.
* @param defaultAttributes the list of default attributes to use.
*/
public DefaultServicesManagerImpl(final ServiceRegistryDao serviceRegistryDao,
final List<String> defaultAttributes) {
this.serviceRegistryDao = serviceRegistryDao;
load();
}
@Transactional(readOnly = false)
@Audit(action = "DELETE_SERVICE", actionResolverName = "DELETE_SERVICE_ACTION_RESOLVER",
resourceResolverName = "DELETE_SERVICE_RESOURCE_RESOLVER")
public synchronized RegisteredService delete(final long id) {
final RegisteredService r = findServiceBy(id);
if (r == null) {
return null;
}
this.serviceRegistryDao.delete(r);
this.services.remove(id);
return r;
}
/**
* {@inheritDoc}
* Note, if the repository is empty, this implementation will return a default service to grant all access.
* <p>
* This preserves default CAS behavior.
*/
@Override
public RegisteredService findServiceBy(final Service service) {
final Collection<RegisteredService> c = convertToTreeSet();
for (final RegisteredService r : c) {
if (r.matches(service)) {
return r;
}
}
return null;
}
public RegisteredService findServiceBy(final long id) {
final RegisteredService r = this.services.get(id);
try {
return r == null ? null : r.clone();
} catch (final CloneNotSupportedException e) {
return r;
}
}
protected TreeSet<RegisteredService> convertToTreeSet() {
return new TreeSet<RegisteredService>(this.services.values());
}
public Collection<RegisteredService> getAllServices() {
return Collections.unmodifiableCollection(convertToTreeSet());
}
public boolean matchesExistingService(final Service service) {
return findServiceBy(service) != null;
}
@Transactional(readOnly = false)
@Audit(action = "SAVE_SERVICE", actionResolverName = "SAVE_SERVICE_ACTION_RESOLVER",
resourceResolverName = "SAVE_SERVICE_RESOURCE_RESOLVER")
public synchronized RegisteredService save(final RegisteredService registeredService) {
final RegisteredService r = this.serviceRegistryDao.save(registeredService);
this.services.put(r.getId(), r);
return r;
}
public void reload() {
logger.info("Reloading registered services.");
load();
}
private void load() {
final ConcurrentHashMap<Long, RegisteredService> localServices =
new ConcurrentHashMap<Long, RegisteredService>();
for (final RegisteredService r : this.serviceRegistryDao.load()) {
logger.debug("Adding registered service {}", r.getServiceId());
localServices.put(r.getId(), r);
}
this.services = localServices;
logger.info(String.format("Loaded %s services.", this.services.size()));
}
}