2010-03-17 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Channels / HttpListenerManager.cs
index 41ac9d4e576aac44a2975d0c01f21e71a470d989..0ce97088b5b2b4d53fe5e99f48e8402d0022bd82 100644 (file)
@@ -3,8 +3,10 @@
 //
 // Author:
 //     Vladimir Krasnov <vladimirk@mainsoft.com>
+//     Atsushi Enomoto  <atsushi@ximian.com>
 //
 // Copyright (C) 2005-2006 Mainsoft, Inc.  http://www.mainsoft.com
+// Copyright (C) 2009-2010 Novell, Inc.  http://www.novell.com
 //
 // Permission is hereby granted, free of charge, to any person obtaining
 // a copy of this software and associated documentation files (the
 using System;
 using System.Collections.Generic;
 using System.Collections.Specialized;
+using System.IdentityModel.Selectors;
+using System.IdentityModel.Tokens;
 using System.ServiceModel.Description;
+using System.ServiceModel.Security;
+using System.Security.Principal;
 using System.Text;
 using System.Threading;
 using System.Net;
@@ -42,6 +48,10 @@ namespace System.ServiceModel.Channels
                public abstract Uri RequestUrl { get; }
                public abstract string HttpMethod { get; }
                public abstract void Abort ();
+
+               public abstract string User { get; }
+               public abstract string Password { get; }
+               public abstract void ReturnUnauthorized ();
        }
 
        class HttpListenerContextInfo : HttpContextInfo
@@ -70,6 +80,19 @@ namespace System.ServiceModel.Channels
                {
                        ctx.Response.Abort ();
                }
+
+               public override string User {
+                       get { return ctx.User != null ? ((HttpListenerBasicIdentity) ctx.User.Identity).Name : null; }
+               }
+
+               public override string Password {
+                       get { return ctx.User != null ? ((HttpListenerBasicIdentity) ctx.User.Identity).Password : null; }
+               }
+
+               public override void ReturnUnauthorized ()
+               {
+                       ctx.Response.StatusCode = 401;
+               }
        }
 
        class AspNetHttpContextInfo : HttpContextInfo
@@ -99,6 +122,20 @@ namespace System.ServiceModel.Channels
                {
                        ctx.Response.Close ();
                }
+
+               public override string User {
+                       get { return ctx.User != null ? ((GenericIdentity) ctx.User.Identity).Name : null; }
+               }
+
+               // FIXME: how to acquire this?
+               public override string Password {
+                       get { return null; }
+               }
+
+               public override void ReturnUnauthorized ()
+               {
+                       ctx.Response.StatusCode = 401;
+               }
        }
 
        internal class HttpSimpleListenerManager : HttpListenerManager
@@ -111,8 +148,8 @@ namespace System.ServiceModel.Channels
                        opened_listeners = new Dictionary<Uri, HttpListener> ();
                }
 
-               public HttpSimpleListenerManager (IChannelListener channelListener)
-                       : base (channelListener)
+               public HttpSimpleListenerManager (IChannelListener channelListener, HttpTransportBindingElement source, ServiceCredentialsSecurityTokenManager securityTokenManager)
+                       : base (channelListener, source, securityTokenManager)
                {
                }
 
@@ -121,6 +158,11 @@ namespace System.ServiceModel.Channels
                        lock (opened_listeners) {
                                if (!opened_listeners.ContainsKey (channelListener.Uri)) {
                                        HttpListener listener = new HttpListener ();
+                                       listener.AuthenticationSchemeSelectorDelegate = delegate (HttpListenerRequest req) {
+                                               return Source.AuthenticationScheme;
+                                       };
+                                       listener.Realm = Source.Realm;
+                                       listener.UnsafeConnectionNtlmAuthentication = Source.UnsafeConnectionNtlmAuthentication;
 
                                        string uriString = channelListener.Uri.ToString ();
                                        if (!uriString.EndsWith ("/", StringComparison.Ordinal))
@@ -154,7 +196,7 @@ namespace System.ServiceModel.Channels
                        http_listener = null;
                }
 
-               public override void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceivedCallback)
+               protected override void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceivedCallback)
                {
                        http_listener.BeginGetContext (delegate (IAsyncResult result) {
                                var hctx = http_listener.EndGetContext (result);
@@ -167,15 +209,13 @@ namespace System.ServiceModel.Channels
        {
                SvcHttpHandler http_handler;
 
-               public AspNetListenerManager (IChannelListener channelListener)
-                       : base (channelListener)
+               public AspNetListenerManager (IChannelListener channelListener, HttpTransportBindingElement source, ServiceCredentialsSecurityTokenManager securityTokenManager)
+                       : base (channelListener, source, securityTokenManager)
                {
-                       http_handler = SvcHttpHandlerFactory.GetHandlerForListener (channelListener);
+                       http_handler = SvcHttpHandler.Current;
                }
 
-               public SvcHttpHandler Source {
-                       get { return http_handler; }
-               }
+               internal SvcHttpHandler HttpHandler { get { return http_handler; } }
 
                protected override void OnRegister (IChannelListener channelListener, TimeSpan timeout)
                {
@@ -189,7 +229,7 @@ namespace System.ServiceModel.Channels
 
                Func<IChannelListener,HttpContext> wait_delegate;
 
-               public override void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceivedCallback)
+               protected override void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceivedCallback)
                {
                        if (wait_delegate == null)
                                wait_delegate = new Func<IChannelListener,HttpContext> (http_handler.WaitForRequest);
@@ -209,19 +249,29 @@ namespace System.ServiceModel.Channels
                AutoResetEvent wait_http_ctx = new AutoResetEvent (false);
                List<HttpContextInfo> pending = new List<HttpContextInfo> ();
 
-public MetadataPublishingInfo MexInfo { get { return mex_info; } }
+               public MetadataPublishingInfo MexInfo { get { return mex_info; } }
+               public HttpTransportBindingElement Source { get; private set; }
+
+               SecurityTokenAuthenticator security_token_authenticator;
+               SecurityTokenResolver security_token_resolver;
 
                static HttpListenerManager ()
                {
                        registered_channels = new Dictionary<Uri, List<IChannelListener>> ();
                }
 
-               protected HttpListenerManager (IChannelListener channelListener)
+               protected HttpListenerManager (IChannelListener channelListener, HttpTransportBindingElement source, ServiceCredentialsSecurityTokenManager securityTokenManager)
                {
                        this.channel_listener = channelListener;
                        // FIXME: this cast should not be required, but current JIT somehow causes an internal error.
                        mex_info = ((IChannelListener) channelListener).GetProperty<MetadataPublishingInfo> ();
                        wsdl_instance = mex_info != null ? mex_info.Instance : null;
+                       Source = source;
+
+                       if (securityTokenManager != null) {
+                               var str = new SecurityTokenRequirement () { TokenType = SecurityTokenTypes.UserName };
+                               security_token_authenticator = securityTokenManager.CreateSecurityTokenAuthenticator (str, out security_token_resolver);
+                       }
                }
 
                public void Open (TimeSpan timeout)
@@ -265,6 +315,11 @@ public MetadataPublishingInfo MexInfo { get { return mex_info; } }
                protected abstract void OnRegister (IChannelListener listener, TimeSpan timeout);
                protected abstract void OnUnregister (IChannelListener listener, bool abort);
 
+               public void CancelGetHttpContextAsync ()
+               {
+                       wait_http_ctx.Set ();
+               }
+
                // Do not directly handle retrieved HttpListenerContexts when
                // the listener received ones.
                // Instead, iterate every listeners to find the most-likely-
@@ -292,25 +347,45 @@ public MetadataPublishingInfo MexInfo { get { return mex_info; } }
                        }
                }
 
-               public abstract void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceiverCallback);
+               protected abstract void KickContextReceiver (IChannelListener listener, Action<HttpContextInfo> contextReceiverCallback);
 
                void DispatchHttpListenerContext (HttpContextInfo ctx)
                {
                        if (wsdl_instance == null) {
-                               pending.Add (ctx);
-                               wait_http_ctx.Set ();
+                               AddListenerContext (ctx);
                                return;
                        }
                        foreach (var l in registered_channels [channel_listener.Uri]) {
                                var lm = l.GetProperty<HttpListenerManager> ();
                                if (lm.FilterHttpContext (ctx)) {
-                                       lm.pending.Add (ctx);
-                                       lm.wait_http_ctx.Set ();
+                                       lm.AddListenerContext (ctx);
                                        return;
                                }
                        }
-                       pending.Add (ctx);
-                       wait_http_ctx.Set ();
+                       AddListenerContext (ctx);
+               }
+
+               void AddListenerContext (HttpContextInfo ctx)
+               {
+                       if (Source.AuthenticationScheme != AuthenticationSchemes.Anonymous) {
+                               if (security_token_authenticator != null)
+                                       // FIXME: use return value?
+                                       try {
+                                               security_token_authenticator.ValidateToken (new UserNameSecurityToken (ctx.User, ctx.Password));
+                                       } catch (Exception) {
+                                               ctx.ReturnUnauthorized ();
+                                       }
+                               else {
+                                       ctx.ReturnUnauthorized ();
+                               }
+                       }
+
+                       lock (registered_channels) {
+                               pending.Add (ctx);
+                               // FIXME: this should not be required, but it somehow saves some failures wrt concurrent calls.
+                               Thread.Sleep (100);
+                               wait_http_ctx.Set ();
+                       }
                }
 
                const UriComponents cmpflag = UriComponents.HttpRequestUrl ^ UriComponents.Query;