Bug 31378: Add Koha::Auth::Client* modules
Signed-off-by: Lukasz Koszyk <lukasz.koszyk@kit.edu> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io> Signed-off-by: Nick Clemens <nick@bywatersolutions.com> Signed-off-by: Martin Renvoize <martin.renvoize@ptfs-europe.com> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
parent
5a84778724
commit
653cbeac52
2 changed files with 316 additions and 0 deletions
198
Koha/Auth/Client.pm
Normal file
198
Koha/Auth/Client.pm
Normal file
|
@ -0,0 +1,198 @@
|
|||
package Koha::Auth::Client;
|
||||
|
||||
# Copyright Theke Solutions 2022
|
||||
#
|
||||
# This file is part of Koha.
|
||||
#
|
||||
# Koha is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Koha 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use Koha::Exceptions::Auth;
|
||||
use Koha::Auth::Providers;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Auth::Client - Base Koha auth client
|
||||
|
||||
=head1 API
|
||||
|
||||
=head2 Methods
|
||||
|
||||
=head3 new
|
||||
|
||||
my $auth_client = Koha::Auth::Client->new();
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my ($class) = @_;
|
||||
my $self = {};
|
||||
|
||||
bless( $self, $class );
|
||||
}
|
||||
|
||||
=head3 get_user
|
||||
|
||||
$auth_client->get_user($provider, $data)
|
||||
|
||||
Get user data according to provider's mapping configuration
|
||||
|
||||
=cut
|
||||
|
||||
sub get_user {
|
||||
my ( $self, $params ) = @_;
|
||||
my $provider_code = $params->{provider};
|
||||
my $data = $params->{data};
|
||||
my $interface = $params->{interface};
|
||||
my $config = $params->{config};
|
||||
|
||||
my $provider = Koha::Auth::Providers->search({ code => $provider_code })->next;
|
||||
|
||||
my ( $mapped_data, $patron ) = $self->_get_data_and_patron({ provider => $provider, data => $data, config => $config });
|
||||
|
||||
if ($mapped_data) {
|
||||
my $domain = $self->has_valid_domain_config({ provider => $provider, email => $mapped_data->{email}, interface => $interface});
|
||||
|
||||
$mapped_data->{categorycode} = $domain->default_category_id;
|
||||
$mapped_data->{branchcode} = $domain->default_library_id;
|
||||
|
||||
return ( $patron, $mapped_data, $domain );
|
||||
}
|
||||
}
|
||||
|
||||
=head3 get_valid_domain_config
|
||||
|
||||
my $domain = Koha::Auth::Client->get_valid_domain_config(
|
||||
{ provider => $provider,
|
||||
email => $user_email,
|
||||
interface => $interface
|
||||
}
|
||||
);
|
||||
|
||||
Gets the best suited valid domain configuration for the given provider.
|
||||
|
||||
=cut
|
||||
|
||||
sub get_valid_domain_config {
|
||||
# FIXME: Should be a hashref param
|
||||
my ( $self, $params ) = @_;
|
||||
my $provider = $params->{provider};
|
||||
my $user_email = $params->{email};
|
||||
my $interface = $params->{interface};
|
||||
|
||||
my $domains = $provider->domains;
|
||||
my $pattern = '@';
|
||||
my $allow = "allow_$interface";
|
||||
my @subdomain_matches;
|
||||
my $default_match;
|
||||
|
||||
while ( my $domain = $domains->next ) {
|
||||
next unless $domain->$allow;
|
||||
|
||||
my $domain_text = $domain->domain;
|
||||
unless ( defined $domain_text && $domain_text ne '') {
|
||||
$default_match = $domain;
|
||||
next;
|
||||
}
|
||||
my ( $asterisk, $domain_name ) = ( $domain_text =~ /^(\*)?(.+)$/ );
|
||||
if ( $asterisk eq '*' ) {
|
||||
$pattern .= '.*';
|
||||
}
|
||||
$domain_name =~ s/\./\\\./g;
|
||||
$pattern .= $domain_name . '$';
|
||||
if ( $user_email =~ /$pattern/ ) {
|
||||
if ( $asterisk eq '*' ) {
|
||||
push @subdomain_matches, { domain => $domain, match_length => length $domain_name };
|
||||
} else {
|
||||
|
||||
# Perfect match.. return this one.
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( @subdomain_matches ) {
|
||||
@subdomain_matches = sort { $b->{match_length} <=> $a->{match_length} } @subdomain_matches
|
||||
unless scalar @subdomain_matches == 1;
|
||||
return $subdomain_matches[0]->{domain};
|
||||
}
|
||||
|
||||
return $default_match || 0;
|
||||
}
|
||||
|
||||
=head3 has_valid_domain_config
|
||||
|
||||
my $has_valid_domain = Koha::Auth::Client->has_valid_domain_config(
|
||||
{ provider => $provider,
|
||||
email => $user_email,
|
||||
interface => $interface
|
||||
}
|
||||
);
|
||||
|
||||
Checks if provider has a valid domain for user email. If has, returns that domain.
|
||||
|
||||
=cut
|
||||
|
||||
sub has_valid_domain_config {
|
||||
# FIXME: Should be a hashref param
|
||||
my ( $self, $params ) = @_;
|
||||
my $domain = $self->get_valid_domain_config( $params );
|
||||
|
||||
Koha::Exceptions::Auth::NoValidDomain->throw( code => 401 )
|
||||
unless $domain;
|
||||
|
||||
return $domain;
|
||||
}
|
||||
|
||||
=head3 _get_data_and_patron
|
||||
|
||||
my $mapping = $auth_client->_get_data_and_patron(
|
||||
{ provider => $provider,
|
||||
data => $data,
|
||||
config => $config
|
||||
}
|
||||
);
|
||||
|
||||
Generic method that maps raw data to patron schema, and returns a patron if it can.
|
||||
|
||||
Note: this only returns an empty I<hashref>. Each class should have its
|
||||
own mapping returned.
|
||||
|
||||
=cut
|
||||
|
||||
sub _get_data_and_patron {
|
||||
return {};
|
||||
}
|
||||
|
||||
=head3 _tranverse_hash
|
||||
|
||||
my $value = $auth_client->_tranverse_hash( { base => $base_hash, keys => $key_string } );
|
||||
|
||||
Get deep nested value in a hash.
|
||||
|
||||
=cut
|
||||
|
||||
sub _tranverse_hash {
|
||||
my ($self, $params) = @_;
|
||||
my $base = $params->{base};
|
||||
my $keys = $params->{keys};
|
||||
my ($key, $rest) = ($keys =~ /^([^.]+)(?:\.(.*))?/);
|
||||
return unless defined $key;
|
||||
my $value = ref $base eq 'HASH' ? $base->{$key} : $base->[$key];
|
||||
return $value unless $rest;
|
||||
return $self->_tranverse_hash({ base => $value, keys => $rest });
|
||||
}
|
||||
|
||||
1;
|
118
Koha/Auth/Client/OAuth.pm
Normal file
118
Koha/Auth/Client/OAuth.pm
Normal file
|
@ -0,0 +1,118 @@
|
|||
package Koha::Auth::Client::OAuth;
|
||||
|
||||
# Copyright Theke Solutions 2022
|
||||
#
|
||||
# This file is part of Koha.
|
||||
#
|
||||
# Koha is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Koha 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 General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Koha; if not, see <http://www.gnu.org/licenses>.
|
||||
|
||||
use Modern::Perl;
|
||||
|
||||
use JSON qw( decode_json );
|
||||
use MIME::Base64 qw{ decode_base64url };
|
||||
use Koha::Patrons;
|
||||
use Mojo::UserAgent;
|
||||
use Mojo::Parameters;
|
||||
|
||||
use base qw( Koha::Auth::Client );
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Koha::Auth::Client::OAuth - Koha OAuth Client
|
||||
|
||||
=head1 API
|
||||
|
||||
=head2 Class methods
|
||||
|
||||
=head3 _get_data_and_patron
|
||||
|
||||
my $mapping = $object->_get_data_and_patron(
|
||||
{ provider => $provider,
|
||||
data => $data,
|
||||
config => $config
|
||||
}
|
||||
);
|
||||
|
||||
Maps OAuth raw data to a patron schema, and returns a patron if it can.
|
||||
|
||||
=cut
|
||||
|
||||
sub _get_data_and_patron {
|
||||
my ( $self, $params ) = @_;
|
||||
|
||||
my $provider = $params->{provider};
|
||||
my $data = $params->{data};
|
||||
my $config = $params->{config};
|
||||
|
||||
my $patron;
|
||||
my $mapped_data;
|
||||
|
||||
my $mapping = decode_json( $provider->mapping );
|
||||
my $matchpoint = $provider->matchpoint;
|
||||
|
||||
if ( $data->{id_token} ) {
|
||||
my ( $header_part, $claims_part, $footer_part ) = split( /\./, $data->{id_token} );
|
||||
|
||||
my $claim = decode_json( decode_base64url($claims_part) );
|
||||
foreach my $key ( keys %$mapping ) {
|
||||
my $pkey = $mapping->{$key};
|
||||
$mapped_data->{$key} = $claim->{$pkey}
|
||||
if defined $claim->{$pkey};
|
||||
}
|
||||
|
||||
my $value = $mapped_data->{$matchpoint};
|
||||
|
||||
my $matchpoint_rs = Koha::Patrons->search( { $matchpoint => $value } );
|
||||
|
||||
if ( defined $value and $matchpoint_rs->count ) {
|
||||
$patron = $matchpoint_rs->next;
|
||||
}
|
||||
|
||||
return ( $mapped_data, $patron )
|
||||
if $patron;
|
||||
}
|
||||
|
||||
if ( defined $config->{userinfo_url} ) {
|
||||
my $access_token = $data->{access_token};
|
||||
my $ua = Mojo::UserAgent->new;
|
||||
my $tx = $ua->get( $config->{userinfo_url} => { Authorization => "Bearer $access_token" } );
|
||||
my $code = $tx->res->code || 'No response';
|
||||
|
||||
return if $code ne '200';
|
||||
my $claim =
|
||||
$tx->res->headers->content_type =~ m!^(application/json|text/javascript)(;\s*charset=\S+)?$!
|
||||
? $tx->res->json
|
||||
: Mojo::Parameters->new( $tx->res->body )->to_hash;
|
||||
|
||||
foreach my $key ( keys %$mapping ) {
|
||||
my $pkey = $mapping->{$key};
|
||||
my $value = $self->_tranverse_hash( { base => $claim, keys => $pkey } );
|
||||
$mapped_data->{$key} = $value
|
||||
if defined $value;
|
||||
}
|
||||
|
||||
my $value = $mapped_data->{$matchpoint};
|
||||
|
||||
my $matchpoint_rs = Koha::Patrons->search( { $matchpoint => $value } );
|
||||
|
||||
if ( defined $value and $matchpoint_rs->count ) {
|
||||
$patron = $matchpoint_rs->next;
|
||||
}
|
||||
|
||||
return ( $mapped_data, $patron )
|
||||
if $patron;
|
||||
}
|
||||
}
|
||||
|
||||
1;
|
Loading…
Reference in a new issue