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

Custom jacc provider fails to find BeanManager via JNDI (java:comp/BeanManager) #24677

Open
escay opened this issue Nov 7, 2023 · 7 comments

Comments

@escay
Copy link
Contributor

escay commented Nov 7, 2023

Environment Details

  • GlassFish Version (and build number): Eclipse GlassFish 7.0.10 (commit: b57abb8)
  • 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?
@dmatej
Copy link
Contributor

dmatej commented Nov 7, 2023

Can you quickly try it yet with 7.0.9? We did some changes related to authentication, it might be a bug.

@arjantijms
Copy link
Contributor

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:

@escay
Copy link
Contributor Author

escay commented Nov 7, 2023

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]]

@escay
Copy link
Contributor Author

escay commented Nov 7, 2023

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.

@escay
Copy link
Contributor Author

escay commented Nov 13, 2023

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.

@OndroMih
Copy link
Contributor

OndroMih commented Feb 4, 2024

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?

@escay
Copy link
Contributor Author

escay commented Feb 4, 2024

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants