Skip to content

Commit

Permalink
Add ReCaptcha v3
Browse files Browse the repository at this point in the history
  • Loading branch information
guimard committed Oct 24, 2024
1 parent a746fc1 commit 3208d68
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 11 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ env:
VERSION: 2.20.0
PGVERSION: 15
DEBIANRELEASE: bookworm
DOCKERREVISION: 2
DOCKERREVISION: 3

on:
push:
Expand Down
8 changes: 0 additions & 8 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
name: Build and test Docker Images

env:
VERSION: 2.20.0
PGVERSION: 15
DEBIANRELEASE: bookworm
DOCKERREVISION: 2

on:
push:
branches-ignore: [master,stable,experimental]
Expand All @@ -18,5 +12,3 @@ jobs:
uses: actions/checkout@v3
- name: build_and_test
run: ./build-all
env:
VERSION: ${{ env.VERSION }}-${{ env.DOCKERREVISION }}
2 changes: 2 additions & 0 deletions Changes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Changes

* Add ReCaptcha v3

## v2.20.0-2 _(2024-10-22)_
* Fix upstream regression on Nginx handler

Expand Down
2 changes: 1 addition & 1 deletion portal/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ COPY *.patch /

RUN for p in appgrid.patch jwt-type.patch app-scope.patch ignorepollers.patch \
fixedLogout.patch more-logs.patch \
matrix-token.patch redirect-ajax.patch \
matrix-token.patch redirect-ajax.patch recaptcha3.patch \
; do echo patch $p && patch -p1 < $p; done && \
rm -f /*.patch && \
echo "# Install nginx configuration files" && \
Expand Down
119 changes: 119 additions & 0 deletions portal/recaptcha3.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
--- /dev/null
+++ b/usr/share/perl5/Lemonldap/NG/Portal/Captcha/ReCaptcha3.pm
@@ -0,0 +1,100 @@
+package Lemonldap::NG::Portal::Captcha::ReCaptcha3;
+
+use strict;
+use Mouse;
+use Lemonldap::NG::Common::UserAgent;
+
+# Add constants used by this module
+
+our $VERSION = '2.20.0';
+
+extends 'Lemonldap::NG::Portal::Main::Plugin';
+
+has ua => (
+ is => 'rw',
+ lazy => 1,
+ builder => sub {
+ my $ua = Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
+ $ua->env_proxy();
+ return $ua;
+ }
+);
+
+has score => (
+ is => 'ro',
+ default => sub {
+ return $_[0]->{conf}->{captchaOptions}->{score} || 0.5;
+ }
+);
+
+sub init {
+ my ($self) = @_;
+ unless ($self->conf->{captchaOptions}->{dataSiteKey}
+ and $self->conf->{captchaOptions}->{secretKey} )
+ {
+ $self->logger->error('Missing required options for reCaptcha');
+ return 0;
+ }
+ return 1;
+}
+
+sub init_captcha {
+ my ( $self, $req ) = @_;
+
+ $req->data->{customScript} .=
+ '<script src="https://www.google.com/recaptcha/api.js?render='
+ . $self->conf->{captchaOptions}->{dataSiteKey}
+ . '"></script>'
+ . '<script type="application/init">
+{
+"datasitekey": "' . $self->conf->{captchaOptions}->{dataSiteKey} . '"
+}
+</script>'
+ . '<script src="/static/common/js/recaptchav3.js"></script>';
+
+ # Read option from the manager configuration
+ my $dataSiteKey = $self->conf->{captchaOptions}->{dataSiteKey};
+ my $html =
+qq'<div class="g-recaptcha" data-sitekey="$dataSiteKey" data-action="LOGIN">
+<input type="hidden" id="grr" name="g-recaptcha-response" />
+</div>';
+ $req->captchaHtml($html);
+}
+
+sub check_captcha {
+ my ( $self, $req ) = @_;
+
+ my $captcha_input = $req->param('g-recaptcha-response');
+ unless ($captcha_input) {
+ $self->logger->info('No captcha value submitted');
+ return 0;
+ }
+ my $response = $self->ua->post(
+ 'https://www.google.com/recaptcha/api/siteverify',
+ {
+ secret => $self->conf->{captchaOptions}->{secretKey},
+ response => $captcha_input,
+ }
+ );
+ if ( $response->is_success ) {
+ my $res = eval { JSON::from_json( $response->decoded_content ) };
+ if ($@) {
+ $self->logger->error("reCaptcha: $@");
+ return 0;
+ }
+ my $success =
+ $res->{success}
+ and $res->{score}
+ and ( $res->{score} >= $self->score );
+ unless ($success) {
+ $self->logger->info(
+ 'reCaptcha errors:' . $response->decoded_content );
+ }
+ return $success;
+ }
+ $self->logger->error( 'reCaptcha error: ' . $response->status_line );
+ return 0;
+}
+
+1;
+
--- /dev/null
+++ b/usr/share/lemonldap-ng/portal/htdocs/static/common/js/recaptchav3.js
@@ -0,0 +1,13 @@
+(function() {
+ $(document).ready(function() {
+ $('form').on('submit', function(e) {
+ e.preventDefault();
+ grecaptcha.ready(function() {
+ grecaptcha.execute(datas['datasitekey'], {action: 'submit'}).then(function(token) {
+ $('#grr').val(token);
+ $(e.currentTarget).unbind('submit').submit();
+ });
+ })
+ })
+ });
+}).call(this);
2 changes: 1 addition & 1 deletion uwsgi-portal/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ COPY *.patch /

RUN for p in appgrid.patch jwt-type.patch app-scope.patch ignorepollers.patch \
fixedLogout.patch more-logs.patch \
matrix-token.patch redirect-ajax.patch \
matrix-token.patch redirect-ajax.patch recaptcha3.patch \
; do echo patch $p && patch -p1 < $p; done && \
rm -f /*.patch && \
echo "# Install nginx configuration files" && \
Expand Down
119 changes: 119 additions & 0 deletions uwsgi-portal/recaptcha3.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
--- /dev/null
+++ b/usr/share/perl5/Lemonldap/NG/Portal/Captcha/ReCaptcha3.pm
@@ -0,0 +1,100 @@
+package Lemonldap::NG::Portal::Captcha::ReCaptcha3;
+
+use strict;
+use Mouse;
+use Lemonldap::NG::Common::UserAgent;
+
+# Add constants used by this module
+
+our $VERSION = '2.20.0';
+
+extends 'Lemonldap::NG::Portal::Main::Plugin';
+
+has ua => (
+ is => 'rw',
+ lazy => 1,
+ builder => sub {
+ my $ua = Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} );
+ $ua->env_proxy();
+ return $ua;
+ }
+);
+
+has score => (
+ is => 'ro',
+ default => sub {
+ return $_[0]->{conf}->{captchaOptions}->{score} || 0.5;
+ }
+);
+
+sub init {
+ my ($self) = @_;
+ unless ($self->conf->{captchaOptions}->{dataSiteKey}
+ and $self->conf->{captchaOptions}->{secretKey} )
+ {
+ $self->logger->error('Missing required options for reCaptcha');
+ return 0;
+ }
+ return 1;
+}
+
+sub init_captcha {
+ my ( $self, $req ) = @_;
+
+ $req->data->{customScript} .=
+ '<script src="https://www.google.com/recaptcha/api.js?render='
+ . $self->conf->{captchaOptions}->{dataSiteKey}
+ . '"></script>'
+ . '<script type="application/init">
+{
+"datasitekey": "' . $self->conf->{captchaOptions}->{dataSiteKey} . '"
+}
+</script>'
+ . '<script src="/static/common/js/recaptchav3.js"></script>';
+
+ # Read option from the manager configuration
+ my $dataSiteKey = $self->conf->{captchaOptions}->{dataSiteKey};
+ my $html =
+qq'<div class="g-recaptcha" data-sitekey="$dataSiteKey" data-action="LOGIN">
+<input type="hidden" id="grr" name="g-recaptcha-response" />
+</div>';
+ $req->captchaHtml($html);
+}
+
+sub check_captcha {
+ my ( $self, $req ) = @_;
+
+ my $captcha_input = $req->param('g-recaptcha-response');
+ unless ($captcha_input) {
+ $self->logger->info('No captcha value submitted');
+ return 0;
+ }
+ my $response = $self->ua->post(
+ 'https://www.google.com/recaptcha/api/siteverify',
+ {
+ secret => $self->conf->{captchaOptions}->{secretKey},
+ response => $captcha_input,
+ }
+ );
+ if ( $response->is_success ) {
+ my $res = eval { JSON::from_json( $response->decoded_content ) };
+ if ($@) {
+ $self->logger->error("reCaptcha: $@");
+ return 0;
+ }
+ my $success =
+ $res->{success}
+ and $res->{score}
+ and ( $res->{score} >= $self->score );
+ unless ($success) {
+ $self->logger->info(
+ 'reCaptcha errors:' . $response->decoded_content );
+ }
+ return $success;
+ }
+ $self->logger->error( 'reCaptcha error: ' . $response->status_line );
+ return 0;
+}
+
+1;
+
--- /dev/null
+++ b/usr/share/lemonldap-ng/portal/htdocs/static/common/js/recaptchav3.js
@@ -0,0 +1,13 @@
+(function() {
+ $(document).ready(function() {
+ $('form').on('submit', function(e) {
+ e.preventDefault();
+ grecaptcha.ready(function() {
+ grecaptcha.execute(datas['datasitekey'], {action: 'submit'}).then(function(token) {
+ $('#grr').val(token);
+ $(e.currentTarget).unbind('submit').submit();
+ });
+ })
+ })
+ });
+}).call(this);

0 comments on commit 3208d68

Please sign in to comment.