Skip to content

Commit

Permalink
Implement service registry auto-initialization (apereo#1771)
Browse files Browse the repository at this point in the history
* Implement JPA service registry auto-initialization

* Overlooked local dev changes pushed

* updated registries to implement size; moved initializer over

* fixed refreshscope issue

* fixed refreshscope issue

* Updated docs

* fixed cloud dependency conflicts

* fixed cloud dependency conflicts

* added amqp settings for the cloud bus

* added amqp settings for the cloud bus

* changed to ctor injection

* given that configuration classes are not able to pick up injections at the ctor level,

switched back to Component, which then helped to set the class as package private

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs

* fixed cs errors with javadocs
  • Loading branch information
dima767 authored and SavvasMisaghMoayyed committed May 18, 2016
1 parent 8d2b19a commit a1b21f4
Show file tree
Hide file tree
Showing 49 changed files with 397 additions and 296 deletions.
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ buildscript {
classpath "nl.eveoh:gradle-aspectj:1.6"
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
classpath "com.netflix.nebula:gradle-lint-plugin:0.27.0"
classpath "org.standardout:gradle-versioneye-plugin:1.3.0"
classpath "org.standardout:gradle-versioneye-plugin:1.4.0"
}
}

Expand Down Expand Up @@ -147,6 +147,7 @@ subprojects {

versioneye {
includePlugins = false
includeSubProjects = true
}

findbugs {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Registry of all RegisteredServices.
*
* @author Scott Battaglia
* @author Dmitriy Kopylenko
* @since 3.1
*/
public interface ServiceRegistryDao {
Expand Down Expand Up @@ -41,4 +41,13 @@ public interface ServiceRegistryDao {
* @return the registered service
*/
RegisteredService findServiceById(long id);

/**
* Return number of records held in this service registry. Provides Java 8 supported default implementation so that implementations
* needed this new functionality could override it and other implementations not caring for it could be left alone.
*
* @return number of registered services held by any particular implementation
* @since 5.0.0
*/
long size();
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.CascadeType;
import javax.persistence.Column;
Expand All @@ -24,7 +22,6 @@
import javax.persistence.OneToMany;
import javax.persistence.PostLoad;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
Expand All @@ -48,11 +45,7 @@
public abstract class AbstractRegisteredService implements RegisteredService, Comparable<RegisteredService> {

private static final long serialVersionUID = 7645279151115635245L;

/** The logger instance. */
@Transient
protected transient Logger logger = LoggerFactory.getLogger(this.getClass());


/** The unique identifier for this service. */
@Column(length = 255, updatable = true, insertable = true, nullable = false)
protected String serviceId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ public class InMemoryServiceRegistryDaoImpl implements ServiceRegistryDao {
*/
public InMemoryServiceRegistryDaoImpl() {
}



/**
* After properties set.
*/
Expand Down Expand Up @@ -106,7 +105,7 @@ private long findHighestId() {
return this.registeredServices.stream().map(RegisteredService::getId).max(Comparator.naturalOrder()).orElse((long) 0);
}

private void logWarning() {
private static void logWarning() {
LOGGER.debug("Runtime memory is used as the persistence storage for retrieving and persisting service definitions. "
+ "Changes that are made to service definitions during runtime "
+ "will be LOST upon container restarts.");
Expand All @@ -116,4 +115,9 @@ private void logWarning() {
public String toString() {
return getClass().getSimpleName();
}

@Override
public long size() {
return registeredServices.size();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,11 @@ public RegisteredService findServiceById(final long id) {
return this.serviceMap.get(id);
}

@Override
public long size() {
return this.serviceMap.size();
}

/**
* Load registered service from file.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package org.apereo.cas.services;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;


/**
* Initializes Jpa service registry database with available JSON service definitions if necessary (based on configuration flag).
*
* @author Dmitriy Kopylenko
* @author Misagh Moayyed
* @since 5.0.0
*/
@Component("serviceRegistryInitializer")
class ServiceRegistryInitializer {

private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRegistryInitializer.class);

private final ServiceRegistryDao serviceRegistryDao;

private final ServiceRegistryDao jsonServiceRegistryDao;

@Value("${svcreg.database.from.json:true}")
private boolean initFromJsonIfAvailable;

@Autowired
ServiceRegistryInitializer(@Qualifier("jsonServiceRegistryDao")
final ServiceRegistryDao jsonServiceRegistryDao,
@Qualifier("serviceRegistryDao")
final ServiceRegistryDao serviceRegistryDao) {
this.jsonServiceRegistryDao = jsonServiceRegistryDao;
this.serviceRegistryDao = serviceRegistryDao;
}

/**
* Init service registry if necessary.
*/
@PostConstruct
public void initServiceRegistryIfNecessary() {

if (this.serviceRegistryDao.equals(this.jsonServiceRegistryDao)) {
return;
}

final long size = this.serviceRegistryDao.size();

if (size == 0) {
if (this.initFromJsonIfAvailable) {
LOGGER.debug("Service registry database will be auto-initialized from default JSON services");
this.jsonServiceRegistryDao.load().forEach(r -> {
LOGGER.debug("Initializing DB with the {} JSON service definition...", r);
this.serviceRegistryDao.save(r);
});
this.serviceRegistryDao.load();
} else {
LOGGER.info("The service registry database will not be initialized from default JSON services. "
+ "Since the service registry database is empty, CAS will refuse to authenticate services "
+ "until service definitions are added to the database.");
}
} else {
LOGGER.debug("Service registry database contains {} service definitions", size);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ public class ServiceTicketImpl extends AbstractTicket implements ServiceTicket {
@Column(name="TICKET_ALREADY_GRANTED", nullable=false)
private Boolean grantedTicketAlready = Boolean.FALSE;

private transient Object lock = new Object();

/**
* Instantiates a new service ticket impl.
*/
Expand Down Expand Up @@ -133,7 +131,7 @@ public boolean equals(final Object object) {
public ProxyGrantingTicket grantProxyGrantingTicket(
final String id, final Authentication authentication,
final ExpirationPolicy expirationPolicy) throws AbstractTicketException {
synchronized (this.lock) {
synchronized (this) {
if(this.grantedTicketAlready) {
throw new InvalidProxyGrantingTicketForServiceTicket(this.service);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
* No-Op cipher executor that does nothing for encryption/decryption.
* @author Misagh Moayyed
* @since 4.1
*/
@Component("noOpCipherExecutor")
public class NoOpCipherExecutor extends AbstractCipherExecutor<String, String> {

private static final Logger LOGGER = LoggerFactory.getLogger(NoOpCipherExecutor.class);
Expand All @@ -19,7 +21,7 @@ public class NoOpCipherExecutor extends AbstractCipherExecutor<String, String> {
public NoOpCipherExecutor() {
super(NoOpCipherExecutor.class.getName());
LOGGER.warn("[{}] does no encryption and may NOT be safe in a production environment. "
+ "Consider using other choices, such as [{}] that handle encryption, signing and verification of "
+ "Consider using other choices, such as [{}] to handle encryption, signing and verification of "
+ "all appropriate values.", this.getClass().getName(), BaseStringCipherExecutor.class.getName());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ serves as a reference and a placeholder for all settings that are available to C

CAS is also configured to load `*.properties` files from an external location that is `/etc/cas/config`. This location is constantly
monitored by CAS to detect external changes. Note that this location simply needs to exist, and does not require any special permissions
or structure. The names of the configuration files that go inside this directory also do not matter, and they can be many.
or structure. The names of the configuration files that go inside this directory also do not matter, and there can be many.

The configuration of this behavior is controlled via the `bootstrap.properties` file:

Expand Down Expand Up @@ -64,7 +64,7 @@ Needless to say, the repositories could use both YAML and Properties syntax to h

<div class="alert alert-info"><strong>Keep What You Need!</strong><p>Again, in all of the above strategies,
an adopter is encouraged to only keep and maintain properties needed for their particular deployment. It is
unnecessary to grab a copy of all CAS settings and move it to an external location. Settings that are
unnecessary to grab a copy of all CAS settings and move them to an external location. Settings that are
defined by the external configuration location or repository are able to override what is provided by CAS
as a default.</p></div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,20 +74,15 @@ You should then grab each generated key for encryption and signing, and put them

If you wish you manually generate keys, you may [use the following tool](https://github.com/mitreid-connect/json-web-key-generator).

### Disable Encryption

If you wish to turn off cookie encryption, adjust your configuration to be the following:

In local `deployerConfigContext.xml`:

```xml
<bean id="noOpCipherExecutor" class="org.apereo.cas.util.NoOpCipherExecutor" />
```

In `application.properties`:

```properties
#CAS components mappings
defaultCookieCipherExecutor=noOpCipherExecutor
cookieCipherExecutor=noOpCipherExecutor
```

## Cookie Generation for Renewed Authentications
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,13 @@ redundancy and replication as per normal Couchbase configuration.
The only truly mandatory setting is the list of nodes.
The other settings are optional, but this is designed to store data in buckets
so in reality the bucket property must also be set.

## Auto Initialization

Upon startup and if the services registry database is blank,
the registry is able to auto initialize itself from default
JSON service definitions available to CAS. This behavior can be controlled via:

```properties
# svcreg.database.from.json=false
```
10 changes: 10 additions & 0 deletions cas-server-documentation/installation/JPA-Service-Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,13 @@ In `application.properties`:
#CAS components mappings
serviceRegistryDao=jpaServiceRegistryDao
```

## Auto Initialization

Upon startup and if the services registry database is blank,
the registry is able to auto initialize itself from default
JSON service definitions available to CAS. This behavior can be controlled via:

```properties
# svcreg.database.from.json=false
```
10 changes: 10 additions & 0 deletions cas-server-documentation/installation/LDAP-Service-Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,13 @@ The following settings are applicable:
```properties
svcreg.ldap.baseDn=dc=example,dc=org
```

## Auto Initialization

Upon startup and if the services registry database is blank,
the registry is able to auto initialize itself from default
JSON service definitions available to CAS. This behavior can be controlled via:

```properties
# svcreg.database.from.json=false
```
21 changes: 16 additions & 5 deletions cas-server-documentation/installation/Mongo-Service-Management.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,20 @@ serviceRegistryDao=mongoServiceRegistryDao
The following configuration in `application.properties` is required.

```properties
mongodb.host=mongodb database url
mongodb.port=mongodb database port
mongodb.userId=mongodb userid to bind
mongodb.userPassword=mongodb password to bind
cas.service.registry.mongo.db=Collection name to store service definitions
# mongodb.host=mongodb database url
# mongodb.port=mongodb database port
# mongodb.userId=mongodb userid to bind
# mongodb.userPassword=mongodb password to bind
# cas.service.registry.mongo.db=Collection name to store service definitions
```

## Auto Initialization

Upon startup and if the services registry database is blank,
the registry is able to auto initialize itself from default
JSON service definitions available to CAS. This behavior can be controlled via:

```properties
# svcreg.database.from.json=false
```

Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ when you use the above ticket registries. It requires explicit configuration bef

## Configuration

Each ticket registry configuration supports a cipher component that needs to be configured by the deployer. A typical cipher configuration
may be the following, placed into the configuration:
Each ticket registry configuration supports a cipher component that needs to be configured by the deployer.

```xml
<alias name="defaultTicketCipherExecutor" alias="ticketCipherExecutor" />
```properties
#CAS components mappings
ticketCipherExecutor=defaultTicketCipherExecutor
```

The settings, algorithms and secret keys used for the cipher may be controlled via `application.properties`:
Expand Down
Loading

0 comments on commit a1b21f4

Please sign in to comment.