Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix for #340 - JNDI lookup returns object or throws exception #341

Merged
merged 1 commit into from
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 54 additions & 36 deletions impl/src/main/java/org/glassfish/soteria/cdi/CdiUtils.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -37,11 +38,10 @@
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.BeforeBeanDiscovery;
import javax.naming.InitialContext;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;

public class CdiUtils {

public static <A extends Annotation> Optional<A> getAnnotation(BeanManager beanManager, Annotated annotated, Class<A> annotationType) {

annotated.getAnnotation(annotationType);
Expand Down Expand Up @@ -74,13 +74,13 @@ public static <A extends Annotation> Optional<A> getAnnotation(BeanManager beanM

return empty();
}

public static void addAnnotatedTypes(BeforeBeanDiscovery beforeBean, BeanManager beanManager, Class<?>... types) {
for (Class<?> type : types) {
beforeBean.addAnnotatedType(beanManager.createAnnotatedType(type), "Soteria " + type.getName());
}
}

public static <A extends Annotation> Optional<A> getAnnotation(BeanManager beanManager, Class<?> annotatedClass, Class<A> annotationType) {

if (annotatedClass.isAnnotationPresent(annotationType)) {
Expand All @@ -107,23 +107,28 @@ public static <A extends Annotation> Optional<A> getAnnotation(BeanManager beanM

return empty();
}

public static BeanManager getBeanManager() {
BeanManager beanManager = jndiLookup("java:comp/BeanManager");

if (beanManager == null) {
// Servlet containers
beanManager = jndiLookup("java:comp/env/BeanManager");
/**
* @return non-null {@link BeanManager}
* @throws IllegalStateException if it wasn't possible to find the CDI BeanManager.
*/
public static BeanManager getBeanManager() throws IllegalStateException {
try {
return jndiLookup("java:comp/BeanManager","java:comp/env/BeanManager");
} catch (NamingException e) {
throw new IllegalStateException("The CDI Bean Manager is not available.", e);
}

return beanManager;
}

//

/**
* @param type the required bean type the reference must have
* @param qualifiers the required qualifiers the reference must have
* @return a bean reference adhering to the required type and qualifiers
*/
public static <T> T getBeanReference(Class<T> type, Annotation... qualifiers) {
return type.cast(getBeanReferenceByType(getBeanManager(), type, qualifiers));
}

/**
* @param beanManager the bean manager
* @param type the required bean type the reference must have
Expand All @@ -145,17 +150,17 @@ public static Object getBeanReferenceByType(BeanManager beanManager, Type type,

return beanReference;
}

@SuppressWarnings("unchecked")
private static <T> T getContextualReference(Class<T> type, BeanManager beanManager, Set<Bean<?>> beans) {

Object beanReference = null;

Bean<?> bean = beanManager.resolve(beans);
if (bean != null) {
beanReference = beanManager.getReference(bean, type, beanManager.createCreationalContext(bean));
}

return (T) beanReference;
}

Expand All @@ -172,47 +177,60 @@ public static <T> List<T> getBeanReferencesByType(Class<T> type, boolean optiona

return result;
}

public static ELProcessor getELProcessor(ELProcessor elProcessor) {
if (elProcessor != null) {
return elProcessor;
}

return getELProcessor();
}

public static ELProcessor getELProcessor() {
ELProcessor elProcessor = new ELProcessor();
elProcessor.getELManager().addELResolver(getBeanManager().getELResolver());

return elProcessor;
}

private static <T> Set<Bean<?>> getBeanDefinitions(Class<T> type, boolean optional, BeanManager beanManager) {
Set<Bean<?>> beans = beanManager.getBeans(type, new AnyAnnotationLiteral());
if (!isEmpty(beans)) {
return beans;
}
}

if (optional) {
return emptySet();
}
}

throw new IllegalStateException("Could not find beans for Type=" + type);
}

@SuppressWarnings("unchecked")
public static <T> T jndiLookup(String name) {
/**
* Tries provided names, first found non-null object is returned.
*
* @param <T> expected type
* @param names list of JNDI names to try.
* @return non-null object
* @throws NamingException if all lookups ended with an exception or null values.
*/
public static <T> T jndiLookup(String... names) throws NamingException {
InitialContext context = null;
try {
context = new InitialContext();
return (T) context.lookup(name);
} catch (NamingException e) {
if (is(e, NameNotFoundException.class)) {
return null;
} else {
throw new IllegalStateException(e);
NamingException exceptionCollector = new NamingException(String.join(", ", names));
for (String name : names) {
try {
@SuppressWarnings("unchecked")
T found = (T) context.lookup(name);
if (found != null) {
return found;
}
} catch (NamingException e) {
exceptionCollector.addSuppressed(e);
}
}
throw exceptionCollector;
} finally {
close(context);
}
Expand All @@ -227,7 +245,7 @@ private static void close(InitialContext context) {
throw new IllegalStateException(e);
}
}

public static <T extends Throwable> boolean is(Throwable exception, Class<T> type) {
Throwable unwrappedException = exception;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2015, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
Expand Down Expand Up @@ -46,6 +47,8 @@
import jakarta.security.enterprise.identitystore.IdentityStore;
import jakarta.security.enterprise.identitystore.IdentityStorePermission;
import jakarta.security.enterprise.identitystore.PasswordHash;

import javax.naming.NamingException;
import javax.sql.DataSource;

public class DatabaseIdentityStore implements IdentityStore {
Expand All @@ -54,18 +57,18 @@ public class DatabaseIdentityStore implements IdentityStore {

private final Set<ValidationType> validationTypes;
private final PasswordHash hashAlgorithm; // Note: effectively application scoped, no support for @PreDestroy now

// CDI requires a no-arg constructor to be portable
// It's only used to create the proxy
protected DatabaseIdentityStore() {
this.dataBaseIdentityStoreDefinition = null;
this.validationTypes = null;
this.hashAlgorithm = null;
}

public DatabaseIdentityStore(DatabaseIdentityStoreDefinition dataBaseIdentityStoreDefinition) {
this.dataBaseIdentityStoreDefinition = dataBaseIdentityStoreDefinition;

validationTypes = unmodifiableSet(new HashSet<>(asList(dataBaseIdentityStoreDefinition.useFor())));
hashAlgorithm = getBeanReference(dataBaseIdentityStoreDefinition.hashAlgorithm());
hashAlgorithm.initialize(
Expand All @@ -74,7 +77,7 @@ public DatabaseIdentityStore(DatabaseIdentityStoreDefinition dataBaseIdentitySto
dataBaseIdentityStoreDefinition.hashAlgorithmParameters())
.flatMap(s -> toStream(evalImmediate(s, (Object)s)))
.collect(toMap(
s -> s.substring(0, s.indexOf('=')) ,
s -> s.substring(0, s.indexOf('=')) ,
s -> evalImmediate(s.substring(s.indexOf('=') + 1))
))));
}
Expand All @@ -93,15 +96,15 @@ public CredentialValidationResult validate(UsernamePasswordCredential usernamePa
DataSource dataSource = getDataSource();

List<String> passwords = executeQuery(
dataSource,
dataSource,
dataBaseIdentityStoreDefinition.callerQuery(),
usernamePasswordCredential.getCaller()
);

if (passwords.isEmpty()) {
return INVALID_RESULT;
}

if (hashAlgorithm.verify(usernamePasswordCredential.getPassword().getValue(), passwords.get(0))) {
Set<String> groups = emptySet();
if (validationTypes.contains(ValidationType.PROVIDE_GROUPS)) {
Expand All @@ -113,7 +116,7 @@ public CredentialValidationResult validate(UsernamePasswordCredential usernamePa

return INVALID_RESULT;
}

@Override
public Set<String> getCallerGroups(CredentialValidationResult validationResult) {

Expand Down Expand Up @@ -159,31 +162,25 @@ public int priority() {
public Set<ValidationType> validationTypes() {
return validationTypes;
}

@SuppressWarnings("unchecked")
private Stream<String> toStream(Object raw) {
if (raw instanceof String[]) {
return stream((String[])raw);
return stream((String[]) raw);
}
if (raw instanceof Stream<?>) {
return ((Stream<String>) raw).map(s -> s.toString());
return ((Stream<String>) raw).map(String::toString);
}

return asList(raw.toString()).stream();
}

private DataSource getDataSource() {
DataSource dataSource = null;
try {
dataSource = jndiLookup(dataBaseIdentityStoreDefinition.dataSourceLookup());
if (dataSource == null) {
throw new IdentityStoreConfigurationException("Jndi lookup failed for DataSource " + dataBaseIdentityStoreDefinition.dataSourceLookup());
}
} catch (IdentityStoreConfigurationException e) {
throw e;
} catch (Exception e) {
throw new IdentityStoreRuntimeException(e);
return jndiLookup(dataBaseIdentityStoreDefinition.dataSourceLookup());
} catch (NamingException e) {
throw new IdentityStoreRuntimeException(
"JNDI lookup failed for DataSource " + dataBaseIdentityStoreDefinition.dataSourceLookup(), e);
}
return dataSource;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletExcepti
// and calling CDI.current() will throw an exception. It's no use to continue then.
// TODO: Do we need to find out *why* the default module does not have CDI initialized?
logger.log(FINEST, "CDI not available for app context id: " + Jaspic.getAppContextID(ctx), e);

return;
}

Expand Down