Skip to content

Commit

Permalink
Merge branch '6.6.x' of github.com:apereo/cas into 6.6.x
Browse files Browse the repository at this point in the history
# Conflicts:
#	gradle.properties
  • Loading branch information
mmoayyed committed Dec 30, 2022
2 parents 13e5a70 + 1fe5556 commit aafef82
Show file tree
Hide file tree
Showing 22 changed files with 399 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -146,4 +146,15 @@ public class SpnegoProperties implements Serializable {
@NestedConfigurationProperty
private WebflowAutoConfigurationProperties webflow = new WebflowAutoConfigurationProperties().setOrder(100);

/**
* The size of the pool used to validate SPNEGO tokens.
* A pool is used to provider better performance than what was previously offered by the simple Lombok {@code Synchronized} annotation.
*/
private int poolSize = 10;

/**
* The timeout of the pool used to validate SPNEGO tokens.
*/
@DurationCapable
private String poolTimeout = "PT2S";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.apereo.cas.util;

import org.slf4j.Logger;

/**
* This is {@link LogMessageSummarizer}.
*
* @author Hal Deadman
* @since 6.6.0
*/
public interface LogMessageSummarizer {

/**
* Method to let summarizer determine whether to summarize or not.
* @param logger Logger logging the message
* @return true True if should summarize
*/
boolean shouldSummarize(Logger logger);

/**
* Summarize stack trace.
* @param message Log message
* @param throwable Throwable to summarize
* @return Summarized Message
*/
String summarizeStackTrace(String message, Throwable throwable);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,10 @@
import org.apereo.cas.authentication.principal.Service;
import org.apereo.cas.configuration.support.ExpressionLanguageCapable;
import org.apereo.cas.util.CollectionUtils;
import org.apereo.cas.util.ResourceUtils;
import org.apereo.cas.util.function.FunctionUtils;
import org.apereo.cas.util.scripting.ExecutableCompiledGroovyScript;
import org.apereo.cas.util.scripting.GroovyShellScript;
import org.apereo.cas.util.scripting.ScriptingUtils;
import org.apereo.cas.util.scripting.WatchableGroovyScriptResource;
import org.apereo.cas.util.spring.SpringExpressionLanguageValueResolver;

import org.apereo.cas.util.spring.ApplicationContextProvider;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -25,8 +17,6 @@
import lombok.val;
import org.apache.commons.lang3.StringUtils;

import javax.persistence.PostLoad;
import javax.persistence.Transient;

/**
* Resolves the username for the service to be the default principal id.
Expand All @@ -39,7 +29,6 @@
@Setter
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
@AllArgsConstructor
@Accessors(chain = true)
public class GroovyRegisteredServiceUsernameProvider extends BaseRegisteredServiceUsernameAttributeProvider {

Expand All @@ -48,53 +37,46 @@ public class GroovyRegisteredServiceUsernameProvider extends BaseRegisteredServi
@ExpressionLanguageCapable
private String groovyScript;

@JsonIgnore
@Transient
@org.springframework.data.annotation.Transient
private transient ExecutableCompiledGroovyScript executableScript;

@JsonCreator
public GroovyRegisteredServiceUsernameProvider(@JsonProperty("groovyScript") final String script) {
this.groovyScript = script;
}

@PostLoad
private void initializeWatchableScriptIfNeeded() {
if (this.executableScript == null) {
val matcherInline = ScriptingUtils.getMatcherForInlineGroovyScript(groovyScript);
val matcherFile = ScriptingUtils.getMatcherForExternalGroovyScript(groovyScript);
private static String fetchAttributeValue(final Principal principal,
final Service service,
final RegisteredService registeredService,
final String groovyScript) {

if (matcherFile.find()) {
val script = SpringExpressionLanguageValueResolver.getInstance().resolve(matcherFile.group());
val resource = FunctionUtils.doUnchecked(() -> ResourceUtils.getRawResourceFrom(script));
this.executableScript = new WatchableGroovyScriptResource(resource);
} else if (matcherInline.find()) {
this.executableScript = new GroovyShellScript(matcherInline.group(1));
}
}
return ApplicationContextProvider.getScriptResourceCacheManager()
.map(cacheMgr -> {
val script = cacheMgr.resolveScriptableResource(groovyScript, registeredService.getServiceId(), groovyScript);
return fetchAttributeValueFromScript(script, principal, service);
})
.map(result -> result.toString())
.orElseThrow(() -> new RuntimeException("No groovy script cache manager is available to execute username provider"));
}

@Override
public String resolveUsernameInternal(final Principal principal, final Service service, final RegisteredService registeredService) {
if (StringUtils.isNotBlank(this.groovyScript)) {
initializeWatchableScriptIfNeeded();
val result = getGroovyAttributeValue(principal, service);
val result = fetchAttributeValue(principal, service, registeredService, groovyScript);
if (result != null) {
LOGGER.debug("Found username [{}] from script", result);
return result.toString();
return result;
}
}
LOGGER.warn("Groovy script [{}] is not valid. CAS will switch to use the default principal identifier [{}]", this.groovyScript, principal.getId());
return principal.getId();
}

private Object getGroovyAttributeValue(final Principal principal, final Service service) {
private static Object fetchAttributeValueFromScript(final ExecutableCompiledGroovyScript script,
final Principal principal, final Service service) {
val args = CollectionUtils.<String, Object>wrap("attributes", principal.getAttributes(),
"id", principal.getId(),
"service", service,
"logger", LOGGER);
executableScript.setBinding(args);
return executableScript.execute(args.values().toArray(), Object.class);
script.setBinding(args);
return script.execute(args.values().toArray(), Object.class);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.apereo.cas.services;

import org.apereo.cas.config.CasCoreUtilConfiguration;
import org.apereo.cas.util.CollectionUtils;
import org.apereo.cas.util.serialization.JacksonObjectMapperFactory;

Expand All @@ -8,6 +9,8 @@
import org.apache.commons.io.FileUtils;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;

import java.io.File;
import java.io.IOException;
Expand All @@ -22,6 +25,10 @@
* @since 5.2.0
*/
@Tag("Groovy")
@SpringBootTest(classes = {
RefreshAutoConfiguration.class,
CasCoreUtilConfiguration.class
})
public class GroovyRegisteredServiceUsernameProviderTests {
private static final File JSON_FILE = new File(FileUtils.getTempDirectoryPath(), "GroovyRegisteredServiceUsernameProviderTests.json");
private static final ObjectMapper MAPPER = JacksonObjectMapperFactory.builder()
Expand All @@ -30,10 +37,10 @@ public class GroovyRegisteredServiceUsernameProviderTests {
@Test
public void verifyUsernameProvider() {
val p = new GroovyRegisteredServiceUsernameProvider();
p.setGroovyScript("file:///src/test/resources/uid.groovy");
p.setGroovyScript("classpath:uid.groovy");
val id = p.resolveUsername(RegisteredServiceTestUtils.getPrincipal(), RegisteredServiceTestUtils.getService(),
RegisteredServiceTestUtils.getRegisteredService());
assertEquals("test", id);
assertEquals("fromscript", id);
}

@Test
Expand Down
11 changes: 8 additions & 3 deletions core/cas-server-core-services/src/test/resources/uid.groovy
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
logger.info("Testing username attribute for attributes $attributes and service ${service.id}")
return "test"

def run(Object[] args) {
def attributes = args[0]
def id = args[1]
def service = args[2]
def logger = args[3]
logger.info("Testing username attribute for attributes $attributes and service ${service.id}")
return "fromscript"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package org.apereo.cas.util;

import lombok.val;
import org.slf4j.Logger;

import java.util.Arrays;

/**
* Default implementation of LogMessageSummarizer summarizes throwable if log level higher than debug.
* @author Misagh Moayyed
* @since 6.6.0
*/
public class DefaultLogMessageSummarizer implements LogMessageSummarizer {

@Override
public boolean shouldSummarize(final Logger logger) {
return !logger.isDebugEnabled();
}

@Override
public String summarizeStackTrace(final String message, final Throwable throwable) {
val builder = new StringBuilder(message).append('\n');
Arrays.stream(throwable.getStackTrace()).limit(3).forEach(trace -> {
val error = String.format("\t%s:%s:%s%n", trace.getFileName(), trace.getMethodName(), trace.getLineNumber());
builder.append(error);
});
return builder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;

/**
* This is {@link LoggingUtils}.
Expand All @@ -21,6 +21,18 @@
*/
@UtilityClass
public class LoggingUtils {

private static final LogMessageSummarizer LOG_MESSAGE_SUMMARIZER;

/*
* Allow customization of whether this class will summarize stack traces when log level higher than debug.
*/
static {
LOG_MESSAGE_SUMMARIZER = ServiceLoader.load(LogMessageSummarizer.class)
.findFirst()
.orElseGet(DefaultLogMessageSummarizer::new);
}

private static final int CHAR_REPEAT_ACCOUNT = 60;

/**
Expand Down Expand Up @@ -82,9 +94,9 @@ public static void error(final Logger logger, final String msg) {
* @param throwable the throwable
*/
public static void error(final Logger logger, final String msg, final Throwable throwable) {
FunctionUtils.doIf(logger.isDebugEnabled(),
unused -> logger.error(msg, throwable),
unused -> logger.error(summarizeStackTrace(msg, throwable)))
FunctionUtils.doIf(LOG_MESSAGE_SUMMARIZER.shouldSummarize(logger),
unused -> logger.error(LOG_MESSAGE_SUMMARIZER.summarizeStackTrace(msg, throwable)),
unused -> logger.error(msg, throwable))
.accept(throwable);
}

Expand Down Expand Up @@ -116,21 +128,12 @@ public static void warn(final Logger logger, final Throwable throwable) {
* @param throwable the throwable
*/
public static void warn(final Logger logger, final String message, final Throwable throwable) {
FunctionUtils.doIf(logger.isDebugEnabled(),
unused -> logger.warn(message, throwable),
unused -> logger.warn(summarizeStackTrace(message, throwable)))
FunctionUtils.doIf(LOG_MESSAGE_SUMMARIZER.shouldSummarize(logger),
unused -> logger.warn(LOG_MESSAGE_SUMMARIZER.summarizeStackTrace(message, throwable)),
unused -> logger.warn(message, throwable))
.accept(throwable);
}

private static String summarizeStackTrace(final String message, final Throwable throwable) {
val builder = new StringBuilder(message).append('\n');
Arrays.stream(throwable.getStackTrace()).limit(3).forEach(trace -> {
val error = String.format("\t%s:%s:%s%n", trace.getFileName(), trace.getMethodName(), trace.getLineNumber());
builder.append(error);
});
return builder.toString();
}

/**
* Get first non-null exception message, and return class name if all messages null.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.apereo.cas.util.DefaultLogMessageSummarizer
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.apereo.cas.util.HttpRequestUtilsTests;
import org.apereo.cas.util.HttpUtilsTests;
import org.apereo.cas.util.InetAddressUtilsTests;
import org.apereo.cas.util.LoggingUtilsSummarizeTests;
import org.apereo.cas.util.LoggingUtilsTests;
import org.apereo.cas.util.SocketUtilsTests;
import org.apereo.cas.util.cache.DistributedCacheManagerTests;
Expand Down Expand Up @@ -71,6 +72,7 @@
SimpleHttpClientFactoryBeanTests.class,
GroovyScriptResourceCacheManagerTests.class,
LoggingUtilsTests.class,
LoggingUtilsSummarizeTests.class,
SocketUtilsTests.class,
BeanContainerTests.class,
ApplicationContextProviderTests.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.apereo.cas.util;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;

/**
* This is {@link LoggingUtilsSummarizeTests} configured with log level higher than debug to trigger summarize logic.
*
* @author Hal Deadman
* @since 6.6.0
*/
@Tag("Utility")
@Slf4j
public class LoggingUtilsSummarizeTests {
@Test
public void verifyOperation() {
assertDoesNotThrow(() -> {
LoggingUtils.error(LOGGER, "error", new RuntimeException("error"));
LoggingUtils.warn(LOGGER, "error", new RuntimeException("error"));
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<Loggers>
<Logger name="org.apereo.cas" level="warn" />
<Logger name="org.apereo.cas.util.LoggingUtilsTests" level="debug" />
<Logger name="org.apereo.cas.util.LoggingUtilsSummarizeTests" level="warn" />
<Logger name="org.apereo.cas.util.EncodingUtils" level="trace" />
<Logger name="org.apereo.cas.util.HttpUtilsTests" level="warn" />
<Logger name="org.apereo.cas.util.spring.boot" level="debug" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ These such as `displayName` above, get bundled into a `custom` scope which
can be used and requested by services and clients.

<div class="alert alert-info"><strong>Usage</strong><p>All user-defined custom scopes as well any custom claims
that would be mapped to those scopes must always be advertised via OpenID Connect dioscvery document and specified
that would be mapped to those scopes must always be advertised via OpenID Connect discovery document and specified
in CAS settings for scopes and claims to be recognized as valid during claim processing.</p>
</div>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Typical questions in this category that are best answered elsewhere are:
It is quite possible that the problem you are trying to resolve has already been solved by the next patch release.
A patch release is a conservative incremental improvement that includes bug fixes and small enhancements and
is absolutely backward compatible with previous PATCH releases of the same MINOR release. For example, if you are currently
on CAS version `6.5.1` and have run into a possible issue, you should consider upgrading to `6.5.2`, and 6.5.3` and so on
on CAS version `6.5.1` and have run into a possible issue, you should consider upgrading to `6.5.2`, and `6.5.3` and so on
to investigate further, assuming releases are of course availble and published.

The project release schedule is [available here](https://github.com/apereo/cas/milestones), and you can always
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Platform metadata for releases, POM generation, etc.
#################################################
group=org.apereo.cas
version=6.6.3
version=6.6.4-SNAPSHOT
projectUrl=https://www.apereo.org/cas
projectInceptionYear=2004
projectScmUrl=scm:[email protected]:apereo/cas.git
Expand Down Expand Up @@ -339,7 +339,7 @@ openidVersion=1.0.0
###############################
# Tomcat versions
###############################
tomcatVersion=9.0.68
tomcatVersion=9.0.70
###############################
# SCIM versions
###############################
Expand Down
Loading

0 comments on commit aafef82

Please sign in to comment.