183 lines
7.7 KiB
C#
183 lines
7.7 KiB
C#
using Microsoft.AspNetCore.Html;
|
|
using Microsoft.AspNetCore.Mvc.Rendering;
|
|
using Nop.Core;
|
|
using Nop.Core.Domain.Security;
|
|
using Nop.Core.Infrastructure;
|
|
using Nop.Services.Localization;
|
|
using Nop.Services.Security;
|
|
using Nop.Web.Framework.Extensions;
|
|
|
|
namespace Nop.Web.Framework.Security.Captcha;
|
|
|
|
/// <summary>
|
|
/// HTML extensions
|
|
/// </summary>
|
|
public static class HtmlExtensions
|
|
{
|
|
#region Utilities
|
|
|
|
/// <summary>
|
|
/// Get the reCAPTCHA language
|
|
/// </summary>
|
|
/// <param name="captchaSettings">Captcha settings</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the language code
|
|
/// </returns>
|
|
private static async Task<string> GetReCaptchaLanguageAsync(CaptchaSettings captchaSettings)
|
|
{
|
|
var language = (captchaSettings.ReCaptchaDefaultLanguage ?? string.Empty).ToLowerInvariant();
|
|
if (captchaSettings.AutomaticallyChooseLanguage)
|
|
{
|
|
//this list got from this site: https://developers.google.com/recaptcha/docs/language
|
|
//but we use languages only with two letters in the code
|
|
var supportedLanguageCodes = new List<string> { "af", "am", "ar", "az", "bg", "bn", "ca", "cs", "da", "de", "el", "en", "es", "et", "eu", "fa", "fi", "fil", "fr", "gl", "gu", "hi", "hr", "hu", "hy", "id", "is", "it", "iw", "ja", "ka", "kn", "ko", "lo", "lt", "lv", "ml", "mn", "mr", "ms", "nl", "no", "pl", "pt", "ro", "ru", "si", "sk", "sl", "sr", "sv", "sw", "ta", "te", "th", "tr", "uk", "ur", "vi", "zu" };
|
|
|
|
var languageService = EngineContext.Current.Resolve<ILanguageService>();
|
|
var workContext = EngineContext.Current.Resolve<IWorkContext>();
|
|
|
|
var currentLanguage = await workContext.GetWorkingLanguageAsync();
|
|
var twoLetterIsoCode = currentLanguage != null
|
|
? languageService.GetTwoLetterIsoLanguageName(currentLanguage).ToLowerInvariant()
|
|
: string.Empty;
|
|
|
|
language = supportedLanguageCodes.Contains(twoLetterIsoCode) ? twoLetterIsoCode : language;
|
|
}
|
|
|
|
return language;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate API script tag
|
|
/// </summary>
|
|
/// <param name="captchaSettings">Captcha settings</param>
|
|
/// <param name="captchaId">Captcha ID</param>
|
|
/// <param name="render">Render</param>
|
|
/// <param name="language">Language</param>
|
|
/// <returns>Script tag</returns>
|
|
private static TagBuilder GenerateLoadApiScriptTag(CaptchaSettings captchaSettings, string captchaId, string render, string language)
|
|
{
|
|
var hl = !string.IsNullOrEmpty(language)
|
|
? $"&hl={language}"
|
|
: string.Empty;
|
|
var url = string.Format($"{captchaSettings.ReCaptchaApiUrl}{NopSecurityDefaults.RecaptchaScriptPath}", captchaId, render, hl);
|
|
var scriptLoadApiTag = new TagBuilder("script") { TagRenderMode = TagRenderMode.Normal };
|
|
scriptLoadApiTag.Attributes.Add("src", url);
|
|
scriptLoadApiTag.Attributes.Add("async", null);
|
|
scriptLoadApiTag.Attributes.Add("defer", null);
|
|
|
|
return scriptLoadApiTag;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
/// <summary>
|
|
/// Generate reCAPTCHA Control
|
|
/// </summary>
|
|
/// <param name="helper">HTML helper</param>
|
|
/// <param name="captchaSettings">Captcha settings</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the result
|
|
/// </returns>
|
|
public static async Task<IHtmlContent> GenerateCheckBoxReCaptchaV2Async(this IHtmlHelper helper, CaptchaSettings captchaSettings)
|
|
{
|
|
//prepare language
|
|
var language = await GetReCaptchaLanguageAsync(captchaSettings);
|
|
|
|
//prepare theme
|
|
var theme = (captchaSettings.ReCaptchaTheme ?? string.Empty).ToLowerInvariant();
|
|
theme = theme switch
|
|
{
|
|
"blackglass" or "dark" => "dark",
|
|
"clean" or "red" or "white" or "light" => "light",
|
|
_ => "light",
|
|
};
|
|
|
|
//prepare identifier
|
|
var id = $"captcha_{CommonHelper.GenerateRandomInteger()}";
|
|
|
|
//prepare public key
|
|
var publicKey = captchaSettings.ReCaptchaPublicKey ?? string.Empty;
|
|
|
|
//generate reCAPTCHA Control
|
|
var scriptCallbackTag = new TagBuilder("script") { TagRenderMode = TagRenderMode.Normal };
|
|
scriptCallbackTag.InnerHtml
|
|
.AppendHtml($"var onloadCallback{id} = function() {{grecaptcha.render('{id}', {{'sitekey' : '{publicKey}', 'theme' : '{theme}' }});}};");
|
|
|
|
var captchaTag = new TagBuilder("div") { TagRenderMode = TagRenderMode.Normal };
|
|
captchaTag.Attributes.Add("id", id);
|
|
|
|
var scriptLoadApiTag = GenerateLoadApiScriptTag(captchaSettings, id, "explicit", language);
|
|
|
|
return new HtmlString(await scriptCallbackTag.RenderHtmlContentAsync() + await captchaTag.RenderHtmlContentAsync() + await scriptLoadApiTag.RenderHtmlContentAsync());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generate reCAPTCHA v3 Control
|
|
/// </summary>
|
|
/// <param name="helper">HTML helper</param>
|
|
/// <param name="captchaSettings">Captcha settings</param>
|
|
/// <param name="actionName">Action name</param>
|
|
/// <returns>
|
|
/// A task that represents the asynchronous operation
|
|
/// The task result contains the result
|
|
/// </returns>
|
|
public static async Task<IHtmlContent> GenerateReCaptchaV3Async(this IHtmlHelper helper, CaptchaSettings captchaSettings, string actionName = null)
|
|
{
|
|
//prepare language
|
|
var language = await GetReCaptchaLanguageAsync(captchaSettings);
|
|
|
|
//prepare identifier
|
|
var id = $"captcha_{CommonHelper.GenerateRandomInteger()}";
|
|
|
|
//prepare public key
|
|
var publicKey = captchaSettings.ReCaptchaPublicKey ?? string.Empty;
|
|
|
|
//prepare reCAPTCHA script
|
|
if (string.IsNullOrEmpty(actionName))
|
|
actionName = helper.ViewContext.RouteData.Values["action"].ToString();
|
|
|
|
var scriptCallback = $@"
|
|
var onloadCallback{id} = function() {{
|
|
var form = $('input[id=""g-recaptcha-response_{id}""]').closest('form');
|
|
var btn = $(form.find(':submit')[0]);
|
|
|
|
var actionBtn = btn.data('action');
|
|
if (actionBtn == null) {{
|
|
actionBtn = '{actionName}';
|
|
}}
|
|
|
|
var loaded = false;
|
|
var isBusy = false;
|
|
btn.on('click', function (e) {{
|
|
if (!isBusy) {{
|
|
isBusy = true;
|
|
grecaptcha.execute('{publicKey}', {{ 'action': actionBtn }}).then(function(token) {{
|
|
$('#g-recaptcha-response_{id}', form).val(token);
|
|
loaded = true;
|
|
btn.trigger('click');
|
|
}});
|
|
}}
|
|
return loaded;
|
|
}});
|
|
}}
|
|
";
|
|
var scriptCallbackTag = new TagBuilder("script") { TagRenderMode = TagRenderMode.Normal };
|
|
scriptCallbackTag.InnerHtml.AppendHtml(scriptCallback);
|
|
|
|
//prepare reCAPTCHA token input
|
|
var captchaTokenInput = new TagBuilder("input") { TagRenderMode = TagRenderMode.Normal };
|
|
captchaTokenInput.Attributes.Add("type", "hidden");
|
|
captchaTokenInput.Attributes.Add("id", $"g-recaptcha-response_{id}");
|
|
captchaTokenInput.Attributes.Add("name", "g-recaptcha-response");
|
|
|
|
var scriptLoadApiTag = GenerateLoadApiScriptTag(captchaSettings, id, publicKey, language);
|
|
|
|
return new HtmlString(await captchaTokenInput.RenderHtmlContentAsync() + await scriptCallbackTag.RenderHtmlContentAsync() + await scriptLoadApiTag.RenderHtmlContentAsync());
|
|
}
|
|
|
|
#endregion
|
|
} |