I am using httpinvoker in JBoss 4.0.4 (little old) for EJB invocations.
Since there are so many clients that make calls to my server, I want to identify the clients for each call in server.
Is there a way to do this with JBoss httpinvoker?
I could imagine adding a header to identify my client in each HTTP request, but cannot find a way to add a header in httpinvoker.
Auditing builds on a name, and thus on an authentication scheme somehow.
Therefore I suggest using the standard client authentication infrastructure to solve your problem. This works for RMI as well (it's not bound to HTTP), and the user ID is even passed down into your EJBs.
Server
Put the EJB in a security-domain (ejb.jar: META-INF/jboss.xml)
You could use the application-policy other which just the UsersRolesLoginModule (conf/login-config.xml); this is the default policy, it's already configured.
Add users.properties and roles.properties to your ejb.jar file (top level package): These are used by the UsersRolesLoginModule
For each user, add his name and a (dummy) password to users.properties
Client
Create a callback class which implements a javax.security.auth.callback.CallbackHandler: This callback is used, when the authentication needs the user and the password.
Create a javax.security.auth.login.LoginContext; pass the callback handler as the 2nd argument; call login() on the instance of the LoginContext
Connect normally to the EJB server using an InitialContext
Add -Djava.security.auth.login.config=.../jboss-4/client/auth.conf when you start the client
This way a user ID is passed from the client to the EJB (as part of the standard authentication process). Now, in the EJB methods, you can get the user ID by calling getCallerPrincipal() on the SessionContext instance. I have tested this against JBoss 4.2.3
Additional information: JBoss client authentication
Addendum 1:
Using RMI or HTTP, the password is not transported in a secure way. In this case just use a dummy password, this is OK for auditing.
On the other hand, if you use RMI over SSL or HttpInvoker over HTTPS, you could change to a real and secure authentication quickly.
Addendum 2:
I am not sure, if it works without defining roles. Possibly you have to
Add a line in roles.properties for each user: Add a connect role, for example
Add role definitions in ejb-jar.xml as well: security-role-ref for each EJB, and security-role and method-permission in the assembly-descriptor
Update
As there is already a login module, there might be another possibility:
If you have the source code of the login module, you could possibly use another TextCallback to get additional information from the client (in your case a user ID). The information could be used to create a custom Principal. Within the EJB, the result of getCallerPrincipal() could be cast to the custom principal.
Related
I implemented an authentication mechanism similar to CustomHeaderHttpAuthenticationMechanism in https://github.com/wildfly-security-incubator/elytron-examples/tree/master/simple-http-mechanism, using PasswordGuessEvidence and also the other Callbacks mentioned in the example. Reason for the custom mechanism is that beside a simple credential check we need also to validate more constraints to check if a user is validated.
Stepping through this authentication mechanism looks quite good, the authenticationComplete method is called and also the authorizeCallback is successful. However, when accessing an EJB via a resteasy endpoint (EJB is annotated with #SecurityDomain and #RolesAllowed...) the SimpleSecurityManager.authorize method fails because the securityContext.getUtil method neither provides a principal nor something else. If accessing a method annotated by #PermitAll it is successful.
I guess the principal should be created by the ServerAuthenticationContext when working through the different callbacks, right?
How do I manage that the SimpleSecurityManager can recognize the principal, would I need to create it in my authentication mechanism, and how?
In this case it sounds like your EJB deployment has not been mapped to the WildFly Elytron security domain so is still making use of PicketBox security in the EJB tier which is why you are not seeing the identity already established.
Within the EJB subsystem you can also add an application-security-domain mapping to map from the security domain specified in the deployment to the WildFly Elytron security domain.
FYI at some point in the future when we are ready to remove PicketBox from the server these additional mappings will no longer be required, they are just unfortunately needed at the moment whilst we have both solutions in parallel.
UPDATE: Some context on the problem below. My goal is to handle requests for users where they supply a Kafka topic with each request. I use Message Hub deployed on Bluemix as Kafka provider. The requests will pass the broker URL, topic name, username, password and API key. Message Hub on Bluemix requires JAAS authentication and provides a login module with different LoginModule implementations. Some are based on CallbackHandlers, others on CredentialProviders.
I picked the one implemented in com.ibm.messagehub.login.MultiUserLoginModule. With that module I should only have to supply a custom credential provider like:
KafkaClient {
com.ibm.messagehub.login.MultiUserLoginModule required
credentialProvider="myApp.CustomCredentialProvider";
};
The challenges are in the class loader and how the CustomCredentialProvider can get the username/password from the request passed to the MultiUserLoginModule at runtime. What configuration do I have to use to get that working?
DETAILS: I have a web application running in WebSphere Liberty 8.5.5 and want to authenticate with a third-party service. That third-party service implements a JAAS LoginModule with a CredentialProvider. My web app extends the CredentialProvider with a CustomCredentialProvider to pass credentials.
What I don't understand is how the class loading is supposed to work. My server.xml defines:
The web application
<webApplication id="streaming-service" location="streaming-service.war" name="streaming-service"/>
The third-party login module
<jaasLoginModule className="com.ibm.messagehub.login.MultiUserLoginModule" controlFlag="REQUIRED" id="KafkaClient" libraryRef="messageHubLoginLib">
<options credentialProvider="myApp.CustomCredentialProvider" serviceName="kafka"/>
</jaasLoginModule>
The library that implements the third-party login module
<library id="messageHubLoginLib">
<fileset dir="${server.output.dir}" includes="messagehub.login-1.0.0.jar"/>
</library>
The login context
<jaasLoginContextEntry id="KafkaClient" loginModuleRef="KafkaClient" name="KafkaClient"/>
The result of the above configuration is a ClassNotFoundException for my CustomCredentialProvider:
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: myApp.CustomCredentialProvider
at com.ibm.messagehub.login.MultiUserLoginModule$MultiUserCallbackHandler.<clinit>(MultiUserLoginModule.java:80)
How do I have to change my configuration for the third-party JAAS login module to find myApp.CustomCredentialProvider implemented in my streaming-service web app?
Note: I already tried to generate a streaming-service.jar and add it directly to the messageHubLoginLib. That resolves the ClassNotFoundException but the CustomCredentialProvider class is loaded completely outside the context of my running web application and still gives me no access my credentials.
You were nearly there with the separate streaming-service.jar. Here are all the steps that are needed.
Identify the classes (from your app) that need to be visible to the login module.
Put all those classes into your messageHubLoginLib (e.g. in a separate streaming-service.jar)
DELETE those classes from your app.
Configure the app with a class loader child element that references the login module library using a commonLibraryRef attribute:
<webApplication id="streaming-service" location="streaming-service.war" name="streaming-service">
<classloader commonLibraryRef="messageHubLoginLib" />
</webApplication>
Generate a streaming-service.jar and add it directly to the messageHubLoginLib should be the right way to do. In this case, you have a jar file that is share-able between your JAAS login module and Web App.
However, you said "CustomCredentialProvider class is loaded completely outside the context of my running web application and still gives me no access my credentials". It sounds like you trying to store the credential in the CustomCredentialProvider class for uses between the login module and web app.
The CustomCredentialProvider class should be independent between the login module and web app. When request come in and try to access the web app, the server trigger the login module for authentication. If it passed, the access to the web app will be successfully.
I don't see you posted. May be you are missing the following configuration?
<jaasLoginContextEntry id="system.WEB_INBOUND" name="system.WEB_INBOUND" loginModuleRef="KafkaClient,hashtable,certificate,token" />
Can you place the MultiUserLoginModule and CustomCredentialProvider jar file in the ${server.config.dir}/lib/global directory and change the libraryRef point to global directory in your server.xml file as following:
<jaasLoginModule className="com.ibm.messagehub.login.MultiUserLoginModule" controlFlag="REQUIRED" id="KafkaClient" libraryRef="global">
<options credentialProvider="myApp.CustomCredentialProvider" serviceName="kafka"/>
</jaasLoginModule>
In a Wildfly 8.1.0.Final we deploy:
our own CRM-webapp (Seam2/JSF1.2)
camunda-webapp 7.3.0
camunda-engine 7.3.0 as a module (shared engine)
custom engine-plugin to enable camunda-engine to use the user/group-store of our CRM
We display camunda tasklist in an iframe inside our CRM.
This setup runs fine so far, but we have to login twice.
So we need SSO, but cannot establish AD/LDAP, like in camunda-sso-jboss example.
I thought of Wildfly's JAAS and SSO capabilities, but i'am not sure, if camunda-webapp supports JAAS-authentication.
I think the security-domain configuration in jboss-web.xml is just generated by a maven archetype and has no effect on the camunda-webapp, is that right? I changed that configuration and it had no effect at all.
Can someone give me a hint, where i should hook into camunda-webapp or if it is possible at all?
Ok, i have a first success.
I changed org.camunda.bpm.webapp.impl.security.auth.Authentications.getFromSession to accept HttpServletRequest as parameter instead of HttpSession (called from AuthenticationFilter.doFilter). If the session contains no Authentications, i try to pull the Principle from the request and if one exists, i log em in silently (copied most from UserAuthenticationResource.doLogin).
Then i have a very simple webapp ("testA") with only one JSP and Basic Authentication. Both camunda-webapp and testA have the same security-domain configured, and the host in the undertow-subsystem has the "single-sign-on"-setting.
Now i can login into /testA, then call /camunda in another tab without further authentication.
The code has to be improved a lot. If everythink works fine, i'll post the details.
If someone thinks this is a wrong approach, please let me know ;-)
I have a CDI -> EJB App.
I do the security in the past with JBoss j_security.
My security with Shiro works.
But my only problem is how can I get SessionContext in my EJB?
With Jboss Security I got the username, who login in the location with:
SessionContext sessionContext;
String email = sessionContext.getCallerPrincipal().getName();
Now I want to get the the username in my EJB.
How ca I set the username with SessionContext?
Thank you for help
This is a bit tricky, and it also depends on the version of JBoss that you are using. In the AS 7.x and EAP 6.x range this can't really be done by using public APIs because of several bugs.
In order to make the sessionContext aware of the user name and roles you can use JBoss specific code like I used here: https://github.com/javaeekickoff/jboss-as-jaspic-patch/commit/d691fd4532d9aeae6136e3adc2537ff81c525673
It should be something like;
SecurityContext context = SecurityActions.getSecurityContext();
context.getUtil().createSubjectInfo(new SimplePrincipal(userName),
null,
someSubject
);
Take a look at the rest of the file to see how someSubject should be created and populated.
Unfortunately for the mentioned JBoss versions #RolesAllowed will never work, since JBoss doesn't take over the already authenticated identity from the local caller, but will always consult a JBoss specific "security domain" just prior to calling the actual bean. Of course it known nothing about Shiro.
I'm trying to generate a Perl library to connect to a WebService. This webservice is in an HTTPS server and my user has access to it.
I've executed wsdl2perl.pl several times, with different options, and it always fails with the message: Unauthorized at /usr/lib/perl5/site_perl/5.8.8/SOAP/WSDL/Expat/Base.pm line 73.
The thing is, when I don't give my user/pass as arguments, it doesn't even asks for them.
I've read [SOAP::WSDL::Manual::Cookbook] (http://search.cpan.org/~mkutter/SOAP-WSDL-2.00.10/lib/SOAP/WSDL/Manual/Cookbook.pod) and done what it says about HTTPS: Crypt::SSLeay is instaleld, and both SOAP::WSDL::Transport::HTTP and SOAP::Transport::HTTP are modified.
Can you give any hint about what may be going wrong?
Can you freely access the WSDL file from your web browser?
Can someone else in your network access it without any problems?
Maybe the web server hosting the WSDL file requires Basic or some other kind of Authentication...
If not necessary ,I don't recommend you to use perl as a web service client .As you know ,perl is a open-source language,although it do support soap protocol,but its support do not seem very standard.At first,its document is not very clear.And also ,its support sometimes is limited.At last,bug always exists here and there.
So ,if you have to use wsdl2perl,you can use komodo to step into the code to find out what happened.This is just what I used to do when using perl as a web service client.You know ,in the back of https is SSL,so ,if your SSL is based on certificate-authorized,you have to set up your cert path and the list of trusted server cert.You'd better use linux-based firefox to have a test.As I know ,you can set up firefox's cert path and firefox's trusted cert list.If firefox can communicated with your web service server succefully,then,it's time to debug your perl client.
To debug situations with Perl and SOAP, interpose a web proxy so you can see exactly what data is being passed and what response comes back from the server. You were getting a 401 Not authorized, I expect, but there may be more detail in the server response.
Both Fiddler http://docs.telerik.com/fiddler and Charles proxy https://www.charlesproxy.com/ can do this.
The error message you quote seems to be from this line :
die $response->message() if $response->code() ne '200';
and in HTTP world, Unauthorized is clearly error code 401, which means your website asks for a username and password (most probably, some website may "hijack" this error code to cater for other conditions like a filter on the source IP).
Do you have them?
If so, you can
after wdsl2perl has run, find in the created files where set_proxy() is called and change the URL in there to include the username and password like that : ...->set_proxy('http://USERNAME:PASSWORD#www.example.com/...')
or your in code, after instantiating the SOAP::WSDL object, call service(SERVICENAME) on it (for each service you have defined in your WSDL file), which gives you a new object, on which you call transport() to access the underlying transport object on which you can call proxy() with the URL as formatted above (yes it is proxy() here and set_proxy() above); or you call credentials() instead of proxy() and you pass 4 strings:
'HOSTNAME:PORT'
the realm, as given by the webserver but I think you can put anything
the username
the password