From 58de07e5fd14c23dd4c4a315b8c5b3fb3219f66a Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 10 Aug 2022 13:20:10 +0000 Subject: [PATCH] Add support mCaptcha as captcha provider (#20458) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://mcaptcha.org/ Co-authored-by: Felipe Leopoldo Sologuren GutiƩrrez --- custom/conf/app.example.ini | 13 +++- .../doc/advanced/config-cheat-sheet.en-us.md | 7 ++- go.mod | 1 + go.sum | 2 + modules/mcaptcha/mcaptcha.go | 27 ++++++++ modules/setting/service.go | 6 ++ modules/setting/setting.go | 1 + package-lock.json | 63 +++++++++++++++++++ package.json | 1 + routers/web/auth/auth.go | 7 +++ routers/web/auth/linkaccount.go | 9 +++ routers/web/auth/openid.go | 11 ++++ services/forms/user_form.go | 1 + services/forms/user_form_auth_openid.go | 1 + templates/user/auth/signup_inner.tmpl | 8 +++ .../user/auth/signup_openid_register.tmpl | 5 ++ web_src/js/features/mcaptcha.js | 16 +++++ web_src/js/index.js | 2 + web_src/less/_form.less | 3 +- web_src/less/helpers.less | 5 ++ 20 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 modules/mcaptcha/mcaptcha.go create mode 100644 web_src/js/features/mcaptcha.js diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 83b3048bc..ab2289995 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -698,9 +698,11 @@ ROUTER = console ;; Enable captcha validation for registration ;ENABLE_CAPTCHA = false ;; -;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha +;; Type of captcha you want to use. Options: image, recaptcha, hcaptcha, mcaptcha. ;CAPTCHA_TYPE = image ;; +;; Change this to use recaptcha.net or other recaptcha service +;RECAPTCHA_URL = https://www.google.com/recaptcha/ ;; Enable recaptcha to use Google's recaptcha service ;; Go to https://www.google.com/recaptcha/admin to sign up for a key ;RECAPTCHA_SECRET = @@ -710,8 +712,13 @@ ROUTER = console ;HCAPTCHA_SECRET = ;HCAPTCHA_SITEKEY = ;; -;; Change this to use recaptcha.net or other recaptcha service -;RECAPTCHA_URL = https://www.google.com/recaptcha/ +;; Change this to use demo.mcaptcha.org or your self-hosted mcaptcha.org instance. +;MCAPTCHA_URL = https://demo.mcaptcha.org +;; +;; Go to your configured mCaptcha instance and register a sitekey +;; and use your account's secret. +;MCAPTCHA_SECRET = +;MCAPTCHA_SITEKEY = ;; ;; Default value for KeepEmailPrivate ;; Each new user will get the value of this setting copied into their profile diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 6b050e12b..ad971e866 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -579,13 +579,16 @@ Certain queues have defaults that override the defaults set in `[queue]` (this o provided email rather than a generated email. - `ENABLE_CAPTCHA`: **false**: Enable this to use captcha validation for registration. - `REQUIRE_EXTERNAL_REGISTRATION_CAPTCHA`: **false**: Enable this to force captcha validation - even for External Accounts (i.e. GitHub, OpenID Connect, etc). You must `ENABLE_CAPTCHA` also. -- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha\] + even for External Accounts (i.e. GitHub, OpenID Connect, etc). You also must enable `ENABLE_CAPTCHA`. +- `CAPTCHA_TYPE`: **image**: \[image, recaptcha, hcaptcha, mcaptcha\] - `RECAPTCHA_SECRET`: **""**: Go to https://www.google.com/recaptcha/admin to get a secret for recaptcha. - `RECAPTCHA_SITEKEY`: **""**: Go to https://www.google.com/recaptcha/admin to get a sitekey for recaptcha. - `RECAPTCHA_URL`: **https://www.google.com/recaptcha/**: Set the recaptcha url - allows the use of recaptcha net. - `HCAPTCHA_SECRET`: **""**: Sign up at https://www.hcaptcha.com/ to get a secret for hcaptcha. - `HCAPTCHA_SITEKEY`: **""**: Sign up at https://www.hcaptcha.com/ to get a sitekey for hcaptcha. +- `MCAPTCHA_SECRET`: **""**: Go to your mCaptcha instance to get a secret for mCaptcha. +- `MCAPTCHA_SITEKEY`: **""**: Go to your mCaptcha instance to get a sitekey for mCaptcha. +- `MCAPTCHA_URL` **https://demo.mcaptcha.org/**: Set the mCaptcha URL. - `DEFAULT_KEEP_EMAIL_PRIVATE`: **false**: By default set users to keep their email address private. - `DEFAULT_ALLOW_CREATE_ORGANIZATION`: **true**: Allow new users to create organizations by default. - `DEFAULT_USER_IS_RESTRICTED`: **false**: Give new users restricted permissions by default diff --git a/go.mod b/go.mod index 6d41af507..fa6fb911d 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b code.gitea.io/sdk/gitea v0.15.1 + codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222 gitea.com/go-chi/binding v0.0.0-20220309004920-114340dabecb gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20211013065431-70641c1a35d5 diff --git a/go.sum b/go.sum index 124e65a72..7f7ed7fe2 100644 --- a/go.sum +++ b/go.sum @@ -62,6 +62,8 @@ code.gitea.io/gitea-vet v0.2.2-0.20220122151748-48ebc902541b/go.mod h1:zcNbT/aJE code.gitea.io/sdk/gitea v0.11.3/go.mod h1:z3uwDV/b9Ls47NGukYM9XhnHtqPh/J+t40lsUrR6JDY= code.gitea.io/sdk/gitea v0.15.1 h1:WJreC7YYuxbn0UDaPuWIe/mtiNKTvLN8MLkaw71yx/M= code.gitea.io/sdk/gitea v0.15.1/go.mod h1:klY2LVI3s3NChzIk/MzMn7G1FHrfU7qd63iSMVoHRBA= +codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222 h1:PCW4i+gnQ9XxF8V+nBch3KWdGe4MiP3xXUCA/z0jhHk= +codeberg.org/gusted/mcaptcha v0.0.0-20220722211632-55c1ffff1222/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= contrib.go.opencensus.io/exporter/aws v0.0.0-20181029163544-2befc13012d0/go.mod h1:uu1P0UCM/6RbsMrgPa98ll8ZcHM858i/AD06a9aLRCA= contrib.go.opencensus.io/exporter/ocagent v0.5.0/go.mod h1:ImxhfLRpxoYiSq891pBrLVhN+qmP8BTVvdH2YLs7Gl0= contrib.go.opencensus.io/exporter/stackdriver v0.12.1/go.mod h1:iwB6wGarfphGGe/e5CWqyUk/cLzKnWsOKPVW3no6OTw= diff --git a/modules/mcaptcha/mcaptcha.go b/modules/mcaptcha/mcaptcha.go new file mode 100644 index 000000000..b889cf423 --- /dev/null +++ b/modules/mcaptcha/mcaptcha.go @@ -0,0 +1,27 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package mcaptcha + +import ( + "context" + "fmt" + + "code.gitea.io/gitea/modules/setting" + + "codeberg.org/gusted/mcaptcha" +) + +func Verify(ctx context.Context, token string) (bool, error) { + valid, err := mcaptcha.Verify(ctx, &mcaptcha.VerifyOpts{ + InstanceURL: setting.Service.McaptchaURL, + Sitekey: setting.Service.McaptchaSitekey, + Secret: setting.Service.McaptchaSecret, + Token: token, + }) + if err != nil { + return false, fmt.Errorf("wasn't able to verify mCaptcha: %v", err) + } + return valid, nil +} diff --git a/modules/setting/service.go b/modules/setting/service.go index bd97e10b0..af8a72cc6 100644 --- a/modules/setting/service.go +++ b/modules/setting/service.go @@ -47,6 +47,9 @@ var Service = struct { RecaptchaURL string HcaptchaSecret string HcaptchaSitekey string + McaptchaSecret string + McaptchaSitekey string + McaptchaURL string DefaultKeepEmailPrivate bool DefaultAllowCreateOrganization bool DefaultUserIsRestricted bool @@ -133,6 +136,9 @@ func newService() { Service.RecaptchaURL = sec.Key("RECAPTCHA_URL").MustString("https://www.google.com/recaptcha/") Service.HcaptchaSecret = sec.Key("HCAPTCHA_SECRET").MustString("") Service.HcaptchaSitekey = sec.Key("HCAPTCHA_SITEKEY").MustString("") + Service.McaptchaURL = sec.Key("MCAPTCHA_URL").MustString("https://demo.mcaptcha.org/") + Service.McaptchaSecret = sec.Key("MCAPTCHA_SECRET").MustString("") + Service.McaptchaSitekey = sec.Key("MCAPTCHA_SITEKEY").MustString("") Service.DefaultKeepEmailPrivate = sec.Key("DEFAULT_KEEP_EMAIL_PRIVATE").MustBool() Service.DefaultAllowCreateOrganization = sec.Key("DEFAULT_ALLOW_CREATE_ORGANIZATION").MustBool(true) Service.DefaultUserIsRestricted = sec.Key("DEFAULT_USER_IS_RESTRICTED").MustBool(false) diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 465dc75ca..0af743dd9 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -59,6 +59,7 @@ const ( ImageCaptcha = "image" ReCaptcha = "recaptcha" HCaptcha = "hcaptcha" + MCaptcha = "mcaptcha" ) // settings diff --git a/package-lock.json b/package-lock.json index 945193538..aabbd84fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "license": "MIT", "dependencies": { "@claviska/jquery-minicolors": "2.3.6", + "@mcaptcha/vanilla-glue": "0.1.0-alpha-2", "@primer/octicons": "17.4.0", "add-asset-webpack-plugin": "2.0.1", "css-loader": "6.7.1", @@ -1662,6 +1663,55 @@ "jsep": "^0.4.0||^1.0.0" } }, + "node_modules/@mcaptcha/core-glue": { + "version": "0.1.0-alpha-3", + "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-3.tgz", + "integrity": "sha512-avphBVgf3PPDWuUoDsB2qiXAss2pc00lUILswJaMQofr8FQyflzkhha8H2Z+qGFiX0Iib/yyP2TOtBDbHqE9Tg==", + "funding": [ + { + "type": "individual", + "url": "http://mcaptcha.org/donate" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mcaptcha" + }, + { + "type": "individual", + "url": "http://batsense.net/donate" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/realaravinth" + } + ] + }, + "node_modules/@mcaptcha/vanilla-glue": { + "version": "0.1.0-alpha-2", + "resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-2.tgz", + "integrity": "sha512-cQOg3EIhdjk1xoZtjD9SVPwQAnd49FCvHKchwFZZuhdNTeFs7SUHynOCekuGow2Ip0RJZuMZGcRxvWMgd0ogng==", + "funding": [ + { + "type": "individual", + "url": "http://mcaptcha.org/donate" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mcaptcha" + }, + { + "type": "individual", + "url": "http://batsense.net/donate" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/realaravinth" + } + ], + "dependencies": { + "@mcaptcha/core-glue": "^0.1.0-alpha-3" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -14144,6 +14194,19 @@ "dev": true, "requires": {} }, + "@mcaptcha/core-glue": { + "version": "0.1.0-alpha-3", + "resolved": "https://registry.npmjs.org/@mcaptcha/core-glue/-/core-glue-0.1.0-alpha-3.tgz", + "integrity": "sha512-avphBVgf3PPDWuUoDsB2qiXAss2pc00lUILswJaMQofr8FQyflzkhha8H2Z+qGFiX0Iib/yyP2TOtBDbHqE9Tg==" + }, + "@mcaptcha/vanilla-glue": { + "version": "0.1.0-alpha-2", + "resolved": "https://registry.npmjs.org/@mcaptcha/vanilla-glue/-/vanilla-glue-0.1.0-alpha-2.tgz", + "integrity": "sha512-cQOg3EIhdjk1xoZtjD9SVPwQAnd49FCvHKchwFZZuhdNTeFs7SUHynOCekuGow2Ip0RJZuMZGcRxvWMgd0ogng==", + "requires": { + "@mcaptcha/core-glue": "^0.1.0-alpha-3" + } + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", diff --git a/package.json b/package.json index a3e13eb60..42cba24f8 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ }, "dependencies": { "@claviska/jquery-minicolors": "2.3.6", + "@mcaptcha/vanilla-glue": "0.1.0-alpha-2", "@primer/octicons": "17.4.0", "add-asset-webpack-plugin": "2.0.1", "css-loader": "6.7.1", diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 610e4d290..8a4c12d57 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/eventsource" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mcaptcha" "code.gitea.io/gitea/modules/password" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/session" @@ -414,6 +415,8 @@ func SignUp(ctx *context.Context) { ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["PageIsSignUp"] = true // Show Disabled Registration message if DisableRegistration or AllowOnlyExternalRegistration options are true @@ -435,6 +438,8 @@ func SignUpPost(ctx *context.Context) { ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["PageIsSignUp"] = true // Permission denied if DisableRegistration or AllowOnlyExternalRegistration options are true @@ -458,6 +463,8 @@ func SignUpPost(ctx *context.Context) { valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse) case setting.HCaptcha: valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) + case setting.MCaptcha: + valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse) default: ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) return diff --git a/routers/web/auth/linkaccount.go b/routers/web/auth/linkaccount.go index a2d76e9c5..4f3f2062b 100644 --- a/routers/web/auth/linkaccount.go +++ b/routers/web/auth/linkaccount.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mcaptcha" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/session" "code.gitea.io/gitea/modules/setting" @@ -40,6 +41,8 @@ func LinkAccount(ctx *context.Context) { ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["AllowOnlyInternalRegistration"] = setting.Service.AllowOnlyInternalRegistration ctx.Data["ShowRegistrationButton"] = false @@ -96,6 +99,8 @@ func LinkAccountPostSignIn(ctx *context.Context) { ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["ShowRegistrationButton"] = false @@ -195,6 +200,8 @@ func LinkAccountPostRegister(ctx *context.Context) { ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["DisableRegistration"] = setting.Service.DisableRegistration ctx.Data["ShowRegistrationButton"] = false @@ -233,6 +240,8 @@ func LinkAccountPostRegister(ctx *context.Context) { valid, err = recaptcha.Verify(ctx, form.GRecaptchaResponse) case setting.HCaptcha: valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) + case setting.MCaptcha: + valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse) default: ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) return diff --git a/routers/web/auth/openid.go b/routers/web/auth/openid.go index 32ae91da4..3b1065189 100644 --- a/routers/web/auth/openid.go +++ b/routers/web/auth/openid.go @@ -15,6 +15,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/hcaptcha" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/mcaptcha" "code.gitea.io/gitea/modules/recaptcha" "code.gitea.io/gitea/modules/session" "code.gitea.io/gitea/modules/setting" @@ -341,6 +342,8 @@ func RegisterOpenID(ctx *context.Context) { ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey ctx.Data["RecaptchaURL"] = setting.Service.RecaptchaURL + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["OpenID"] = oid userName, _ := ctx.Session.Get("openid_determined_username").(string) if userName != "" { @@ -372,6 +375,8 @@ func RegisterOpenIDPost(ctx *context.Context) { ctx.Data["CaptchaType"] = setting.Service.CaptchaType ctx.Data["RecaptchaSitekey"] = setting.Service.RecaptchaSitekey ctx.Data["HcaptchaSitekey"] = setting.Service.HcaptchaSitekey + ctx.Data["McaptchaSitekey"] = setting.Service.McaptchaSitekey + ctx.Data["McaptchaURL"] = setting.Service.McaptchaURL ctx.Data["OpenID"] = oid if setting.Service.AllowOnlyInternalRegistration { @@ -397,6 +402,12 @@ func RegisterOpenIDPost(ctx *context.Context) { return } valid, err = hcaptcha.Verify(ctx, form.HcaptchaResponse) + case setting.MCaptcha: + if err := ctx.Req.ParseForm(); err != nil { + ctx.ServerError("", err) + return + } + valid, err = mcaptcha.Verify(ctx, form.McaptchaResponse) default: ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) return diff --git a/services/forms/user_form.go b/services/forms/user_form.go index c8f2b02d8..8ce1d85c5 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -96,6 +96,7 @@ type RegisterForm struct { Retype string GRecaptchaResponse string `form:"g-recaptcha-response"` HcaptchaResponse string `form:"h-captcha-response"` + McaptchaResponse string `form:"m-captcha-response"` } // Validate validates the fields diff --git a/services/forms/user_form_auth_openid.go b/services/forms/user_form_auth_openid.go index fd3368d30..992517f34 100644 --- a/services/forms/user_form_auth_openid.go +++ b/services/forms/user_form_auth_openid.go @@ -31,6 +31,7 @@ type SignUpOpenIDForm struct { Email string `binding:"Required;Email;MaxSize(254)"` GRecaptchaResponse string `form:"g-recaptcha-response"` HcaptchaResponse string `form:"h-captcha-response"` + McaptchaResponse string `form:"m-captcha-response"` } // Validate validates the fields diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl index 356c3e1cd..58380f57d 100644 --- a/templates/user/auth/signup_inner.tmpl +++ b/templates/user/auth/signup_inner.tmpl @@ -54,6 +54,14 @@
{{end}} + {{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}} +
+ {{.locale.Tr "captcha"}} +
+
+
+ {{end}} +
diff --git a/templates/user/auth/signup_openid_register.tmpl b/templates/user/auth/signup_openid_register.tmpl index 5edd7966f..9fe0a9de1 100644 --- a/templates/user/auth/signup_openid_register.tmpl +++ b/templates/user/auth/signup_openid_register.tmpl @@ -40,6 +40,11 @@
{{end}} + {{if and .EnableCaptcha (eq .CaptchaType "mcaptcha")}} +
+
+
+ {{end}}
diff --git a/web_src/js/features/mcaptcha.js b/web_src/js/features/mcaptcha.js new file mode 100644 index 000000000..725e2e28a --- /dev/null +++ b/web_src/js/features/mcaptcha.js @@ -0,0 +1,16 @@ +export async function initMcaptcha() { + const mCaptchaEl = document.querySelector('.m-captcha'); + if (!mCaptchaEl) return; + + const {default: mCaptcha} = await import(/* webpackChunkName: "mcaptcha-vanilla-glue" */'@mcaptcha/vanilla-glue'); + mCaptcha.INPUT_NAME = 'm-captcha-response'; + const siteKey = mCaptchaEl.getAttribute('data-sitekey'); + const instanceURL = mCaptchaEl.getAttribute('data-instance-url'); + + mCaptcha.default({ + siteKey: { + instanceUrl: new URL(instanceURL), + key: siteKey, + } + }); +} diff --git a/web_src/js/index.js b/web_src/js/index.js index b96e79c3c..bd56fff77 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -86,6 +86,7 @@ import {initCommonOrganization} from './features/common-organization.js'; import {initRepoWikiForm} from './features/repo-wiki.js'; import {initRepoCommentForm, initRepository} from './features/repo-legacy.js'; import {initFormattingReplacements} from './features/formatting.js'; +import {initMcaptcha} from './features/mcaptcha.js'; // Run time-critical code as soon as possible. This is safe to do because this // script appears at the end of and rendered HTML is accessible at that point. @@ -182,6 +183,7 @@ $(document).ready(() => { initRepository(); initCommitStatuses(); + initMcaptcha(); initUserAuthLinkAccountView(); initUserAuthOauth2(); diff --git a/web_src/less/_form.less b/web_src/less/_form.less index eeab07c47..c95876321 100644 --- a/web_src/less/_form.less +++ b/web_src/less/_form.less @@ -156,7 +156,8 @@ textarea:focus, padding-left: @create-page-form-input-padding+30px; } - .inline.field > label { + .inline.field > label, + .inline.field > span { text-align: right; width: @create-page-form-input-padding; word-wrap: break-word; diff --git a/web_src/less/helpers.less b/web_src/less/helpers.less index cacf7d9c8..5510ee5b2 100644 --- a/web_src/less/helpers.less +++ b/web_src/less/helpers.less @@ -168,3 +168,8 @@ .py-3 { padding-top: .5rem !important; padding-bottom: .5rem !important; } .py-4 { padding-top: 1rem !important; padding-bottom: 1rem !important; } .py-5 { padding-top: 2rem !important; padding-bottom: 2rem !important; } + +@media @mediaSm { + .db-small { display: block !important; } + .w-100-small { width: 100% !important; } +}