diff --git a/impl/src/main/java/org/glassfish/soteria/cdi/CdiUtils.java b/impl/src/main/java/org/glassfish/soteria/cdi/CdiUtils.java index 5ead0c3..c7e4db6 100644 --- a/impl/src/main/java/org/glassfish/soteria/cdi/CdiUtils.java +++ b/impl/src/main/java/org/glassfish/soteria/cdi/CdiUtils.java @@ -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 @@ -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 Optional getAnnotation(BeanManager beanManager, Annotated annotated, Class annotationType) { annotated.getAnnotation(annotationType); @@ -74,13 +74,13 @@ public static Optional 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 Optional getAnnotation(BeanManager beanManager, Class annotatedClass, Class annotationType) { if (annotatedClass.isAnnotationPresent(annotationType)) { @@ -107,23 +107,28 @@ public static Optional 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 getBeanReference(Class 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 @@ -145,17 +150,17 @@ public static Object getBeanReferenceByType(BeanManager beanManager, Type type, return beanReference; } - + @SuppressWarnings("unchecked") private static T getContextualReference(Class type, BeanManager beanManager, Set> beans) { - + Object beanReference = null; - + Bean bean = beanManager.resolve(beans); if (bean != null) { beanReference = beanManager.getReference(bean, type, beanManager.createCreationalContext(bean)); } - + return (T) beanReference; } @@ -172,19 +177,19 @@ public static List getBeanReferencesByType(Class 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; } @@ -192,27 +197,40 @@ private static Set> getBeanDefinitions(Class type, boolean option Set> 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 jndiLookup(String name) { + /** + * Tries provided names, first found non-null object is returned. + * + * @param 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 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); } @@ -227,7 +245,7 @@ private static void close(InitialContext context) { throw new IllegalStateException(e); } } - + public static boolean is(Throwable exception, Class type) { Throwable unwrappedException = exception; diff --git a/impl/src/main/java/org/glassfish/soteria/identitystores/DatabaseIdentityStore.java b/impl/src/main/java/org/glassfish/soteria/identitystores/DatabaseIdentityStore.java index 257e38c..92712ec 100644 --- a/impl/src/main/java/org/glassfish/soteria/identitystores/DatabaseIdentityStore.java +++ b/impl/src/main/java/org/glassfish/soteria/identitystores/DatabaseIdentityStore.java @@ -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 @@ -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 { @@ -54,7 +57,7 @@ public class DatabaseIdentityStore implements IdentityStore { private final Set 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() { @@ -62,10 +65,10 @@ protected DatabaseIdentityStore() { 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( @@ -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)) )))); } @@ -93,15 +96,15 @@ public CredentialValidationResult validate(UsernamePasswordCredential usernamePa DataSource dataSource = getDataSource(); List passwords = executeQuery( - dataSource, + dataSource, dataBaseIdentityStoreDefinition.callerQuery(), usernamePasswordCredential.getCaller() ); - + if (passwords.isEmpty()) { return INVALID_RESULT; } - + if (hashAlgorithm.verify(usernamePasswordCredential.getPassword().getValue(), passwords.get(0))) { Set groups = emptySet(); if (validationTypes.contains(ValidationType.PROVIDE_GROUPS)) { @@ -113,7 +116,7 @@ public CredentialValidationResult validate(UsernamePasswordCredential usernamePa return INVALID_RESULT; } - + @Override public Set getCallerGroups(CredentialValidationResult validationResult) { @@ -159,31 +162,25 @@ public int priority() { public Set validationTypes() { return validationTypes; } - + @SuppressWarnings("unchecked") private Stream toStream(Object raw) { if (raw instanceof String[]) { - return stream((String[])raw); + return stream((String[]) raw); } if (raw instanceof Stream) { - return ((Stream) raw).map(s -> s.toString()); + return ((Stream) 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; } } diff --git a/impl/src/main/java/org/glassfish/soteria/servlet/SamRegistrationInstaller.java b/impl/src/main/java/org/glassfish/soteria/servlet/SamRegistrationInstaller.java index d3a7db1..72f07cc 100644 --- a/impl/src/main/java/org/glassfish/soteria/servlet/SamRegistrationInstaller.java +++ b/impl/src/main/java/org/glassfish/soteria/servlet/SamRegistrationInstaller.java @@ -74,7 +74,6 @@ public void onStartup(Set> 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; }