glassfish
glassfish copied to clipboard
Custom jacc provider fails to find BeanManager via JNDI (java:comp/BeanManager)
Environment Details
- GlassFish Version (and build number): Eclipse GlassFish 7.0.10 (commit: b57abb800a07df684bfc0ebc66d00446e2ac56f6)
- JDK version: jdk-17.0.8.1_1-openjdk-adoptium
- OS: Windows 10
Problem Description
I have a custom jacc provider. The jar is placed it in the /lib folder, and registered in domain.xml. The implementation is based on Arjan Thijms example from: https://arjan-tijms.omnifaces.org/2015/03/java-ee-authorization-jacc-revisited.html
This provider works on Payara 5.2022 and 6.2023, but now I try to use the same provider on GlassFish 7. The jacc provider is registered during startup. No errors.
[2023-11-07T10:53:27.356527+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01115] [jakarta.enterprise.system.core.security] [tid: _ThreadID=1 _ThreadName=main] [levelValue: 800] [[
Realm [MySecurityRealm] of classtype [my.userlib.security.MyGlassfishSecurityRealm] successfully created.]]
...
[2023-11-07T10:53:35.128538+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01010] [jakarta.enterprise.system.core.security] [tid: _ThreadID=141 _ThreadName=Thread-9] [levelValue: 800] [[
Entering Security Startup Service.]]
[2023-11-07T10:53:35.130535+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01143] [jakarta.enterprise.system.core.security] [tid: _ThreadID=141 _ThreadName=Thread-9] [levelValue: 800] [[
Loading policy provider my.jaccprovider.policy.CustomPolicy.]]
[2023-11-07T10:53:35.137544+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=141 _ThreadName=Thread-9] [levelValue: 800] [[
### CustomPolicy - constructor - start]]
[2023-11-07T10:53:35.137544+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=141 _ThreadName=Thread-9] [levelValue: 800] [[
### CustomPolicy - constructor - end]]
[2023-11-07T10:53:35.146490+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01011] [jakarta.enterprise.system.core.security] [tid: _ThreadID=141 _ThreadName=Thread-9] [levelValue: 800] [[
Security Service(s) started successfully.]]
What is special about the implementation is that it wants to use BeanManager via JNDI. The jacc provider jar contains interface class:
public interface AuthorizationMechanism {
default Boolean preAuthenticatePreAuthorize(Permission requestedPermission, SecurityConstraints securityConstraints) {
return null;
}
default Boolean preAuthenticatePreAuthorizeByRole(Permission requestedPermission, SecurityConstraints securityConstraints) {
return null;
}
default Boolean postAuthenticatePreAuthorize(Permission requestedPermission, Caller caller, SecurityConstraints securityConstraints) {
return null;
}
default Boolean postAuthenticatePreAuthorizeByRole(Permission requestedPermission, Caller caller, SecurityConstraints securityConstraints) {
return null;
}
}
The implementation bean:
@ApplicationScoped
public class CustomAuthorizationMechanism implements AuthorizationMechanism {
is part of the ear file (as an ejb jar with beans.xml) of the application that needs to use the jacc provider and will make some database calls and reuses the datasource that is already configured for the ear file. The goal is to find the application scoped CustomAuthorizationMechanism instance by trying to load the AuthorizationMechanism via BeanManager.
When I deploy an ear file at one point deployment of the ear fails with NameNotFoundException: No object bound for java:comp/BeanManager:
[2023-11-07T11:28:28.074091+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.cdi] [tid: _ThreadID=143 _ThreadName=AutoDeployer] [levelValue: 800] [[
### CdiUtils - is, exception: javax.naming.NamingException: Lookup failed for java:comp/BeanManager in SerialContext[myEnv={java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming}] [Root exception is javax.naming.NameNotFoundException: No object bound for java:comp/BeanManager], type: class javax.naming.NameNotFoundException]]
[2023-11-07T11:28:28.475304+01:00] [GF 7.0.10] [SEVERE] [] [jakarta.enterprise.system.core] [tid: _ThreadID=143 _ThreadName=AutoDeployer] [levelValue: 1000] [[
Exception while loading the app : jakarta.ejb.CreateException: Initialization failed for Singleton scheduler.ServiceStarter
jakarta.ejb.CreateException: Initialization failed for Singleton scheduler.ServiceStarter
at com.sun.ejb.containers.AbstractSingletonContainer.createSingletonEJB(AbstractSingletonContainer.java:450)
at com.sun.ejb.containers.AbstractSingletonContainer$SingletonContextFactory.create(AbstractSingletonContainer.java:620)
at com.sun.ejb.containers.AbstractSingletonContainer.instantiateSingletonInstance(AbstractSingletonContainer.java:362)
at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(SingletonLifeCycleManager.java:195)
at org.glassfish.ejb.startup.SingletonLifeCycleManager.initializeSingleton(SingletonLifeCycleManager.java:156)
at org.glassfish.ejb.startup.SingletonLifeCycleManager.doStartup(SingletonLifeCycleManager.java:134)
at org.glassfish.ejb.startup.EjbApplication.start(EjbApplication.java:137)
...
at org.glassfish.deployment.autodeploy.AutoDeployService$1.run(AutoDeployService.java:209)
at java.base/java.util.TimerThread.mainLoop(Timer.java:566)
at java.base/java.util.TimerThread.run(Timer.java:516)
Caused by: jakarta.ejb.EJBAccessException
at com.sun.ejb.containers.BaseContainer.mapLocal3xException(BaseContainer.java:2057)
at com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1858)
... 32 more
Caused by: jakarta.ejb.AccessLocalException: Client not authorized for this invocation
at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1699)
at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:168)
... 63 more
]]
the relevant code shortened is:
public class my.jaccprovider.policy.CustomPolicy extends java.security.Policy {
@Override
public boolean implies(ProtectionDomain domain, Permission requestedPermission) {
....
context = new InitialContext();
BeanManager bm = (BeanManager) context.lookup("java:comp/BeanManager");
}
}
Impact of Issue
The questions I have are:
- Would BeanManager be allowed to be found via JNDI in GlassFish 7 from the glassfish-7.0.10\glassfish\domains\my-domain\lib folder? or from a custom jacc provider?
- Why does this work in Payara 5 (jakartaee8) and 6 (jakartaee10) and not in Glassfish? is it a bug in Glassfish?
- Is there a jacc provider example for Glassfish7 that works together with code in an ear file?
Can you quickly try it yet with 7.0.9? We did some changes related to authentication, it might be a bug.
Thanks for the report! It's a long time ago I created that example, and I would have to take a look at it again.
One important question though; on Payara did you also install the JACC Provider in the \glassfish\domains\my-domain\lib folder, or did you install it using the JACC-per-app feature I added to Payara?
Note btw that for Jakarta EE 11 / GlassFish 8, the JACC Provider will have to change as the JDK Policy is deprecated for removal. The plan is also to make that example a default feature in Jakarta Security.
See:
- https://github.com/jakartaee/authorization/issues/99
- https://github.com/jakartaee/security/issues/296
I tested on Glassfish 7.0.9 with the same "NameNotFoundException: No object bound for java:comp/BeanManager" result. (manually copied domain from 7.0.10 and erased osgi-cache/felix folder)
I have the same domain configuration in use for both Payara and Glassfish the same domain.xml configuration like:
<security-service jacc="amm">
<auth-realm classname="com.my.userlib.security.MyGlassfishSecurityRealm" name="MySecurityRealm">
<property name="jaas-context" value="myRealm"></property>
</auth-realm>
<jacc-provider policy-provider="com.my.jaccprovider.policy.CustomPolicy" name="amm" policy-configuration-factory-provider="my.jaccprovider.configuration.CustomPolicyConfigurationFactory"></jacc-provider>
and the jars are in the domain specific lib on the same location:
C:\payara-5.2022.5\glassfish\domains\my-domain\lib\jacc-provider-2.44.5-SNAPSHOT.jar - javaee8
C:\payara-6.2023.10\glassfish\domains\my-domain\lib\jacc-provider-3.0.2-SNAPSHOT.jar - jakartaee10
C:\glassfish-7.0.9\glassfish\domains\my-domain\lib\jacc-provider-3.0.2-SNAPSHOT.jar - jakartaee10
C:\glassfish-7.0.10\glassfish\domains\my-domain\lib\jacc-provider-3.0.2-SNAPSHOT.jar - jakartaee10
I tested again using Payara with my logging enabled. I see the following behavior difference:
- Glassfish starts to log debugging of my.jaccprovider on start-domain, before the ear is deployed.
- Payara does not log debugging of my.jaccprovider on start-domain, but starts to log once ear is being deployed. I have no logging.properties changes compared to the 'nucleus' domain1 templates of both products, so it seems the security provider startup / loading is different.
Payara: CustomPolicy constructor called after ear deployment
[2023-11-07T20:28:33.302+0100] [Payara 6.2023.10] [INFO] [NCLS-DEPLOYMENT-02027] [javax.enterprise.system.tools.deployment.autodeploy] [tid: _ThreadID=111 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1699385313302] [levelValue: 800] [[
Selecting file C:\payara-6.2023.10\glassfish\domains\my-amm\autodeploy\my-test.payara.postgresql.ear for autodeployment]]
...
[2023-11-07T20:28:52.903+0100] [Payara 6.2023.10] [INFO] [NCLS-SECURITY-01002] [javax.enterprise.system.core.security] [tid: _ThreadID=111 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1699385332903] [levelValue: 800] [[
Java security manager is disabled.]]
[2023-11-07T20:28:52.903+0100] [Payara 6.2023.10] [INFO] [NCLS-SECURITY-01010] [javax.enterprise.system.core.security] [tid: _ThreadID=111 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1699385332903] [levelValue: 800] [[
Entering Security Startup Service.]]
[2023-11-07T20:28:52.904+0100] [Payara 6.2023.10] [INFO] [NCLS-SECURITY-01143] [javax.enterprise.system.core.security] [tid: _ThreadID=111 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1699385332904] [levelValue: 800] [[
Loading policy provider my.jaccprovider.policy.CustomPolicy.]]
[2023-11-07T20:28:52.913+0100] [Payara 6.2023.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=111 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1699385332913] [levelValue: 800] [[
### CustomPolicy - constructor - start]]
[2023-11-07T20:28:52.913+0100] [Payara 6.2023.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=111 _ThreadName=payara-executor-service-scheduled-task] [timeMillis: 1699385332913] [levelValue: 800] [[
### CustomPolicy - constructor - end]]
Glassfish: CustomPolicy constructor called before ear deployment
[2023-11-07T15:16:54.299543+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01002] [jakarta.enterprise.system.core.security] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Java security manager is disabled.]]
[2023-11-07T15:16:54.299543+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01010] [jakarta.enterprise.system.core.security] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Entering Security Startup Service.]]
[2023-11-07T15:16:54.301538+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01143] [jakarta.enterprise.system.core.security] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Loading policy provider my.jaccprovider.policy.CustomPolicy.]]
[2023-11-07T15:16:54.307551+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
### CustomPolicy - constructor - start]]
[2023-11-07T15:16:54.307551+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
### CustomPolicy - constructor - end]]
[2023-11-07T15:16:54.313534+01:00] [GF 7.0.10] [INFO] [NCLS-SECURITY-01011] [jakarta.enterprise.system.core.security] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Security Service(s) started successfully.]]
...
[2023-11-07T15:16:55.865800+01:00] [GF 7.0.10] [INFO] [] [org.glassfish.soteria.servlet.SamRegistrationInstaller] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Initializing Soteria 3.0.3 for context '']]
[2023-11-07T15:16:55.920622+01:00] [GF 7.0.10] [INFO] [faces.config.listener.version] [jakarta.enterprise.resource.webcontainer.faces.config] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Initializing Mojarra 4.0.4 for context '']]
...
[2023-11-07T15:16:56.460449+01:00] [GF 7.0.10] [INFO] [AS-WEB-GLUE-00172] [jakarta.enterprise.web] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Loading application [__admingui] at [/]]]
[2023-11-07T15:16:56.461418+01:00] [GF 7.0.10] [INFO] [NCLS-CORE-00022] [jakarta.enterprise.system.core] [tid: _ThreadID=140 _ThreadName=Thread-9] [levelValue: 800] [[
Loading application __admingui done in 3,351 ms]]
[2023-11-07T15:19:05.140118+01:00] [GF 7.0.10] [INFO] [NCLS-DEPLOYMENT-02027] [jakarta.enterprise.system.tools.deployment.autodeploy] [tid: _ThreadID=142 _ThreadName=AutoDeployer] [levelValue: 800] [[
Selecting file C:\glassfish-7.0.10\glassfish\domains\my-amm\autodeploy\my-test.payara.postgresql.ear for autodeployment]]
[2023-11-07T15:19:05.209142+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=142 _ThreadName=AutoDeployer] [levelValue: 800] [[
### CustomPolicy - implies - start]]
[2023-11-07T15:19:05.209142+01:00] [GF 7.0.10] [INFO] [] [my.jaccprovider.policy.CustomPolicy] [tid: _ThreadID=142 _ThreadName=AutoDeployer] [levelValue: 800] [[
### CustomPolicy - doImplies - start]]
Trace from my code contains eventually: "NameNotFoundException: No object bound for java:comp/BeanManager" on both 7.0.9 and 7.0.10
### CdiUtils - is, exception: javax.naming.NamingException: Lookup failed for java:comp/BeanManager in SerialContext[myEnv={java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl, java.naming.factory.url.pkgs=com.sun.enterprise.naming}] [Root exception is javax.naming.NameNotFoundException: No object bound for java:comp/BeanManager], type: class javax.naming.NameNotFoundException]]
I was not aware a JACC per application option exists in Payara, I assume you mean: https://www.javadoc.io/doc/fish.payara.api/payara-api/6.2023.9/fish/payara/jacc/JaccConfigurationFactory.html No payara specific code is used.
I think (not 100% sure) the dependencies used to build the custom provider are:
<dependency>
<groupId>jakarta.authorization</groupId>
<artifactId>jakarta.authorization-api</artifactId>
<version>2.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-api</artifactId>
<version>3.0.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.platform</groupId>
<artifactId>jakarta.jakartaee-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.glassfish.security</groupId>
<artifactId>security</artifactId>
<version>3.1.1</version>
<scope>provided</scope>
</dependency>
I am sure there are no Payara specific dependencies.
I can also add that instead of manually looking for:
BeanManager foundJndi = jndiLookup("java:comp/BeanManager");
I tried using CDI directly via:
beanReference = CDI.current().select(AuthorizationMechanism.class).get();
also does not work on Glassfish, it leads to:
java.lang.IllegalStateException: Unable to access CDI.
I can try to build a reproducer if needed.
Hi @escay, it looks like the EJB container is initialized before the CDI container, and then CDI is not available in your custom JACC provider. Can you try adding beans.xml file into each of your EJB and WAR modules that are in the EAR?
All ejb modules and war modules (and there are many) have beans.xml, most of them use bean-discovery-mode="none" to make deployment quicker / avoid scanning (since they only contain EJB annotations), only web uses some "annotated" for CDI annotations.
The custom JACC provider is already loaded and active before any ear file is deployed when I configure it in Glassfish. Regardless of ear deployment or not it seems to me that there is no BeanManager and no CDI container available for the jacc provider code, in my-domain\lib. Comes back to my question: Would BeanManager be allowed to be found via JNDI in GlassFish 7 from the glassfish-7.0.10\glassfish\domains\my-domain\lib folder? or from a custom jacc provider? I know Payara allows this, I do not know what you would expect / support in Glassfish.
Another note:
Within the ear file the
BeanManager bm = (BeanManager) context.lookup("java:comp/BeanManager");
is working fine to find any bean and load them in Glassfish 7.
The workaround for me is:
- I did not want to have to deal with database connections and settings in jacc provider code. (Therefor I wanted to make the bean calls in the original solution which asks for a bean in the ear using jndi)
- I turned the dependency around: jacc provider does not allow anything at start, and does not make any calls to code in the ear file.
- Ear file informs jacc provider classes (which is on the classpath) with role mapping information from the database table during deployment and when role configurations change in the database.
- Jacc provider no longer needs to lookup a bean in an ear file to get to the database.
- No need for bean calls to ear file to reuse database logic. A bit of logic in the ear file about the jacc provider.
This seems to work reliably in the last few days.
There is no need to improve things in Glassfish at this moment, I can wait to see what the new JakartaEE11 / GF8 solution brings.
This issue has been marked as inactive and old and will be closed in 7 days if there is no further activity. If you want the issue to remain open please add a comment
Ok to close this issue. Workaround is working fine over the last year.