Bug 31378: Add Koha::Auth::Client* modules
[koha.git] / Koha / Auth / Client.pm
1 package Koha::Auth::Client;
2
3 # Copyright Theke Solutions 2022
4 #
5 # This file is part of Koha.
6 #
7 # Koha is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # Koha is distributed in the hope that it will be useful, but
13 # WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the GNU General Public License
18 # along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20 use Modern::Perl;
21
22 use Koha::Exceptions::Auth;
23 use Koha::Auth::Providers;
24
25 =head1 NAME
26
27 Koha::Auth::Client - Base Koha auth client
28
29 =head1 API
30
31 =head2 Methods
32
33 =head3 new
34
35     my $auth_client = Koha::Auth::Client->new();
36
37 =cut
38
39 sub new {
40     my ($class) = @_;
41     my $self = {};
42
43     bless( $self, $class );
44 }
45
46 =head3 get_user
47
48     $auth_client->get_user($provider, $data)
49
50 Get user data according to provider's mapping configuration
51
52 =cut
53
54 sub get_user {
55     my ( $self, $params ) = @_;
56     my $provider_code = $params->{provider};
57     my $data          = $params->{data};
58     my $interface     = $params->{interface};
59     my $config        = $params->{config};
60
61     my $provider = Koha::Auth::Providers->search({ code => $provider_code })->next;
62
63     my ( $mapped_data, $patron ) = $self->_get_data_and_patron({ provider => $provider, data => $data, config => $config });
64
65     if ($mapped_data) {
66         my $domain = $self->has_valid_domain_config({ provider => $provider, email => $mapped_data->{email}, interface => $interface});
67
68         $mapped_data->{categorycode} = $domain->default_category_id;
69         $mapped_data->{branchcode}   = $domain->default_library_id;
70
71         return ( $patron, $mapped_data, $domain );
72     }
73 }
74
75 =head3 get_valid_domain_config
76
77     my $domain = Koha::Auth::Client->get_valid_domain_config(
78         {   provider  => $provider,
79             email     => $user_email,
80             interface => $interface
81         }
82     );
83
84 Gets the best suited valid domain configuration for the given provider.
85
86 =cut
87
88 sub get_valid_domain_config {
89     # FIXME: Should be a hashref param
90     my ( $self, $params ) = @_;
91     my $provider   = $params->{provider};
92     my $user_email = $params->{email};
93     my $interface  = $params->{interface};
94
95     my $domains = $provider->domains;
96     my $pattern = '@';
97     my $allow   = "allow_$interface";
98     my @subdomain_matches;
99     my $default_match;
100
101     while ( my $domain = $domains->next ) {
102         next unless $domain->$allow;
103
104         my $domain_text = $domain->domain;
105         unless ( defined $domain_text && $domain_text ne '') {
106             $default_match = $domain;
107             next;
108         }
109         my ( $asterisk, $domain_name ) = ( $domain_text =~ /^(\*)?(.+)$/ );
110         if ( $asterisk eq '*' ) {
111             $pattern .= '.*';
112         }
113         $domain_name =~ s/\./\\\./g;
114         $pattern .= $domain_name . '$';
115         if ( $user_email =~ /$pattern/ ) {
116             if ( $asterisk eq '*' ) {
117                 push @subdomain_matches, { domain => $domain, match_length => length $domain_name };
118             } else {
119
120                 # Perfect match.. return this one.
121                 return $domain;
122             }
123         }
124     }
125
126     if ( @subdomain_matches ) {
127         @subdomain_matches = sort { $b->{match_length} <=> $a->{match_length} } @subdomain_matches
128           unless scalar @subdomain_matches == 1;
129         return $subdomain_matches[0]->{domain};
130     }
131
132     return $default_match || 0;
133 }
134
135 =head3 has_valid_domain_config
136
137     my $has_valid_domain = Koha::Auth::Client->has_valid_domain_config(
138         {   provider  => $provider,
139             email     => $user_email,
140             interface => $interface
141         }
142     );
143
144 Checks if provider has a valid domain for user email. If has, returns that domain.
145
146 =cut
147
148 sub has_valid_domain_config {
149     # FIXME: Should be a hashref param
150     my ( $self, $params ) = @_;
151     my $domain = $self->get_valid_domain_config( $params );
152
153     Koha::Exceptions::Auth::NoValidDomain->throw( code => 401 )
154       unless $domain;
155
156     return $domain;
157 }
158
159 =head3 _get_data_and_patron
160
161     my $mapping = $auth_client->_get_data_and_patron(
162         {   provider => $provider,
163             data     => $data,
164             config   => $config
165         }
166     );
167
168 Generic method that maps raw data to patron schema, and returns a patron if it can.
169
170 Note: this only returns an empty I<hashref>. Each class should have its
171 own mapping returned.
172
173 =cut
174
175 sub _get_data_and_patron {
176     return {};
177 }
178
179 =head3 _tranverse_hash
180
181     my $value = $auth_client->_tranverse_hash( { base => $base_hash, keys => $key_string } );
182
183 Get deep nested value in a hash.
184
185 =cut
186
187 sub _tranverse_hash {
188     my ($self, $params) = @_;
189     my $base = $params->{base};
190     my $keys = $params->{keys};
191     my ($key, $rest) = ($keys =~ /^([^.]+)(?:\.(.*))?/);
192     return unless defined $key;
193     my $value = ref $base eq 'HASH' ? $base->{$key} : $base->[$key];
194     return $value unless $rest;
195     return $self->_tranverse_hash({ base => $value, keys => $rest });
196 }
197
198 1;