diff --git a/OwinWebApiTest/Global.asax b/OwinWebApiTest/Global.asax deleted file mode 100644 index 3a35a99..0000000 --- a/OwinWebApiTest/Global.asax +++ /dev/null @@ -1 +0,0 @@ -<%@ Application Codebehind="Global.asax.cs" Inherits="OwinWebApiTest.WebApiApplication" Language="C#" %> diff --git a/OwinWebApiTest/Global.asax.cs b/OwinWebApiTest/Global.asax.cs deleted file mode 100644 index 0f13cfe..0000000 --- a/OwinWebApiTest/Global.asax.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Web; -using System.Web.Http; -using System.Web.Routing; - -namespace OwinWebApiTest -{ - public class WebApiApplication : System.Web.HttpApplication - { - protected void Application_Start() - { - //GlobalConfiguration.Configure(WebApiConfig.Register); - } - } -} diff --git a/OwinWebApiTest/Models/CasServiceValidationResponse.cs b/OwinWebApiTest/Models/CasServiceValidationResponse.cs new file mode 100644 index 0000000..256cbc8 --- /dev/null +++ b/OwinWebApiTest/Models/CasServiceValidationResponse.cs @@ -0,0 +1,20 @@ +namespace OwinWebApiTest.Models +{ + public class CasServiceValidationResponse + { + public CasServiceValidationSuccess success { get; set; } + public CasServiceValidationFailure failure { get; set; } + } + + public class CasServiceValidationSuccess + { + public string user { get; set; } + public string proxyGrantingTicket { get; set; } + } + + public class CasServiceValidationFailure + { + public string code { get; set; } + public string description { get; set; } + } +} \ No newline at end of file diff --git a/OwinWebApiTest/OwinWebApiTest.csproj b/OwinWebApiTest/OwinWebApiTest.csproj index b16b24c..a2b6256 100644 --- a/OwinWebApiTest/OwinWebApiTest.csproj +++ b/OwinWebApiTest/OwinWebApiTest.csproj @@ -138,22 +138,23 @@ - - + + Designer + - - Global.asax - + + + Web.config diff --git a/OwinWebApiTest/Properties/PublishProfiles/FileSystem.pubxml b/OwinWebApiTest/Properties/PublishProfiles/FileSystem.pubxml new file mode 100644 index 0000000..b406743 --- /dev/null +++ b/OwinWebApiTest/Properties/PublishProfiles/FileSystem.pubxml @@ -0,0 +1,17 @@ + + + + + FileSystem + Release + Any CPU + + True + False + D:\devel\aspnet\OwinWebApiTest.deploy + True + + \ No newline at end of file diff --git a/OwinWebApiTest/Providers/CasAuthorizationServerProvider.cs b/OwinWebApiTest/Providers/CasAuthorizationServerProvider.cs new file mode 100644 index 0000000..962afac --- /dev/null +++ b/OwinWebApiTest/Providers/CasAuthorizationServerProvider.cs @@ -0,0 +1,116 @@ +using Microsoft.Owin.Security.OAuth; +using Microsoft.Owin.Security; +using Microsoft.Owin.Infrastructure; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Configuration; +using System.Net.Http; +using System.Security.Claims; +using System.Threading.Tasks; + +using OwinWebApiTest.Models; + +namespace OwinWebApiTest.Providers +{ + + public class CasAuthorizationServerProvider : OAuthAuthorizationServerProvider + { + private static string casValidationUrl; + private static string serviceUser; + + public CasAuthorizationServerProvider() + { + casValidationUrl = ConfigurationManager.AppSettings["CasHost"] + + ConfigurationManager.AppSettings["CasValidationPath"]; + serviceUser = ConfigurationManager.AppSettings["ServiceUser"]; + } + + public override async Task ValidateClientAuthentication( + OAuthValidateClientAuthenticationContext context) + { + // required but as we're not using client auth just validate & move on... + await Task.FromResult(context.Validated()); + } + + public override async Task GrantResourceOwnerCredentials( + OAuthGrantResourceOwnerCredentialsContext context) + { + dynamic args = await context.Request.ReadFormAsync(); + + if (string.IsNullOrEmpty(args["ticket"]) || string.IsNullOrEmpty(args["service"])) { + context.SetError("invalid_grant", "No CAS ticket or service URL sent."); + context.Rejected(); + return; + } + + var res = await ValidateCasTicket(args["ticket"], args["service"]); + + if (res.success == null && !string.IsNullOrEmpty(serviceUser)) { + res.success = new CasServiceValidationSuccess { user = serviceUser }; + } + + if (res.success == null) { + context.Rejected(); + context.SetError("invalid_grant", "CAS validation failed: " + (res.failure != null + ? res.failure.description : "No response received from the CAS server")); + return; + } + + //var acda = new AccessControlDA(); + //var ac = acda.GetAccessControl(res.success.user); + var ac = new { userId = args["username"], saveAllowed = true, saveAllUnits = true }; + + ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType); + identity.AddClaim(new Claim(ClaimTypes.Name, res.success.user)); + identity.AddClaim(new Claim(ClaimTypes.Role, "User")); + //identity.AddClaim(new Claim("user_name", context.UserName)); + //identity.AddClaim(new Claim("sub", context.UserName)); + + // Identity info will be encoded into an Access ticket as a result of this call: + //context.Validated(identity); + + var props = new AuthenticationProperties(new Dictionary { + { "username", res.success.user }, + { "AccessControl", JsonConvert.SerializeObject(ac) }, + }); + + var ticket = new AuthenticationTicket(identity, props); + context.Validated(ticket); + } + + private async Task ValidateCasTicket(string ticket, string service) + { + var requestUri = WebUtilities.AddQueryString(casValidationUrl, new Dictionary() { + { "service", service }, + { "ticket", ticket }, + { "format", "JSON" }, + }); + + using (HttpClient client = new HttpClient()) + { + return await GetCasServiceValidationAsync(client, requestUri); + } + } + + public async Task GetCasServiceValidationAsync( + HttpClient client, string requestUri) + { + using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, requestUri)) + { + using (HttpResponseMessage response = await client.SendAsync(request)) + { + response.EnsureSuccessStatusCode(); + dynamic resp = await response.Content.ReadAsAsync(); + var success = resp.SelectToken("serviceResponse.authenticationSuccess"); + var failure = resp.SelectToken("serviceResponse.authenticationFailure"); + + return new CasServiceValidationResponse() { + success = success != null ? success.ToObject() : null, + failure = failure != null ? failure.ToObject() : null, + }; + } + } + } + } +} \ No newline at end of file diff --git a/OwinWebApiTest/Providers/SimpleAuthorizationServerProvider.cs b/OwinWebApiTest/Providers/SimpleAuthorizationServerProvider.cs index f1e522b..2d27518 100644 --- a/OwinWebApiTest/Providers/SimpleAuthorizationServerProvider.cs +++ b/OwinWebApiTest/Providers/SimpleAuthorizationServerProvider.cs @@ -8,12 +8,12 @@ namespace OwinWebApiTest.Providers { public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context) { - context.Validated(); + await Task.FromResult(context.Validated()); } public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context) { - context.Validated(new ClaimsIdentity(context.Options.AuthenticationType)); + await Task.FromResult(context.Validated(new ClaimsIdentity(context.Options.AuthenticationType))); } } diff --git a/OwinWebApiTest/Startup.cs b/OwinWebApiTest/Startup.cs index e1f76f0..c999a2e 100644 --- a/OwinWebApiTest/Startup.cs +++ b/OwinWebApiTest/Startup.cs @@ -1,10 +1,12 @@ using System; +using System.Configuration; using System.Threading.Tasks; -using Microsoft.Owin; using Owin; -using System.Web.Http; -using Microsoft.Owin.Security.OAuth; +using Microsoft.Owin; using Microsoft.Owin.Cors; +using Microsoft.Owin.Security.OAuth; +using System.Web.Http; +using System.Net; using OwinWebApiTest.Providers; @@ -18,13 +20,17 @@ namespace OwinWebApiTest { app.UseCors(CorsOptions.AllowAll); - // token generation + double tokenLifetime; + double.TryParse(ConfigurationManager.AppSettings["AccessTokenLifetimeHours"], out tokenLifetime); + + // token configuration app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions { AllowInsecureHttp = true, - TokenEndpointPath = new PathString("/token"), - AccessTokenExpireTimeSpan = TimeSpan.FromHours(8), - Provider = new SimpleAuthorizationServerProvider() + TokenEndpointPath = new PathString("/api/auth/validate"), + AccessTokenExpireTimeSpan = TimeSpan.FromHours(tokenLifetime != 0 ? tokenLifetime : 10), + //Provider = new SimpleAuthorizationServerProvider() + Provider = new CasAuthorizationServerProvider() }); // token consumption @@ -33,6 +39,9 @@ namespace OwinWebApiTest HttpConfiguration config = new HttpConfiguration(); app.UseWebApi(WebApiConfig.Register(config)); + // allow self-signed certificates + ServicePointManager.ServerCertificateValidationCallback += + (sender, cert, chain, sslPolicyErrors) => true; } } } diff --git a/OwinWebApiTest/Web.config b/OwinWebApiTest/Web.config index 67ded2d..d4e2984 100644 --- a/OwinWebApiTest/Web.config +++ b/OwinWebApiTest/Web.config @@ -8,7 +8,16 @@
- + + + + + + + +