Skip to content

Commit 08663b0

Browse files
committed
New security option: Use invisible reCAPTCHA
1 parent 7b44b30 commit 08663b0

File tree

8 files changed

+69
-17
lines changed

8 files changed

+69
-17
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* #1144 Enable multi server search index
77
* Made Topic ACL enabled
88
* Implemented paging & filtering for Topic grid
9+
* New security option: Use invisible reCAPTCHA
910
* **BeezUp**:
1011
* #1459 Add option to only submit one category name per product
1112
* Allow to specify export categories per product

src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,12 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder)
321321
builder.AddOrUpdate("Common.DisplayOrder.Hint",
322322
"Specifies display order. 1 represents the top of the list.",
323323
"Legt die Anzeige-Priorität fest. 1 steht bspw. für das erste Element in der Liste.");
324-
}
324+
325+
builder.AddOrUpdate("Admin.Configuration.Settings.GeneralCommon.UseInvisibleReCaptcha",
326+
"Use invisible reCAPTCHA",
327+
"Unsichtbaren reCAPTCHA verwenden",
328+
"Does not require the user to click on a checkbox, instead it is invoked directly when the user submits a form. By default only the most suspicious traffic will be prompted to solve a captcha.",
329+
"Der Benutzer muss nicht auf ein Kontrollkästchen klicken, sondern die Validierung erfolgt direkt beim Absenden eines Formulars. Nur bei 'verdächtigem' Traffic wird der Benutzer aufgefordert, ein Captcha zu lösen.");
330+
}
325331
}
326332
}

src/Presentation/SmartStore.Web.Framework/Security/Captcha/CaptchaSettings.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public class CaptchaSettings : ISettings
1717
public bool ShowOnProductReviewPage { get; set; }
1818
public string ReCaptchaPublicKey { get; set; }
1919
public string ReCaptchaPrivateKey { get; set; }
20-
}
20+
public bool UseInvisibleReCaptcha { get; set; }
21+
}
2122
}
Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Text;
2+
using System.Web;
23
using System.Web.Mvc;
34
using SmartStore.Core;
45
using SmartStore.Core.Infrastructure;
@@ -8,30 +9,34 @@ namespace SmartStore.Web.Framework.Security
89
{
910
public static class HtmlCaptchaExtensions
1011
{
11-
public static string GenerateCaptcha(this HtmlHelper helper)
12+
public static IHtmlString GenerateCaptcha(this HtmlHelper helper)
1213
{
13-
var sb = new StringBuilder();
1414
var captchaSettings = EngineContext.Current.Resolve<CaptchaSettings>();
1515
var workContext = EngineContext.Current.Resolve<IWorkContext>();
1616
var widgetUrl = CommonHelper.GetAppSetting<string>("g:RecaptchaWidgetUrl");
17-
var elementId = "GoogleRecaptchaWidget";
17+
var ident = CommonHelper.GenerateRandomDigitCode(5).ToString();
18+
var elementId = "recaptcha" + ident;
19+
var siteKey = captchaSettings.ReCaptchaPublicKey;
20+
var callbackName = "recaptchaOnload" + ident;
1821

19-
var url = "{0}?onload=googleRecaptchaOnloadCallback&render=explicit&hl={1}".FormatInvariant(
22+
var url = "{0}?onload={1}&render=explicit&hl={2}".FormatInvariant(
2023
widgetUrl,
24+
callbackName,
2125
workContext.WorkingLanguage.UniqueSeoCode.EmptyNull().ToLower()
2226
);
2327

24-
sb.AppendLine("<script type=\"text/javascript\">");
25-
sb.AppendLine("var googleRecaptchaOnloadCallback = function() {");
26-
sb.AppendLine(" grecaptcha.render('{0}', {{".FormatInvariant(elementId));
27-
sb.AppendLine(" 'sitekey' : '{0}'".FormatInvariant(captchaSettings.ReCaptchaPublicKey));
28-
sb.AppendLine(" });");
29-
sb.AppendLine("};");
30-
sb.AppendLine("</script>");
31-
sb.AppendLine("<div id=\"{0}\"></div>".FormatInvariant(elementId));
32-
sb.AppendLine("<script src=\"{0}\" async defer></script>".FormatInvariant(url));
28+
var script = new[]
29+
{
30+
"<script>",
31+
" var {0} = function() {{".FormatInvariant(callbackName),
32+
" renderGoogleRecaptcha('{0}', '{1}', {2});".FormatInvariant(elementId, siteKey, captchaSettings.UseInvisibleReCaptcha.ToString().ToLower()),
33+
" };",
34+
"</script>",
35+
"<div id='{0}' class='g-recaptcha'></div>".FormatInvariant(elementId, siteKey),
36+
"<script src='{0}' async defer></script>".FormatInvariant(url),
37+
}.StrJoin("");
3338

34-
return sb.ToString();
39+
return MvcHtmlString.Create(script);
3540
}
3641
}
3742
}

src/Presentation/SmartStore.Web/Administration/Models/Settings/GeneralCommonSettingsModel.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ public partial class CaptchaSettingsModel
155155
[SmartResourceDisplayName("Admin.Configuration.Settings.GeneralCommon.reCaptchaPrivateKey")]
156156
[AllowHtml]
157157
public string ReCaptchaPrivateKey { get; set; }
158+
159+
[SmartResourceDisplayName("Admin.Configuration.Settings.GeneralCommon.UseInvisibleReCaptcha")]
160+
public bool UseInvisibleReCaptcha { get; set; }
158161
}
159162

160163
public partial class PdfSettingsModel

src/Presentation/SmartStore.Web/Administration/Views/Setting/GeneralCommon.cshtml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,15 @@
296296
</td>
297297
</tr>
298298
<tbody id="pnlCaptchaSettings">
299+
<tr>
300+
<td class="adminTitle">
301+
@Html.SmartLabelFor(model => model.CaptchaSettings.UseInvisibleReCaptcha)
302+
</td>
303+
<td class="adminData">
304+
@Html.SettingEditorFor(model => model.CaptchaSettings.UseInvisibleReCaptcha)
305+
@Html.ValidationMessageFor(model => model.CaptchaSettings.UseInvisibleReCaptcha)
306+
</td>
307+
</tr>
299308
<tr>
300309
<td class="adminTitle">
301310
@Html.SmartLabelFor(model => model.CaptchaSettings.ShowOnLoginPage)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
<%@ Application Codebehind="Global.asax.cs" Inherits="SmartStore.Web.MvcApplication" Language="C#" %>
1+
<%@ Application Codebehind="Global.asax.cs" Inherits="SmartStore.Web.MvcApplication" Language="C#" %>

src/Presentation/SmartStore.Web/Scripts/smartstore.common.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,33 @@
282282
};
283283
}
284284

285+
window.renderGoogleRecaptcha = function (containerId, sitekey, invisible) {
286+
var frm = $('#' + containerId).closest('form');
287+
288+
if (frm.length === 0)
289+
return;
290+
291+
var holderId = grecaptcha.render(containerId, {
292+
sitekey: sitekey,
293+
size: invisible ? 'invisible' : undefined,
294+
badge: 'bottomleft',
295+
callback: function (token) {
296+
if (invisible && frm) {
297+
frm[0].submit();
298+
}
299+
}
300+
});
301+
302+
if (invisible) {
303+
frm.on('submit', function (e) {
304+
if ($.validator === undefined || frm.valid() == true) {
305+
e.preventDefault();
306+
grecaptcha.execute(holderId);
307+
}
308+
});
309+
}
310+
}
311+
285312
// on document ready
286313
$(function () {
287314
var rtl = SmartStore.globalization.culture.isRTL;

0 commit comments

Comments
 (0)