-
Notifications
You must be signed in to change notification settings - Fork 560
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
Adding support for WSTrustChannelFactory and WSTrustChannel #4757
Conversation
/// </summary> | ||
[ServiceContract] | ||
[ComVisible(false)] | ||
public interface IWSTrustChannelContract : IWSTrustContract |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have this on any of our other interfaces. Is this here for a specific reason or just because of existing coding patterns?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Existing patterns.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
removed [ComVisible(false)] in: ab9b3c1
/// <returns>A message containing a WS-Trust response.</returns> | ||
[OperationContract(Name = "Cancel", Action = "*", ReplyAction = "*")] | ||
Task<Message> CancelAsync(Message message); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All of these methods are interchangeable as they take and return a Message and they all have the Action and ReplyAction to be "*". I need to spend a little bit of time trying some things with this code, but I'm pretty certain that the Name property is only used to provide the Action and ReplyAction if they aren't explicitly specified. This whole interface seems like it could be eliminated and replaced with a Task based equivalent of IRequestChannel. IWSTrustChannelContract has the strongly typed operations on it. I'll update once I know more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have done some local testing and confirmed what I believed. When an operation is defined like this, the is zero request shape that comes from the operation. Every method in this interface will generate the exact same on the wire message. I have no idea what the original intention was of the authors of the class in NetFx, but this interface make no sense at all to exist. We can remove this interface and just have IWSTrustChannelContract live on it's own.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does make a difference, the responsibility is on the user to create the correct message.
To make it easier for users we probably would want to make WSTrustBodyWriter public, although the code is simple.
see code here: https://github.com/brentschmaltz/WCF/blob/9b084e846fea828fbb5d65bbe8ac70bdd26f9ded/WsTrustClientCore/Program.cs#L46
SelfHostSTS will identify the different messages and route them.
For Renew see: https://github.com/brentschmaltz/WCF/blob/9b084e846fea828fbb5d65bbe8ac70bdd26f9ded/SelfHostSTS/SelfHostSecurityTokenService.cs#L215
/// Constructs a <see cref="WSTrustChannel" />. | ||
/// </summary> | ||
/// <param name="channelfactory">The <see cref="Federation.ChannelFactory" /> that is creating this object.</param> | ||
/// <param name="requestChannel">The <see cref="IRequestChannel" /> this object will be used to send and receive <see cref="Message" /> objects.</param> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the cref say Federation.ChannelFactory? I thought this was a regular S.SM.ChannelFactory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in: ab9b3c1
WsTrustVersion trustVersion) | ||
{ | ||
if (!(channelFactory is IRequestChannel requestChannel)) | ||
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetResourceString(SR.ChannelFactoryMustSupportIRequestChannel))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a little confused by this. Should the passed in ChannelFactory implement IRequestChannel, or should it produce clients which implement IRequestChannel? If the latter, there's no need to do any check as in Core, all channels implement IRequestChannel, but this code doesn't check for that. ChannelFactory itself doesn't implement IRequestChannel so this would always throw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in: ab9b3c1
void ICommunicationObject.Open(TimeSpan timeout) | ||
{ | ||
RequestChannel.Open(timeout); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the passed in IRequestChannel is already open, this will throw. Either need a state check here and only call Open if in state Created, or add validation that the passed in channel should be in the Created state.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When would it be called if it was already open?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using our code it won't be. But you've got to protect against people using the public API's outside of our usage. So someone could do this:
var factory = new ChannelFactory<IRequestChannel>(binding, endpoint);
var channel = factory.CreateChannel();
channel.Open();
var trustChannel = new WSTrustChannel(factory, channel, WsTrustVersion.Trust13);
((ICommunicationObject)trustChannel).Open();
If this code is a little more convoluted and the IRequestChannel gets passed in from somewhere else, someone could presume a newly created WSTrustChannel needs to be opened as that's the normal case. So thinking about it, I think we should throw in the constructor if the passed in IRequestChannel isn't in the Created state as that's really where the problem comes from.
Also, while it's not part of the ICommunicationObject interface (which is explicitly implemented so you must cast to call open), do you think there's any benefit in adding an OpenAsync and CloseAsync method to this class? No cast would be needed, but this class isn't like a regular channel instance anyway so I think that's fine. Just use Task.Factory.FromAsync to wrap the Begin/End methods. Otherwise we're expecting people to either handle APM (I refer you to our teams chat about that) or use the synchronous versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I check to make sure the IRequestChannel is in the Created state.
src/System.ServiceModel.Federation/src/System/ServiceModel/Federation/WSTrustChannel.cs
Outdated
Show resolved
Hide resolved
/// </summary> | ||
/// <param name="endpointConfigurationName">The configuration name used for the endpoint.</param> | ||
public WSTrustChannelFactory(string endpointConfigurationName) | ||
: base(endpointConfigurationName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't support endpoint configuration name as we don't support app.config. Remove this constructor as the base class will just throw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in: ab9b3c1
/// <param name="endpointConfigurationName">The configuration name used for the endpoint.</param> | ||
/// <param name="remoteAddress">The <see cref="EndpointAddress" /> that provides the location of the service.</param> | ||
public WSTrustChannelFactory(string endpointConfigurationName, EndpointAddress remoteAddress) | ||
: base(endpointConfigurationName, remoteAddress) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same for this one, it will just throw so remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in: ab9b3c1
src/System.ServiceModel.Federation/src/System/ServiceModel/Federation/WSTrustChannelFactory.cs
Show resolved
Hide resolved
/// added as an IssuedToken on the outbound WCF message. | ||
/// </summary> | ||
public static class WSTrustUtilities | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All members of this class are internal. I think you intended for the class to be internal too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in: ab9b3c1
/// A <see cref="WSTrustChannelFactory" /> that produces <see cref="WSTrustChannel" /> objects used to communicate with a WS-Trust endpoint. | ||
/// </summary> | ||
[ComVisible(false)] | ||
public class WSTrustChannelFactory : ChannelFactory<IWSTrustChannelContract> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this can probably be removed. It looks like you added it because originally on NetFx this was there.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
addressed in: ab9b3c1
...System.ServiceModel.Federation/src/System/ServiceModel/Federation/IWSTrustChannelContract.cs
Show resolved
Hide resolved
src/System.ServiceModel.Federation/src/System/ServiceModel/Federation/WSTrustChannel.cs
Show resolved
Hide resolved
0a105a9
to
051b6ff
Compare
051b6ff
to
975db6c
Compare
move rest of inline strings in Federation namespace to Strings.resx
cc @safern @joperezr All the other tests in this repo don't actually use the System.ServiceModel.* contracts, instead they compile against System.Private.ServiceModel so they don't need the facades. This test needs the facades, since it's testing the System.ServiceModel.Federation product assembly which compiles against the reference assemblies. I think the solution might be to add references from the test project to the src projects for all the shims used by System.ServiceModel.Federation, and make sure that those don't conflict with the transitive references from the product assembly. The way we solve a similar problem in dotnet/runtime is to flow the reference assemblies across along with the src project reference using |
@ericstj I replaced the reference to S.P.SM project with a reference to System.ServiceModel.Primitives.Facade.csproj and it now compiles and the tests run. I'm seeing an odd problem though. When editing in VS, I get the error: |
Is System.Private.ServiceModel in the SLN? Can you try just adding facade references (in addition to S.P.SM) rather than replacing S.P.SM? That error indicates that the design time build is unable to follow the type-forward, which indicates it didn't have a reference to S.P.SM (or that the typeforward was missing from S.SM.P in design time). I would expect the design time build should see both S.SM.P and S.P.SM with only the S.SM.P reference, but something about your build might be preventing that. You can debug design time builds with information described here: https://github.com/dotnet/project-system/blob/main/docs/design-time-builds.md |
This is similar to what existed in .net framework with some small changes.
WSTrustChannel ctor takes a ChannelFactory rather than a WSTrustChannelFactory.
The new Async model has replaced the Begin / End model found in the original WCF release.
Common methods that were used in WSTrustSecurityTokenProvider and WSTrustChannel were moved to WSTrustUtilities.
An additional constructor was added that takes a separate IRequestChannel.