<commit_before>﻿/*
 * Authors:
 *   钟峰(<NAME>) <<EMAIL>>
 *
 * Copyright (C) 2016 Zongsoft Corporation <http://www.zongsoft.com>
 *
 * This file is part of Zongsoft.Web.
 *
 * Zongsoft.Web is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * Zongsoft.Web is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Zongsoft.Web; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

using System;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http.Filters;

using Zongsoft.Security;
using Zongsoft.Security.Membership;

namespace Zongsoft.Web.Http.Security
{
	public class AuthenticationFilter : IAuthenticationFilter
	{
		#region 常量定义
		private const string HTTP_AUTHORIZATION_SCHEME = "Credential";
		#endregion

		#region 成员字段
		private ICredentialProvider _credentialProvider;

		public bool AllowMultiple
		{
			get
			{
				throw new NotImplementedException();
			}
		}
		#endregion

		#region 公共属性
		[Zongsoft.Services.ServiceDependency]
		public ICredentialProvider CredentialProvider
		{
			get
			{
				if(_credentialProvider == null)
					_credentialProvider = Zongsoft.ComponentModel.ApplicationContextBase.Current.ServiceFactory.Default.Resolve<ICredentialProvider>();

				return _credentialProvider;
			}
			set
			{
				if(value == null)
					throw new ArgumentNullException();

				_credentialProvider = value;
			}
		}
		#endregion

		#region 验证实现
		public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
		{
			if(context.Request.Headers.Authorization == null || !string.Equals(context.Request.Headers.Authorization.Scheme, HTTP_AUTHORIZATION_SCHEME, StringComparison.OrdinalIgnoreCase))
				return;

			var credentialId = GetCredentialId(context.Request.Headers.Authorization.Parameter);

			if(string.IsNullOrWhiteSpace(credentialId))
				context.Principal = CredentialPrincipal.Empty;
			else
				context.Principal = new CredentialPrincipal(new CredentialIdentity(credentialId, this.CredentialProvider));
		}

		public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
		{
			var principal = context.ActionContext.RequestContext.Principal;

			if(AuthenticationUtility.IsAuthenticated(principal) || AuthenticationUtility.GetAuthorizationMode(context.ActionContext.ActionDescriptor) == AuthorizationMode.Anonymous)<commit_msg>feat: 为Web API验证增加对Cookie作为凭证载体的支持。 :cookie:<commit_after>﻿/*
 * Authors:
 *   钟峰(<NAME>) <<EMAIL>>
 *
 * Copyright (C) 2016 Zongsoft Corporation <http://www.zongsoft.com>
 *
 * This file is part of Zongsoft.Web.
 *
 * Zongsoft.Web is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * Zongsoft.Web is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Zongsoft.Web; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Http;
using System.Web.Http.Filters;

using Zongsoft.Security;
using Zongsoft.Security.Membership;

namespace Zongsoft.Web.Http.Security
{
	public class AuthenticationFilter : IAuthenticationFilter
	{
		#region 常量定义
		private const string HTTP_AUTHORIZATION_SCHEME = "Credential";
		#endregion

		#region 成员字段
		private ICredentialProvider _credentialProvider;

		public bool AllowMultiple
		{
			get
			{
				throw new NotImplementedException();
			}
		}
		#endregion

		#region 公共属性
		[Zongsoft.Services.ServiceDependency]
		public ICredentialProvider CredentialProvider
		{
			get
			{
				if(_credentialProvider == null)
					_credentialProvider = Zongsoft.ComponentModel.ApplicationContextBase.Current.ServiceFactory.Default.Resolve<ICredentialProvider>();

				return _credentialProvider;
			}
			set
			{
				if(value == null)
					throw new ArgumentNullException();

				_credentialProvider = value;
			}
		}
		#endregion

		#region 验证实现
		public async Task AuthenticateAsync(HttpAuthenticationContext context, CancellationToken cancellationToken)
		{
			string credentialId = null;

			//优先从HTTP的Authorization头获取凭证编号，如果没有获取成功则从请求的Cookie中获取
			if(context.Request.Headers.Authorization != null && string.Equals(context.Request.Headers.Authorization.Scheme, HTTP_AUTHORIZATION_SCHEME, StringComparison.OrdinalIgnoreCase))
			{
				credentialId = GetCredentialId(context.Request.Headers.Authorization.Parameter);
			}
			else
			{
				var cookie = context.Request.Headers.GetCookies(Zongsoft.Web.Security.AuthenticationUtility.CredentialKey).FirstOrDefault();

				if(cookie != null)
					credentialId = cookie[Zongsoft.Web.Security.AuthenticationUtility.CredentialKey].Value;
			}

			if(string.IsNullOrWhiteSpace(credentialId))
				context.Principal = CredentialPrincipal.Empty;
			else
				context.Principal = new CredentialPrincipal(new CredentialIdentity(credentialId, this.CredentialProvider));
		}

		public async Task ChallengeAsync(HttpAuthenticationChallengeContext context, CancellationToken cancellationToken)
		{
			var principal = context.ActionContext.RequestContext.Principal;

			if(AuthenticationUtility.IsAuthenticated(principal) || AuthenticationUtility.GetAuthorizationMode(context.ActionContext.ActionDescriptor) == AuthorizationMode.Anonymous)
{'fraction_non_alphanumeric': 0.07649854361490112, 'fraction_numerical': 0.005212325617047371, 'mean_word_length': 4.663194444444445, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2354494', 'n_tokens_mistral': 2045, 'n_tokens_neox': 1861, 'n_words': 516}