I have the following working code, that successfully connects to a WS using WSSecurity with UsernameToken authentication:
public void addValidation(
Stub stub) throws Exception {
ServiceClient serviceClient = stub._getServiceClient();
serviceClient.engageModule("rampart");
Options options = serviceClient.getOptions();
options.setProperty(
WSSHandlerConstants.OUTFLOW_SECURITY,
getOutflowConfiguration());
serviceClient.setOptions(options);
}
private Parameter getOutflowConfiguration() {
OutflowConfiguration outflowConfiguration =
new OutflowConfiguration();
outflowConfiguration.setActionItems(
"UsernameToken");
outflowConfiguration.setUser(
this.username);
outflowConfiguration.setPasswordType(
"PasswordText");
outflowConfiguration.setPasswordCallbackClass(
"es.ssib.otic.inte_portafib.cliente_custodia_axis2.PWCBHandler");
return
outflowConfiguration.getProperty();
}
Now, since I want to pass the password programatically, I am trying to configure the password as a parameter, but it does not work.
I have tried replacing the "outflow configuration" with
options.setUserName(
this.username);
options.setPassword(
this.password);
But those fields are ignored by Axis 2 completely.
I have tried using this example
public void addValidation(
Stub stub) throws Exception {
ServiceClient serviceClient =
stub._getServiceClient();
Options options = serviceClient.getOptions();
options.setUserName(this.username);
options.setPassword(this.password);
options.setProperty(
RampartMessageData.KEY_RAMPART_OUT_POLICY,
loadPolicy());
serviceClient.engageModule("rampart");
}
private Policy loadPolicy() throws XMLStreamException, IOException {
InputStream resource = new FileInputStream([path to policy.xml]");
StAXOMBuilder builder = new StAXOMBuilder(resource);
return PolicyEngine.getPolicy(builder.getDocumentElement());
}}
with this policy file
<wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SupportingTokens
xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/
ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient"/>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
but it fails with the message:
Incorrect inclusion value: -1
I am using Rampart 1.5 with Axis2 1.4
UPDATE: I know how to make the CallbackHandler check a static constant, or to invoke some data object to get the password. But I do not want to do that.
Related
I'm trying to create a websocket in java that listens to a local application on the endpoint f.ex. "wss://localhost.localapp.com:8080/".
The application do send through that websocket information about what is happening.
When I run the web-application and it tries to connect to the secure websocket it throws this error:
XNIO000100: 'https' URL scheme chosen but no SSL provider given
Here is my code for connecting to the client endpoint:
#ClientEndpoint
public class ClientEndpoint {
private Logger logger = Logger.getLogger(getClass().getName());
public ClientEndpoint() {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
container.connectToServer(this, new URI("wss://localhost.localapp.com:8080/"));
} catch (DeploymentException | URISyntaxException | InterruptedException | IOException e) {
System.out.println("Connection error occured!: " + e);
}
}
#OnOpen
public void onOpen(Session session) {
this.logger.info("New websocket session opened: " + session.getId());
}
#OnClose
public void onClose(Session session) {
this.logger.info("Websoket session closed: " + session.getId());
}
#OnMessage
public void onMessage(Session session, String message) throws IOException, EncodeException {
this.logger.info("Message recieved: " + message);
}
#OnError
public void error(Session session, Throwable t) {
t.printStackTrace();
}
}
So the errors happends on this code line:
container.connectToServer(this, new URI("wss://localhost.localapp.com:8080/"));
And the stack trace output this:
ERROR localhost jboss7.1 [RunnerReadFacade] Application was stopped due to exception. Transaction is rollbacked.: java.lang.IllegalArgumentException: XNIO000100: 'https' URL scheme chosen but no SSL provider given
at org.xnio.http.HttpUpgrade$HttpUpgradeState.doUpgrade(HttpUpgrade.java:253)
at org.xnio.http.HttpUpgrade$HttpUpgradeState.access$100(HttpUpgrade.java:165)
at org.xnio.http.HttpUpgrade.performUpgrade(HttpUpgrade.java:129)
at io.undertow.websockets.client.WebSocketClient$ConnectionBuilder.connectImpl(WebSocketClient.java:323)
at io.undertow.websockets.client.WebSocketClient$ConnectionBuilder.connect(WebSocketClient.java:211)
at io.undertow.websockets.jsr.ServerWebSocketContainer.connectToServerInternal(ServerWebSocketContainer.java:463)
at io.undertow.websockets.jsr.ServerWebSocketContainer.connectToServerInternal(ServerWebSocketContainer.java:457)
at io.undertow.websockets.jsr.ServerWebSocketContainer.connectToServer(ServerWebSocketContainer.java:211)
How should I solve this?
With JavaScript I do not need a SSL provider and can simply create the connection with the websocket just by running:
websocket = new WebSocket("wss://localhost.localapp.com:8080/");
Why does the error only occure to Java?
UPDATE:
This probably cannot be done because the application is on your local computer and the server is not local.
But could this be done if you would use a local server?
You can either switch to Programmatic Client Endpoint or define Thread local SSL Context. See details at https://issues.jboss.org/browse/THORN-2131 (wsstest-master.zip includes demo app). 'Implement io.undertow.websockets.jsr.WebsocketClientSslProvider and add a META-INF/services entry' didn't work for me.
I have to implement a JAX-WS-Client which consumes a service from an external partner. I use Apache CXF. The service defines two WS-Policies for authentication in the wsdl - KerberosToken and UsernameToken. Since the service is from an external partner it cant be changed.
The problem: Authentication fails at Kerberos-Authentication since I want to use
the simple UsernameToken-Authentication.
WSLD-Policy-Part:
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="SecurityServiceUsernameUnsecureTransportPolicy">
<wsp:ExactlyOne>
<wsp:All>
<sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:KerberosToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssGssKerberosV5ApReqToken11/>
</wsp:Policy>
</sp:KerberosToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:All>
<wsp:All>
<sp:SupportingTokens xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:All>
</wsp:ExactlyOne>
</wsp:Policy>
Setting Username/Password at CXF-Endpoint:
public void addAuthenticationProperties( Endpoint endpoint ) {
endpoint.put( SecurityConstants.USERNAME, userName );
endpoint.put( SecurityConstants.PASSWORD, password );
}
As I understand the -Tag means, that if any (excatly one) of this policies is fullfilled move on. But CXF does not even try to fullfill the UsernameToken - Policy.
If I remove the -Block for KerberosToken authentication works fine, but in production this is not possible.
Any hints? If there is an error in the wsdl or in my approach please let me know. And please be detailed - I am a noob in this field.
Thanks in advance!
Some days later I found a workaround for this problem - wouldn't call it solution ;-)
At first the old version:
CXF generated a class ExampleWS_Service and a corresponding interface ExampleWS. So I used the ExampleWS_Service to set everthing up and invoke the service:
// creating Port
URL url = new URL( config.getSchema(), config.getHost(), config.getPort(), config.getPath() );
ExampleWS_Service service = new ExampleWS_Service( url );
ExampleWS port = service.getExampleWSPort();
// Adding authentication-info
Client client = ClientProxy.getClient( port );
Endpoint cxfEndpoint = client.getEndpoint();
cxfEndpoint.put( SecurityConstants.USERNAME, userName );
cxfEndpoint.put( SecurityConstants.PASSWORD, password );
...
// invoke service
port.doSomething( [data] );
The workaround:
I switched to JaxWsProxyFactoryBean and setting everthing up. In this case a WSS4JOutInterceptor is needed to put authentication information in the request-header. Some code:
URL url = new URL( config.getSchema(), config.getHost(), config.getPort(), config.getPath() );
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.setServiceClass( ExampleWS.class );
factory.setAddress( url.toString() );
factory.setBindingId( "http://schemas.xmlsoap.org/wsdl/soap12/" );
ExampleWS port = ( ExampleWS )factory.create();
// Adding authentication-info using WSS4JOutInterceptor
Client client = ClientProxy.getClient( port );
Endpoint cxfEndpoint = client.getEndpoint();
Map<String, Object> outProps = new HashMap<>();
outProps.put( WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN );
outProps.put( WSHandlerConstants.USER, userName );
outProps.put( WSHandlerConstants.PASSWORD_TYPE, passwordType );
outProps.put( WSHandlerConstants.PW_CALLBACK_REF, [Instance of javax.security.auth.callback.CallbackHandler] );
WSS4JOutInterceptor wssOut = new WSS4JOutInterceptor( outProps );
cxfEndpoint.getOutInterceptors().add( wssOut );
...
// invoke service
port.doSomething( [data] );
I don't know why exactly this workaround actually work, but it does :-)
Perhaps someone of you can give me so enlightenment.
CXF doesn't handle multiple policy options on the outbound side for security, only on the inbound side. So your only option is tighten the policy to either Kerberos or UsernameToken, depending on whatever policy you want your client to use.
Im following and using the sample code from Custom Authenticator and Login Module and UserAdapter from Java SQL Adapter.
I want to get the user list after authenticated.
My configuring the authenticationConfig.xml file
<realms>
<realm loginModule="CustomLoginModule" name="CustomAuthenticatorRealm">
<className>com.mypackage.MyCustomAuthenticator</className>
</realm>
</realms>
<loginModules>
<loginModule name="CustomLoginModule">
<className>com.mypackage.MyCustomLoginModule</className>
</loginModule>
</loginModules>
My configuring the Java adapter, UserAdapterResource.java file
#GET
#Produces("application/json")
#OAuthSecurity(scope="CustomAuthenticatorRealm")
public Response getAllUsers() throws SQLException{
JSONArray results = new JSONArray();
Connection con = ds.getConnection();
PreparedStatement getAllUsers = con.prepareStatement("SELECT * FROM users");
ResultSet data = getAllUsers.executeQuery();
while(data.next()){
JSONObject item = new JSONObject();
item.put("userId", data.getString("userId"));
item.put("firstName", data.getString("firstName"));
item.put("lastName", data.getString("lastName"));
item.put("password", data.getString("password"));
results.add(item);
}
getAllUsers.close();
con.close();
return Response.ok(results).build();
}
But when I invoke the procedure above on client-side, it still return a response without authentication require, while it have to show a login module
From your code you only have a challenge handler for the CustomAuthenticatorRealm realm. Why not updated your adapter and protect it with that same realm instead of using myRealm.
Updated UserAdapterResource.java skeleton
#Path("/")
public class UserAdapterResource {
// ...
#POST
#OAuthSecurity(scope="CustomAuthenticatorRealm")
public Response createUser(#FormParam("userId") String userId,
#FormParam("firstName") String firstName,
#FormParam("lastName") String lastName,
#FormParam("password") String password)
throws SQLException{
// ...
}
#GET
#Produces("application/json")
#Path("/{userId}")
public Response getUser(#PathParam("userId") String userId) throws SQLException{
// ...
}
#GET
#Produces("application/json")
#OAuthSecurity(scope="CustomAuthenticatorRealm")
public Response getAllUsers() throws SQLException{
// ...
}
// it's a good practice to protect this operation
#PUT
#Path("/{userId}")
#OAuthSecurity(scope="CustomAuthenticatorRealm")
public Response updateUser(#PathParam("userId") String userId,
#FormParam("firstName") String firstName,
#FormParam("lastName") String lastName,
#FormParam("password") String password)
throws SQLException{
// ...
}
// it's a good practice to protect this operation
#DELETE
#Path("/{userId}")
#OAuthSecurity(scope="CustomAuthenticatorRealm")
public Response deleteUser(#PathParam("userId") String userId) throws SQLException {
// ...
}
}
With these changes, when the application launches it will show the login form to authenticate before showing the list of users.
UPDATE:
The Java Adapter protection is using OAuth and so the MobileFirst server issues a token for authentication. This token has a lifespan with an expiration. Logging out of a realm doesn't affect the token.
One way to implement this based on your needs is to decrease the TTL (time to live) of your token to something like 10 or 15 seconds (or whatever you want). You can do this by setting the expirationInSeconds attribute in your login module inside authenticationConfig.xml.
authenticationConfig.xml
<!-- token will expire 10 seconds after being issued -->
<loginModule name="CustomLoginModule" expirationInSeconds="10">
<className>com.mypackage.MyCustomLoginModule</className>
</loginModule>
If 10 seconds have passed since the app connected to the server via adapter invocation or any other method then the user will need to reauthenticate.
I've been struggling with this problem for a whole day and do not know how to fix it. I have tried various things to resolve the issue but I am at a loss.
I have a project where I am attempting to use the LogReceiverServer from NLog to send and receive messages between 2 PCs. I followed this example here. Everything actually works fine, my WCF service starts up correctly, my client starts up correctly, even the sending of the message to log from client to server works. But, when I shut the client down, I get SocketExceptions thrown by the server for each message that was transmitted. I know this is due to the channel not being closed properly by the client. I cannot find where I must close the channel to prevent the exceptions being thrown by my server. I have read that to manually close the channel I must use
Channel.Close();
would that be correct and where would I put that?
I want to prevent these SocketExceptions. I have found this, but it does not seem to be the correct thing to do. Correct me if I am wrong, but would the solution not use the same principles?
Unless of course I am understanding this completely wrong...
Everything is done using the config files (App.Config and NLog.Config).
Here is my LogReceiverService Target from NLog.config:
<target xsi:type="LogReceiverService"
name="logreceiver"
endpointConfigurationName="LogReceiverClient"
endpointAddress="net.tcp://server:8888/NLogServices/LogReceiverServer/logreceiverserver" />
Here is my endpoint from my app.config:
<endpoint address="net.tcp://server:8888/NLogServices/LogReceiverServer/logreceiverserver"
binding="netTcpBinding"
bindingConfiguration="LogReceiverClient"
contract="NLog.LogReceiverService.ILogReceiverClient"
name="LogReceiverClient" />
Any help or advise would greatly be appreciated.
EDIT: Extended on problem description
OK, So first, here is the Service on my host pretty much as I got it from here:
/// <summary>
/// Log service server object that logs messages.
/// </summary>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class LogReceiverServer : ILogReceiverServer
{
public void ProcessLogMessages(NLogEvents nevents)
{
var events = nevents.ToEventInfo("Client.");
foreach (var ev in events)
{
var logger = LogManager.GetLogger(ev.LoggerName);
logger.Log(ev);
}
}
}
I then created this class, where I inherit from LogReceiverWebServiceTarget and override protected virtual WcfLogReceiverClient CreateWcfLogReceiverClient(); method. It is exactly the same as is found on GitHub here, except that I registered on the ProcessLogMessagesCompleted event where I close the 'client':
[Target("wcftarget")]
public class WcfTarget : LogReceiverWebServiceTarget
{
protected override WcfLogReceiverClient CreateWcfLogReceiverClient()
{
WcfLogReceiverClient client;
if (string.IsNullOrEmpty(EndpointConfigurationName))
{
// endpoint not specified - use BasicHttpBinding
Binding binding;
if (UseBinaryEncoding)
{
binding = new CustomBinding(new BinaryMessageEncodingBindingElement(), new HttpTransportBindingElement());
}
else
{
binding = new BasicHttpBinding();
}
client = new WcfLogReceiverClient(binding, new EndpointAddress(EndpointAddress));
}
else
{
client = new WcfLogReceiverClient(EndpointConfigurationName, new EndpointAddress(EndpointAddress));
/*commenting this out causes multiple socket exceptions on host*/
client.ProcessLogMessagesCompleted += client_ProcessLogMessagesCompleted;
}
return client;
}
private void client_ProcessLogMessagesCompleted(object sender, AsyncCompletedEventArgs e)
{
WcfLogReceiverClient client = sender as WcfLogReceiverClient;
if (client.State == CommunicationState.Opened)
{
(sender as WcfLogReceiverClient).Close();
}
}
}
The Logger in NLog.config is:
<logger name="*" writeTo="logreceiver" minlevel="Info" />
So then if I try to log like this:
class Program
{
private static NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();
private static void Main(string[] args)
{
logger.Info("foo");
}
}
my host gives prints this to Debug:
A first chance exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
A first chance exception of type 'System.ServiceModel.CommunicationException' occurred in System.ServiceModel.dll
Will this have any impact on performance of the host over a long period of time?
The problem has been resolved: https://github.com/NLog/NLog/commit/138fd2ec5d94072a50037a42bc2b84b6910df641
I'm building a WCF router and my client uses Reliable Sessions. In this scenario when the client opens a channel a message is sent (establishing a Reliable Session?). Its contents is as follows:
<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing">
<s:Header>
<a:Action s:mustUnderstand="1">http://docs.oasis-open.org/ws-rx/wsrm/200702/CreateSequence</a:Action>
<a:MessageID>urn:uuid:1758f794-c5d3-4573-b252-7a07344cc257</a:MessageID>
<a:To s:mustUnderstand="1">http://localhost:8010/RouterService</a:To>
</s:Header>
<s:Body>
<CreateSequence xmlns="http://docs.oasis-open.org/ws-rx/wsrm/200702">
<AcksTo>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</AcksTo>
<Offer>
<Identifier>urn:uuid:64a12658-71d9-4967-88ec-9bb0610f7ecb</Identifier>
<Endpoint>
<a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
</Endpoint>
<IncompleteSequenceBehavior>DiscardFollowingFirstGap</IncompleteSequenceBehavior>
</Offer>
</CreateSequence>
</s:Body>
</s:Envelope>
The problem here is that the headers do not contain any information I can use to look up what service to route the message to. In Busatmante's router sample code she gets around this by adding a header to the endpoint:
<client>
<endpoint address="http://localhost:8010/RouterService" binding="ws2007HttpBinding"
bindingConfiguration="wsHttp"
contract="localhost.IMessageManagerService" >
<headers>
<Route xmlns="http://www.thatindigogirl.com/samples/2008/01" >http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
</headers>
</endpoint>
</client>
When the reliable session is opened the message contains this custom header.
<Route a:IsReferenceParameter="true" xmlns="http://www.thatindigogirl.com/samples/2008/01">http://www.thatindigogirl.com/samples/2008/01/IMessageManagerService</Route>
This is great; however, I have a requirement to configure the client programatically. I figured that the ChannelFactory Endpoint would have a Header object to which I could manually add my custom header. Unfortunately it does not. So I did some searching and found some recomendations to extend WCF by implementing an IClientMessageInspector to add my header and adding it as a behavior to my endpoint.
public class ContractNameMessageInspector : IClientMessageInspector {
private const string HEADER_NAME = "ContractName";
private readonly string _ContractName;
public ContractNameMessageInspector(string contractName) {
_ContractName = contractName;
}
#region IClientMessageInspector Members
public void AfterReceiveReply(ref Message reply, object correlationState) { }
public object BeforeSendRequest(ref Message request, IClientChannel channel) {
HttpRequestMessageProperty httpRequestMessage;
object httpRequestMessageObject;
if (request.Properties.TryGetValue(HttpRequestMessageProperty.Name, out httpRequestMessageObject)) {
httpRequestMessage = httpRequestMessageObject as HttpRequestMessageProperty;
if (httpRequestMessage != null && string.IsNullOrEmpty(httpRequestMessage.Headers[HEADER_NAME])) {
httpRequestMessage.Headers[HEADER_NAME] = this._ContractName;
}
}
else {
httpRequestMessage = new HttpRequestMessageProperty();
httpRequestMessage.Headers.Add(HEADER_NAME, this._ContractName);
request.Properties.Add(HttpRequestMessageProperty.Name, httpRequestMessage);
}
return null;
}
#endregion
}
So when my client makes a service call the message contains the custom header but the message establishing the Reliable Sessions still does not.
So my question is; how do I add a custom header to the Endpoint programatically in such a way that the reliable session message contains it?
Many Thanks
Figured it out. Although there is no property or method to add a header to an EndpointAddress there is an optional parameter on the constructor.
_Binding = bindingFactory.GetBinding(serviceUri, typeof(T));
AddressHeader header = AddressHeader.CreateAddressHeader("ContractName", "NameSpace", typeof (T).ToString());
_Address = new EndpointAddress(serviceUri, header);
_ChannelFactory = new ChannelFactory<T>(_Binding, _Address);
Now when I receive the message establishing the reliable session it actually does contain my custom header. This kinda makes sense as the message inspector most likely only operates on dispatched messages while the message establishing the reliable session is generated by lower level WCF code.
This works for me
public TResult Invoke<TResult>(Func<TClient, TResult> func,MessageHeader header)
{
TClient client = default(TClient);
var sw = new Stopwatch();
try
{
sw.Start();
using (client = _channelFactory.CreateChannel())
{
using (OperationContextScope contextScope = new OperationContextScope(client))
{
OperationContext.Current.OutgoingMessageHeaders.Add(header);
return func.Invoke(client);
}
}
}
finally
{
CloseConnection(client);
Instrument(this.GetType().Name, sw);
}
}
To programmatically add address headers see MSDN's Address Headers where one can programmatically add a header such as:
var cl = new MyWCFClientContext();
var eab = new EndpointAddressBuilder(cl.Endpoint.Address);
eab.Headers.Add( AddressHeader.CreateAddressHeader("ClientIdentification", // Header Key
string.Empty, // Namespace
"JabberwockyClient")); // Header Value
cl.Endpoint.Address = eab.ToEndpointAddress();