Twisted SMTP server, adding SSL support, 2 factories? - ssl

Having recently asked Twisted mail server with TLS - no portal? It seems I was barking up the wrong tree - so now it seems pertinent and appropriate to ask a fresh, revised question.
So I am trying to extend the basic SMTP server sample found at http://twistedmatrix.com/documents/current/_downloads/emailserver.tac to support SSL connections. I can create an ssl Context Factory as follows (code lifted from my previous efforts:
caCertFile = open("/opt/tesa/etc/certs/CA/cacert.pem","r")
certFile = open("/opt/tesa/etc/certs/server/server.crt","r")
keyFile = open("/opt/tesa/etc/certs/server/server.key","r")
caCertData = caCertFile.read()
pKeyData = keyFile.read()
certData = certFile.read()
caCert = ssl.Certificate.loadPEM(caCertData)
cert = load_certificate(FILETYPE_PEM, certData)
pKey = load_privatekey(FILETYPE_PEM, pKeyData)
sslCtxFactory = ssl.CertificateOptions(privateKey=pKey, certificate=cert, trustRoot=caCert)
So now I have the SMTP Factory from emailserver.tac, and my sslCtxFactory above. In my old code, I created an object of class ESMTP as follows:
myESMTP = ESMTP(contextFactory=sslCtxFactory)
factory = protocol.Factory.forProtocol(lambda: mySMTP(contextFactory=sslCtxFactory))
endpoint = endpoints.TCP4ServerEndpoint(reactor, 8001)
endpoint.listen(factory)
But since this code isn't using a portal etc, then some of the methods/functions in the class definition for ESMTP fail owing to various attributes not being defined (eg self.delivery, self.portal).
So I'm trying to understand how to implement my sslCtxFactory in the mailserver.tac code - while still using a portal / realm as per mailserver.tac, so as to ensure the methods in twisted's ESMTP class don't fail.
But how can I bring these two factories "together"?
Thanks

This looks strange:
myESMTP = ESMTP(contextFactory=sslCtxFactory)
factory = protocol.Factory.forProtocol(lambda: mySMTP(contextFactory=sslCtxFactory))
Instances of the ESMTP class provided by Twisted aren't callable. I think you must have meant something like:
from functools import partial
myESMTP = partial(ESMTP, contextFactory=sslCtxFactory)
factory = protocol.Factory.forProtocol(mySMTP)
Assuming so...
The ESMTP class respects the self.portal attribute for its authentication and authorization behavior. All you need to do is get it set.
SMTPFactory is a factory for SMTP instances that sets the protocol's portal attribute to the value of its own (the factory's) portal attribute. The only trouble is that it makes SMTP instances instead of ESMTP instances.
Fortunately, it (the factory) respects its protocol attribute for the class to instantiate.
If you were to write:
portal = Portal(...)
myESMTP = partial(ESMTP, contextFactory=...)
factory = SMTPFactory.forProtocol(protocol=myESMTP, portal=portal)
then you would have a factory which:
creates instances of ESMTP
passes contextFactory=... to the initializer of ESMTP when instantiating it
sets the portal attribute on the ESMTP instances after it instantiates them, using the value passed in to forProtocol here.

Related

Native way to send custom object in JMS queue? (citrus-simulator)

I'm looking for a native (correct, in terms of the framework) way to send thirdparty (custom) Object to JMS in citrus-simulator?
I have tried:
scenario
.send()
.payloadModel(myObject);
but in JMS queue myObject appears as com.consol.citrus.message.DefaultMessage instead of com...myObject.
Example:
Scenario - receives http POST request (as trigger) and send's JMS Object to message queue.
config
...
.useObjectMessages(true)
...
scenario
...
import com.temafon.data.MORequest;
#Scenario("morequest")
#RequestMapping(value = "/simulator/morequest",method = RequestMethod.POST)
public class JmsMoRequestScenario extends AbstractSimulatorScenario {
#Override
public void run(ScenarioDesigner scenario) {
MORequest request = new MORequest(12345678901L, "USSD", "1172", "ON",
11L);
request.setSourcePort(3);
scenario
.receive()
.payload("getmorequest");
scenario
.send("jms.queue.destination")
.payloadModel(request);
After this case, I expect to get in jms.queue.destination something like screen 1
Expectation
Reality
P.S. I have implemented workaround already, with JavaActionBuilder and jmsTemplate.send in additional class and
scenario
.java(//Object).method(//instance");
But it doesn't seems like correct citrus-simulator way
Setting the payloadModel() in Citrus Java DSL will always result in some kind of marshalled representation of the object and this is not what you want. Neither is using .useObjectMessages(true) working for you because this results in the whole Citrus message object to be used as message payload.
You need to define a complete Citrus message object and that will remain untouched in terms of payload creation for the JMS destination. Citrus is then automatically using a JMS object message with proper object payload.
MORequest request = new MORequest(12345678901L, "USSD", "1172", "ON", 11L);
request.setSourcePort(3);
scenario
.receive()
.payload("getmorequest");
scenario
.send("jms.queue.destination")
.message(new JmsMessage(request));
Note that I am using the .message() fluent API instead of the payloadModel() API. The message API receives a Citrus message object such as com.consol.citrus.jms.message.JmsMessage or com.consol.citrus.message.DefaultMessage
Your custom domain model object request is used as constructor arg and will result in the JMS message as object payload as is. Of course MORequest must be of type java.io.Serializable

How can I search for ldap fields when using ActiveDirectoryRealm in Apache Shiro?

We use Apache Shiro to authenticate and authorize users using our active directory.
Authenticating the user and mapping groups works just fine using the following config:
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.searchBase = "OU=MYORGANIZATION,DC=MYDOMAIN,DC=COM"
adRealm.groupRolesMap = "CN=SOMEREADGROUP":"read","CN=SOMEMODIFYGROUP":"modify","CN=SOMEADMINGROUP":"admin"
adRealm.url = ldaps://my.ad.url:636
adRealm.systemUsername= systemuser
adRealm.systemPassword= secret
adRealm.principalSuffix= #myorganization.mydomain.com
I can authenticate in Shiro using the following lines:
String user = "someuser";
String password = "somepassword";
Subject currentUser = SecurityUtils.getSubject ();
if (!currentUser.isAuthenticated ()){
UsernamePasswordToken token = new UsernamePasswordToken (user,
password);
token.setRememberMe (true);
currentUser.login (token);
}
We now want to get more user information from our ActiveDirectory. How can I do that using Apache Shiro? I was not able to find anything about it in the documentation.
In the source code of ActiveDirectoryRealm I found this line:
NamingEnumeration answer = ldapContext.search(searchBase, searchFilter, searchArguments, searchCtls);
So the first part of the answer is clear: use the ldapContext to search something in it. But how can I retrieve the LdapContext?
It depends on what you are trying to do. Are you just trying to reuse the context to run a query for something other then authentication or authorization? Or are you trying to change the behavior of the query in the AD realm?
If the latter, you would need to extend the ActiveDirectoryRealm and override the queryForAuthorizationInfo() method.
Are you implementing something that is custom for your environment?
(updated)
A couple things:
The realm has access to the LdapContext in the two touch points: queryForAuthenticationInfo() and queryForAuthorizationInfo(), so if you extend the AD realm or AbstractLdapRealm you should already have it. You could change the query to return other info and add the extra info to your Principal. Then you have access to that info directly from your Subject object.
Your realms, are not required to be singletons.
If you want to do some other sort of user management (email all users with a given role, create a user, etc). Then you could create a LdapContextFactory in your shiro.ini, and use the same instance for multiple objects.
[main]
...
ldapContextFactory = org.apache.shiro.realm.ldap.JndiLdapContextFactory
ldapContextFactory.systemUsername = foobar
ldapContextFactory.systemPassword = barfoo
adRealm = org.apache.shiro.realm.activedirectory.ActiveDirectoryRealm
adRealm.ldapContextFactory = $ldapContextFactory
...
myObject = com.biz.myco.MyObject
myObject.ldapContextFactory = $ldapContextFactory
This would work well if myObject is interacting with other Shiro components, (responding to events, etc), but less so if you need access to it from another framework. You could work around this by some sort of static initialization that builds creates the ldapContextFactory, but in my opinion, this is where the sweet spot of using the shiro.ini ends, and where using Guice or Spring shines.

Calling third party secure webservice through WCF - authentication problems

I'm writing a client against a vendor's webservice, using WCF in Visual Studio 2010. I have no ability to change their implementation or configuration.
Running against an install on their test server, I had no problems. I added a service reference from their wsdl, set the url in code, and made the call:
var client = new TheirWebservicePortTypeClient();
client.Endpoint.Address = new System.ServiceModel.EndpointAddress(webServiceUrl);
if (webServiceUsername != "")
{
client.ClientCredentials.UserName.UserName = webServiceUsername;
client.ClientCredentials.UserName.Password = webServicePassword;
}
TheirWebserviceResponse response = client.TheirOperation(myRequest);
Simple and straightforward. Until they moved it to their production server and configured it to use https. Then I got this error:
The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Basic realm='.
So I went looking for help. I found this: Can not call web service with basic authentication using wcf.
The approved answer suggested this:
BasicHttpBinding binding = new BasicHttpBinding();
binding.SendTimeout = TimeSpan.FromSeconds(25);
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType =
HttpClientCredentialType.Basic;
EndpointAddress address = new EndpointAddress(your-url-here);
ChannelFactory<MyService> factory =
new ChannelFactory<MyService>(binding, address);
MyService proxy = factory.CreateChannel();
proxy.ClientCredentials.UserName.UserName = "username";
proxy.ClientCredentials.UserName.Password = "password";
Which also seemed simple enough. Except for my trying to figure out which of the multitude of classes and interfaces that were generated from the wsdl to make the service reference I should use in place of the "MyService", above.
My first try was to use "TheirWebservicePortTypeClient" - the class I had instantiated in the previous version. That gave me a runtime error:
The type argument passed to the generic ChannelFactory class must be an interface type.
So I dug into the generated code, a bit more. I saw this:
public partial class TheirWebservicePortTypeClient
:
System.ServiceModel.ClientBase<TheirWebservicePortType>,
TheirWebservicePortType
{
...
}
So I tried instantiating ChannelFactory<> with TheirWebservicePortType.
This gave me compile-time errors. The resulting proxy didn't have a ClientCredentials member, or a TheirOperation() method.
So I tried "System.ServiceModel.ClientBase".
Instantiation ChannelFactory<> with it still gave me compile-time errors. The resulting proxy did have a ClientCredentials member, but it still didn't have a TheirOperation() method.
So, what gives? How do I pass a username/password to an HTTPS webservice, from a WCF client?
==================== Edited to explain the solution ====================
First, as suggested, instantiation the factory with TheirWebservicePortType, adding the username and password to the factory.Credentials, instead of to proxy.ClientCredentials worked fine. Except for one bit of confusion.
Maybe it's something to do with the odd way the wsdl is written, but the client class, TheirWebservicePortTypeClient, defined TheirOperation as taking a Request argument and returning a Response result. The TheirWebservicePortType interface defined TheirOperation as taking a TheirOperation_Input argument and returning a TheirOperation_Output result, where TheirOperation_Input contained a Request member and TheirOperation_Output contained a Response member.
In any case, if I constructed a TheirOperation_Input object from the passed Request, the call to the proxy succeeded, and I could then extract the contained Response object from the returned TheirOperation_Output object:
TheirOperation_Output output = client.TheirOperation(new TheirOperation_Input(request));
TheirWebserviceResponse response = output.TheirWebserviceResponse;
You add the credentials to the ChannelFactory Credentials property

WCF Unique ID for each service method call

I'm logging using log4net, and I want to log a id that is unique for each serice method call. I dont need it unique across service calls, just within a method call. Is there any built in id i can use in wcf? I don't want to manually create a guid or something at the start of the method call.
e.g.
wcfMethod(int x)
{
log("xxx");
somework
log("yyy");
}
private log(string message)
{
var frame = new StackFrame(1);
var method = frame.GetMethod();
var type = method.DeclaringType;
var name = method.Name;
var log = LogManager.GetLogger(type);
// LOG ID HERE
ThreadContext.Properties["MessageId"] = OperationContext.Current.IncomingMessageHeaders.MessageId; // SOMETHING HERE
}
I've tried OperationContext.Current.IncomingMessageHeaders.MessageId but thats always null.
I've read about wcf instance correlation but i don't need something that complicated (e.g. unique across different method calls).
Please if anyone can help that would be much apprieciated. Thanks in advance.
Plain SOAP or REST has no such identification included in messages. You must use some additional feature or transport protocol (for example MSMQ) supporting identifications of messages. In case of MessageId you have to use SOAP service with WS-Addressing and this information must be passed from client.

Where do I specify proxy credentials in my WCF client?

I have created a WCF client using channel factory.
But I am not able to connect to a server in another machine. I am getting a (407) Prxy Authentication Required exception.
WSHttpBinding wsBinding = new WSHttpBinding();
wsBinding.BypassProxyOnLocal = true;
EndpointAddress endpoint =
new EndpointAddress("http://machineName:7676/MyWCFService");
ChannelFactory<IService> sericeInterface =
new ChannelFactory<IService>(wsBinding, endpoint);
sericeInterface.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
sericeInterface = sericeInterface.CreateChannel();
This is my client connection code nippet. I am getting exception when I call a method of the service.
Take a look at this CodePlex link, try to find a scenario that matches closely to yours. It provides checklists and samples of how to set the various credentials for different situations/bindings.
Also this MSDN link might help with Windows Authentication, which you seem to be using.
To assign credentials you'll need something like the below taken from the MSDN link:
CalculatorClient cc = new
CalculatorClient("WSHttpBinding_ICalculator");
// This code returns the WindowsClientCredential type.
cc.ClientCredentials.Windows.ClientCredential.UserName = GetUserName();
cc.ClientCredentials.Windows.ClientCredential.Password = GetPassword();