Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions HawkNet.WCF/HawkAuthenticationManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
using System;
using System.Diagnostics;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.ServiceModel.Channels;
using System.IdentityModel.Claims;
using System.IdentityModel.Policy;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Security;
using System.ServiceModel.Web;
using System.Security;
using System.Web;
using System.Linq;

namespace HawkNet.WCF
{
class HawkAuthenticationManager : ServiceAuthenticationManager
{
const string HawkScheme = "Hawk";

static TraceSource TraceSource = new TraceSource("HawkNet.WCF");

Func<string, HawkCredential> credentials;
int timeskewInSeconds;
string schemeOverride;

public HawkAuthenticationManager(Func<string, HawkCredential> credentials, int timeskewInSeconds, string schemeOverride)
: base()
{
this.credentials = credentials;
this.timeskewInSeconds = timeskewInSeconds;
this.schemeOverride = schemeOverride;

if (Trace.CorrelationManager.ActivityId == Guid.Empty)
Trace.CorrelationManager.ActivityId = Guid.NewGuid();
}

public override ReadOnlyCollection<IAuthorizationPolicy> Authenticate(ReadOnlyCollection<IAuthorizationPolicy> authPolicy, Uri listenUri, ref Message requestMessage)
{
IPrincipal principal = ExtractCredentials(requestMessage);
var policies = new List<IAuthorizationPolicy>();
policies.Add(new HawkPrincipalAuthorizationPolicy());

if (principal != null)
{
requestMessage.Properties["Principal"] = principal;
}

return policies.AsReadOnly();
}

private IPrincipal ExtractCredentials(Message requestMessage)
{
var request = (HttpRequestMessageProperty)requestMessage.Properties[HttpRequestMessageProperty.Name];

var authHeader = request.Headers["Authorization"];
if (authHeader != null && authHeader.StartsWith(HawkScheme, StringComparison.InvariantCultureIgnoreCase))
{
var hawk = authHeader.Substring(HawkScheme.Length).Trim();

TraceSource.TraceInformation(string.Format("{0} - Received Auth header: {1}",
Trace.CorrelationManager.ActivityId, hawk));

var uri = new Uri(HttpUtility.UrlDecode(requestMessage.Properties.Via.AbsoluteUri));

var principal = Hawk.Authenticate(hawk,
request.Headers["host"],
request.Method,
new UriBuilder (uri) { Scheme = (!string.IsNullOrEmpty(schemeOverride)) ? schemeOverride : uri.Scheme }.Uri,
this.credentials,
this.timeskewInSeconds);

return principal;
}
return null;
}
}
}
45 changes: 45 additions & 0 deletions HawkNet.WCF/HawkAuthorizationManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
using System;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.IdentityModel.Claims;
using System.Net;

namespace HawkNet.WCF
{
class HawkAuthorizationManager : ServiceAuthorizationManager
{
public bool SendChallenge { get; set; }

public HawkAuthorizationManager(bool sendChallenge) : base()
{
SendChallenge = sendChallenge;
}

protected override bool CheckAccessCore(OperationContext operationContext)
{
if (!operationContext.ServiceSecurityContext.AuthorizationContext.ClaimSets.Any())
{
if (SendChallenge)
{
var newReply = Message.CreateMessage(MessageVersion.None, null);
var responseProperty = new HttpResponseMessageProperty() { StatusCode = HttpStatusCode.Unauthorized };

var ts = Hawk.ConvertToUnixTimestamp(DateTime.Now).ToString();
var challenge = string.Format("ts=\"{0}\" ntp=\"{1}\"",
ts, "pool.ntp.org");

responseProperty.Headers.Add("WWW-Authenticate", challenge);

newReply.Properties[HttpResponseMessageProperty.Name] = responseProperty;

//overwrite the original reply with the unauthorized message.
operationContext.RequestContext.Reply(newReply);
}
return false;
}

return base.CheckAccessCore(operationContext);
}
}
}
14 changes: 10 additions & 4 deletions HawkNet.WCF/HawkNet.WCF.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="HawkNet, Version=1.4.4.0, Culture=neutral, PublicKeyToken=840f8ba19d15c979, processorArchitecture=MSIL">
<HintPath>..\packages\HawkNet.1.4.4.0\lib\net45\HawkNet.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Microsoft.ServiceModel.Web">
<HintPath>..\packages\Microsoft.ServiceModel.Web.1.0.1\lib\net35\Microsoft.ServiceModel.Web.dll</HintPath>
</Reference>
Expand All @@ -66,13 +62,23 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="HawkAuthenticationManager.cs" />
<Compile Include="HawkAuthorizationManager.cs" />
<Compile Include="HawkClientEndpointBehavior.cs" />
<Compile Include="HawkRequestInterceptor.cs" />
<Compile Include="HawkServiceBehavior.cs" />
<Compile Include="HawkPrincipalAuthorizationPolicy.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HawkNet\HawkNet.csproj">
<Project>{f997ce59-eee3-42e4-b41a-b32985994603}</Project>
<Name>HawkNet</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Expand Down
64 changes: 64 additions & 0 deletions HawkNet.WCF/HawkPrincipalAuthorizationPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IdentityModel.Claims;
using System.IdentityModel.Policy;
using System.ServiceModel;
using System.Security.Principal;

namespace HawkNet.WCF
{
class HawkPrincipalAuthorizationPolicy : IAuthorizationPolicy
{
string id = Guid.NewGuid().ToString();

public ClaimSet Issuer
{
get { return ClaimSet.System; }
}

public string Id
{
get { return this.id; }
}

public bool Evaluate(EvaluationContext evaluationContext, ref object state)
{
object principalValue;

if (OperationContext.Current.IncomingMessageProperties.TryGetValue("Principal", out principalValue))
{
IPrincipal user = principalValue as IPrincipal;
if (user != null)
{
evaluationContext.AddClaimSet(this, new DefaultClaimSet(this.GetClaims(user)));
evaluationContext.Properties["Identities"] = new List<IIdentity>(new IIdentity[] { user.Identity });
evaluationContext.Properties["Principal"] = user;

return true;
}
}
// This lets people get to the help page, and really anything else that isn't restricted by a PrincipalPermission. Is that what we want?
/*
var anonymous = new GenericPrincipal(new GenericIdentity(""), new string[] { });

evaluationContext.AddClaimSet(this, new DefaultClaimSet(new Claim(ClaimTypes.Anonymous, "", Rights.PossessProperty)));
evaluationContext.Properties["Identities"] = new List<IIdentity>(new IIdentity[] { anonymous.Identity });
evaluationContext.Properties["Principal"] = anonymous;

return true;
*/
return false;
}

public virtual IList<Claim> GetClaims(IPrincipal user)
{
IList<Claim> claims = new List<Claim>();
claims.Add(Claim.CreateNameClaim(user.Identity.Name));
return claims;
}

}
}
Loading