View | Details | Raw Unified | Return to bug 39224
Collapse All | Expand All

(-)a/C4/Auth.pm (-20 / +26 lines)
Lines 173-179 sub get_template_and_user { Link Here
173
    my $cookie_mgr = Koha::CookieManager->new;
173
    my $cookie_mgr = Koha::CookieManager->new;
174
174
175
    # Get shibboleth login attribute
175
    # Get shibboleth login attribute
176
    my $shib       = C4::Context->config('useshibboleth') && shib_ok();
176
    my $shib       = C4::Context->preference('ShibbolethAuthentication') && shib_ok();
177
    my $shib_login = $shib ? get_login_shib() : undef;
177
    my $shib_login = $shib ? get_login_shib() : undef;
178
178
179
    C4::Context->interface( $in->{type} );
179
    C4::Context->interface( $in->{type} );
Lines 811-817 sub checkauth { Link Here
811
    my $query = shift;
811
    my $query = shift;
812
812
813
    # Get shibboleth login attribute
813
    # Get shibboleth login attribute
814
    my $shib       = C4::Context->config('useshibboleth') && shib_ok();
814
    my $shib       = C4::Context->preference('ShibbolethAuthentication') && shib_ok();
815
    my $shib_login = $shib ? get_login_shib() : undef;
815
    my $shib_login = $shib ? get_login_shib() : undef;
816
816
817
    # $authnotrequired will be set for scripts which will run without authentication
817
    # $authnotrequired will be set for scripts which will run without authentication
Lines 1021-1026 sub checkauth { Link Here
1021
        $auth_state = 'logout';
1021
        $auth_state = 'logout';
1022
    }
1022
    }
1023
1023
1024
    my $is_sco_sci =
1025
        (      $query->param('sco_user_login')
1026
            || $query->param('sci_user_login')
1027
            || ( $template_name && $template_name =~ m|sc[io]/| ) );
1028
    my $forced_provider =
1029
        $is_sco_sci ? undef : Koha::Auth::Identity::Providers->find_forced_provider( $ENV{SERVER_NAME} );
1030
1024
    unless ($userid) {
1031
    unless ($userid) {
1025
1032
1026
        #we initiate a session prior to checking for a username to allow for anonymous sessions...
1033
        #we initiate a session prior to checking for a username to allow for anonymous sessions...
Lines 1151-1165 sub checkauth { Link Here
1151
            }
1158
            }
1152
1159
1153
            # If shib configured and shibOnly enabled, we should ignore anything other than a shibboleth type login.
1160
            # If shib configured and shibOnly enabled, we should ignore anything other than a shibboleth type login.
1154
            if (
1161
            if (   $shib
1155
                   $shib
1156
                && !$shibSuccess
1162
                && !$shibSuccess
1157
                && (
1163
                && $forced_provider )
1158
                    ( ( $type eq 'opac' ) && C4::Context->preference('OPACShibOnly') )
1159
                    || ( ( $type ne 'opac' )
1160
                        && C4::Context->preference('staffShibOnly') )
1161
                )
1162
                )
1163
            {
1164
            {
1164
                $return = 0;
1165
                $return = 0;
1165
            }
1166
            }
Lines 1522-1527 sub checkauth { Link Here
1522
        );
1523
        );
1523
    }
1524
    }
1524
1525
1526
    if ($forced_provider) {
1527
        my $redirect_url;
1528
        if ( $forced_provider->protocol eq 'SAML2' ) {
1529
            $redirect_url = login_shib_url($query);
1530
        } elsif ( $forced_provider->protocol eq 'OIDC' || $forced_provider->protocol eq 'OAuth' ) {
1531
            my $base = ( $type eq 'opac' ) ? "/api/v1/public/oauth/login" : "/api/v1/oauth/login";
1532
            $redirect_url = "$base/" . $forced_provider->code . "/$type";
1533
        }
1534
        if ($redirect_url) {
1535
            print $query->redirect( -uri => $redirect_url, -status => 303 );
1536
            safe_exit;
1537
        }
1538
    }
1539
1525
    if ($cas) {
1540
    if ($cas) {
1526
1541
1527
        # Is authentication against multiple CAS servers enabled?
1542
        # Is authentication against multiple CAS servers enabled?
Lines 1544-1558 sub checkauth { Link Here
1544
1559
1545
    if ($shib) {
1560
    if ($shib) {
1546
1561
1547
        #If shibOnly is enabled just go ahead and redirect directly
1548
        if (   ( ( $type eq 'opac' ) && C4::Context->preference('OPACShibOnly') )
1549
            || ( ( $type ne 'opac' ) && C4::Context->preference('staffShibOnly') ) )
1550
        {
1551
            my $redirect_url = login_shib_url($query);
1552
            print $query->redirect( -uri => "$redirect_url", -status => 303 );
1553
            safe_exit;
1554
        }
1555
1556
        $template->param(
1562
        $template->param(
1557
            shibbolethAuthentication => $shib,
1563
            shibbolethAuthentication => $shib,
1558
            shibbolethLoginUrl       => login_shib_url($query),
1564
            shibbolethLoginUrl       => login_shib_url($query),
Lines 2003-2009 sub checkpw { Link Here
2003
    $type = 'opac' unless $type;
2009
    $type = 'opac' unless $type;
2004
2010
2005
    # Get shibboleth login attribute
2011
    # Get shibboleth login attribute
2006
    my $shib       = C4::Context->config('useshibboleth') && shib_ok();
2012
    my $shib       = C4::Context->preference('ShibbolethAuthentication') && shib_ok();
2007
    my $shib_login = $shib ? get_login_shib() : undef;
2013
    my $shib_login = $shib ? get_login_shib() : undef;
2008
2014
2009
    my $anonymous_patron = C4::Context->preference('AnonymousPatron');
2015
    my $anonymous_patron = C4::Context->preference('AnonymousPatron');
(-)a/C4/Auth_with_shibboleth.pm (-19 / +49 lines)
Lines 18-27 package C4::Auth_with_shibboleth; Link Here
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
18
# along with Koha; if not, see <https://www.gnu.org/licenses>.
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
use base 'Exporter';
21
22
our ( @ISA, @EXPORT_OK );
22
23
23
BEGIN {
24
BEGIN {
24
    our @EXPORT_OK = qw(shib_ok logout_shib login_shib_url checkpw_shib get_login_shib);
25
    require Exporter;
26
    @ISA       = qw(Exporter);
27
    @EXPORT_OK = qw(shib_ok logout_shib login_shib_url checkpw_shib get_login_shib);
25
}
28
}
26
29
27
use C4::Context;
30
use C4::Context;
Lines 34-39 use Carp qw( carp ); Link Here
34
use List::MoreUtils qw( any );
37
use List::MoreUtils qw( any );
35
38
36
use Koha::Logger;
39
use Koha::Logger;
40
use Koha::Auth::Identity::Providers;
37
41
38
# Check that shib config is not malformed
42
# Check that shib config is not malformed
39
43
Lines 241-268 sub _get_return { Link Here
241
}
245
}
242
246
243
sub _get_shib_config {
247
sub _get_shib_config {
244
    my $config = C4::Context->config('shibboleth');
248
    my $hostname = $ENV{SERVER_NAME};
245
249
    my $provider = Koha::Auth::Identity::Providers->search(
246
    if ( !$config ) {
250
        {
247
        Koha::Logger->get->warn('shibboleth config not defined');
251
            'me.protocol'          => 'SAML2',
252
            'me.enabled'           => 1,
253
            'hostname.hostname'    => $hostname,
254
            'hostnames.is_enabled' => 1,
255
        },
256
        { prefetch => [ 'mappings', { 'hostnames' => 'hostname' } ], rows => 1 }
257
    )->next;
258
    return 0 unless $provider;
259
260
    my ( $mapping, $matchpoint ) = $provider->mappings->as_auth_mapping;
261
262
    unless ($matchpoint) {
263
        carp 'shibboleth matchpoint not defined';
248
        return 0;
264
        return 0;
249
    }
265
    }
250
266
251
    if ( $config->{matchpoint}
267
    unless ( defined $mapping->{$matchpoint}->{is} ) {
252
        && defined( $config->{mapping}->{ $config->{matchpoint} }->{is} ) )
268
        carp 'shibboleth matchpoint not mapped';
253
    {
254
        my $logger = Koha::Logger->get;
255
        $logger->debug( "koha borrower field to match: " . $config->{matchpoint} );
256
        $logger->debug( "shibboleth attribute to match: " . $config->{mapping}->{ $config->{matchpoint} }->{is} );
257
        return $config;
258
    } else {
259
        if ( !$config->{matchpoint} ) {
260
            carp 'shibboleth matchpoint not defined';
261
        } else {
262
            carp 'shibboleth matchpoint not mapped';
263
        }
264
        return 0;
269
        return 0;
265
    }
270
    }
271
272
    my $saml2_config = $provider->get_config // {};
273
274
    my $config = {
275
        matchpoint => $matchpoint,
276
        mapping    => $mapping,
277
        autocreate => $saml2_config->{autocreate} || 0,
278
        sync       => $saml2_config->{sync}       || 0,
279
        welcome    => $saml2_config->{welcome}    || 0,
280
    };
281
282
    my $logger = Koha::Logger->get;
283
    $logger->debug( "koha borrower field to match: " . $config->{matchpoint} );
284
    $logger->debug( "shibboleth attribute to match: " . $mapping->{ $config->{matchpoint} }->{is} );
285
286
    return $config;
266
}
287
}
267
288
268
1;
289
1;
Lines 405-410 A sugar function to that simply returns the current page URI with appropriate pr Link Here
405
426
406
This routine is NOT exported
427
This routine is NOT exported
407
428
429
=head2 get_force_sso_setting
430
431
  my $force_sso = get_force_sso_setting($interface);
432
433
Returns the force SSO setting for the given interface (opac or intranet).
434
Returns 0 if shibboleth is not configured or the setting is disabled.
435
436
  $interface - 'opac' or 'intranet'
437
408
=head2 _get_shib_config
438
=head2 _get_shib_config
409
439
410
  my $config = _get_shib_config();
440
  my $config = _get_shib_config();
(-)a/Koha/Auth/Hostname.pm (+87 lines)
Line 0 Link Here
1
package Koha::Auth::Hostname;
2
3
# Copyright Koha Community 2026
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 <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use base qw(Koha::Object);
23
24
use C4::Context;
25
use Koha::Database;
26
27
=head1 NAME
28
29
Koha::Auth::Hostname - Koha Auth Hostname Object class
30
31
=head1 API
32
33
=head2 Class methods
34
35
=head3 sync_from_syspref
36
37
    Koha::Auth::Hostname->sync_from_syspref( 'OPACBaseURL', $url );
38
39
Updates (or inserts) the reserved hostname row for the given URL syspref.
40
C<OPACBaseURL> is always stored as C<hostname_id = 1>; C<staffClientBaseURL>
41
as C<hostname_id = 2>. Does nothing if C<$value> is empty or does not contain
42
a recognisable hostname.
43
44
=cut
45
46
my %SYSPREF_HOSTNAME_ID = (
47
    opacbaseurl        => 1,
48
    staffclientbaseurl => 2,
49
);
50
51
sub sync_from_syspref {
52
    my ( $class, $pref_name, $value ) = @_;
53
    $value //= '';
54
    my $hostname_id = $SYSPREF_HOSTNAME_ID{ lc $pref_name } or return;
55
    my ($hostname)  = $value =~ m{^https?://([^/:?#]+)}     or return;
56
    Koha::Database->new->schema->resultset('Hostname')->update_or_create(
57
        { hostname_id => $hostname_id, hostname => $hostname },
58
        { key         => 'primary' }
59
    );
60
}
61
62
=head3 sync_from_sysprefs
63
64
Ensures that the hostnames derived from OPACBaseURL and staffClientBaseURL
65
are present in the hostnames table. OPACBaseURL maps to C<hostname_id = 1>
66
and staffClientBaseURL to C<hostname_id = 2>.
67
68
=cut
69
70
sub sync_from_sysprefs {
71
    my ($class) = @_;
72
    for my $pref ( keys %SYSPREF_HOSTNAME_ID ) {
73
        $class->sync_from_syspref( $pref, C4::Context->preference($pref) );
74
    }
75
}
76
77
=head2 Internal methods
78
79
=head3 _type
80
81
=cut
82
83
sub _type {
84
    return 'Hostname';
85
}
86
87
1;
(-)a/Koha/Auth/Hostnames.pm (+50 lines)
Line 0 Link Here
1
package Koha::Auth::Hostnames;
2
3
# Copyright Koha Community 2026
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 <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use base qw(Koha::Objects);
23
24
use Koha::Auth::Hostname;
25
26
=head1 NAME
27
28
Koha::Auth::Hostnames - Koha Auth Hostnames Object set class
29
30
=head1 API
31
32
=head2 Internal methods
33
34
=head3 _type
35
36
=cut
37
38
sub _type {
39
    return 'Hostname';
40
}
41
42
=head3 object_class
43
44
=cut
45
46
sub object_class {
47
    return 'Koha::Auth::Hostname';
48
}
49
50
1;
(-)a/Koha/Auth/Identity/Provider.pm (-62 / +47 lines)
Lines 22-27 use Modern::Perl; Link Here
22
use base qw(Koha::Object Koha::Object::JSONFields);
22
use base qw(Koha::Object Koha::Object::JSONFields);
23
23
24
use Koha::Auth::Identity::Provider::Domains;
24
use Koha::Auth::Identity::Provider::Domains;
25
use Koha::Auth::Identity::Provider::Hostnames;
26
use Koha::Auth::Identity::Provider::Mappings;
25
use Koha::Exceptions;
27
use Koha::Exceptions;
26
28
27
=head1 NAME
29
=head1 NAME
Lines 46-51 sub domains { Link Here
46
    return Koha::Auth::Identity::Provider::Domains->_new_from_dbic( scalar $self->_result->domains );
48
    return Koha::Auth::Identity::Provider::Domains->_new_from_dbic( scalar $self->_result->domains );
47
}
49
}
48
50
51
=head3 mappings
52
53
    my $mappings = $provider->mappings;
54
55
Returns the related I<Koha::Auth::Identity::Provider::Mappings> iterator.
56
57
=cut
58
59
sub mappings {
60
    my ($self) = @_;
61
62
    return Koha::Auth::Identity::Provider::Mappings->_new_from_dbic(
63
        scalar $self->_result->identity_provider_mappings );
64
}
65
66
=head3 hostnames
67
68
    my $hostnames = $provider->hostnames;
69
70
Returns the related I<Koha::Auth::Identity::Provider::Hostnames> iterator.
71
72
=cut
73
74
sub hostnames {
75
    my ($self) = @_;
76
77
    return Koha::Auth::Identity::Provider::Hostnames->_new_from_dbic( scalar $self->_result->hostnames );
78
}
79
49
=head3 get_config
80
=head3 get_config
50
81
51
    my $config = $provider->get_config;
82
    my $config = $provider->get_config;
Lines 81-86 sub get_config { Link Here
81
        }
112
        }
82
    );
113
    );
83
114
115
    # SAML2 (config holds protocol-specific settings like autocreate/sync/welcome)
116
    $provider->set_config(
117
        {
118
            autocreate => 1,
119
            sync       => 1,
120
            welcome    => 0,
121
        }
122
    );
123
84
This method stores the passed config in JSON format.
124
This method stores the passed config in JSON format.
85
125
86
=cut
126
=cut
Lines 99-151 sub set_config { Link Here
99
    return $self->set_encoded_json_field( { data => $config, field => 'config' } );
139
    return $self->set_encoded_json_field( { data => $config, field => 'config' } );
100
}
140
}
101
141
102
=head3 get_mapping
142
=head3 store
103
143
104
    my $mapping = $provider->get_mapping;
144
Identity provider specific store method to ensure config population before saving to db
105
106
Returns a I<hashref> containing the attribute mapping for the provider.
107
145
108
=cut
146
=cut
109
147
110
sub get_mapping {
148
sub store {
111
    my ($self) = @_;
149
    my ($self) = @_;
112
150
    $self->config( $self->config // '{}' );
113
    return $self->decode_json_field( { field => 'mapping' } );
151
    return $self->SUPER::store();
114
}
115
116
=head3 set_mapping
117
118
    $provider->mapping( $mapping );
119
120
This method stores the passed mappings in JSON format.
121
122
=cut
123
124
sub set_mapping {
125
    my ( $self, $mapping ) = @_;
126
127
    return $self->set_encoded_json_field( { data => $mapping, field => 'mapping' } );
128
}
129
130
=head3 upgrade_class
131
132
    my $upgraded_object = $provider->upgrade_class
133
134
Returns a new instance of the object, with the right class.
135
136
=cut
137
138
sub upgrade_class {
139
    my ($self) = @_;
140
    my $protocol = $self->protocol;
141
142
    my $class = $self->protocol_to_class_mapping->{$protocol};
143
144
    Koha::Exception->throw( $protocol . ' is not a valid protocol' )
145
        unless $class;
146
147
    eval "require $class";
148
    return $class->_new_from_dbic( $self->_result );
149
}
152
}
150
153
151
=head2 Internal methods
154
=head2 Internal methods
Lines 162-173 suitable for API output. Link Here
162
sub to_api {
165
sub to_api {
163
    my ( $self, $params ) = @_;
166
    my ( $self, $params ) = @_;
164
167
165
    my $config  = $self->get_config;
168
    my $config = $self->get_config;
166
    my $mapping = $self->get_mapping;
167
169
168
    my $json = $self->SUPER::to_api($params);
170
    my $json = $self->SUPER::to_api($params);
169
    $json->{config}  = $config;
171
    $json->{config} = $config;
170
    $json->{mapping} = $mapping;
171
172
172
    return $json;
173
    return $json;
173
}
174
}
Lines 180-201 sub _type { Link Here
180
    return 'IdentityProvider';
181
    return 'IdentityProvider';
181
}
182
}
182
183
183
=head3 protocol_to_class_mapping
184
185
    my $mapping = Koha::Auth::Identity::Provider::protocol_to_class_mapping
186
187
Internal method that returns a mapping between I<protocol> codes and
188
implementing I<classes>. To be used by B<upgrade_class>.
189
190
=cut
191
192
sub protocol_to_class_mapping {
193
    return {
194
        OAuth => 'Koha::Auth::Identity::Provider::OAuth',
195
        OIDC  => 'Koha::Auth::Identity::Provider::OIDC',
196
    };
197
}
198
199
=head3 mandatory_config_attributes
184
=head3 mandatory_config_attributes
200
185
201
Stub method for raising exceptions on invalid protocols.
186
Stub method for raising exceptions on invalid protocols.
(-)a/Koha/Auth/Identity/Provider/Hostname.pm (+56 lines)
Line 0 Link Here
1
package Koha::Auth::Identity::Provider::Hostname;
2
3
# Copyright Koha Community 2026
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 <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use base qw(Koha::Object);
23
24
=head1 NAME
25
26
Koha::Auth::Identity::Provider::Hostname - Koha Auth Provider Hostname Object class
27
28
=head1 API
29
30
=head2 Class methods
31
32
=head3 to_api
33
34
Overrides the default serialization to embed the hostname string from the
35
related Hostname record alongside the hostname_id FK.
36
37
=cut
38
39
sub to_api {
40
    my ( $self, $params ) = @_;
41
    my $data = $self->SUPER::to_api($params);
42
    $data->{hostname} = $self->_result->hostname->hostname;
43
    return $data;
44
}
45
46
=head2 Internal methods
47
48
=head3 _type
49
50
=cut
51
52
sub _type {
53
    return 'IdentityProviderHostname';
54
}
55
56
1;
(-)a/Koha/Auth/Identity/Provider/Hostnames.pm (+51 lines)
Line 0 Link Here
1
package Koha::Auth::Identity::Provider::Hostnames;
2
3
# Copyright Koha Community 2026
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 <https://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
22
use Koha::Database;
23
use Koha::Auth::Identity::Provider::Hostname;
24
25
use base qw(Koha::Objects);
26
27
=head1 NAME
28
29
Koha::Auth::Identity::Provider::Hostnames - Koha Auth Provider Hostnames Object set class
30
31
=head1 API
32
33
=head2 Internal methods
34
35
=head3 _type
36
37
=cut
38
39
sub _type {
40
    return 'IdentityProviderHostname';
41
}
42
43
=head3 object_class
44
45
=cut
46
47
sub object_class {
48
    return 'Koha::Auth::Identity::Provider::Hostname';
49
}
50
51
1;
(-)a/Koha/Auth/Identity/Provider/Mapping.pm (+72 lines)
Line 0 Link Here
1
package Koha::Auth::Identity::Provider::Mapping;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <https://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use base qw(Koha::Object);
21
22
use Koha::Exceptions;
23
use Koha::Auth::Identity::Provider::Mappings;
24
25
=head1 NAME
26
27
Koha::Auth::Identity::Provider::Mapping - Koha Identity Provider field mapping object
28
29
=head1 API
30
31
=head2 Class methods
32
33
=head3 store
34
35
Override store to enforce the single-matchpoint rule and validate that at least
36
one of C<provider_field> or C<default_content> is supplied.
37
38
=cut
39
40
sub store {
41
    my ($self) = @_;
42
43
    unless ( defined $self->provider_field || defined $self->default_content ) {
44
        Koha::Exceptions::MissingParameter->throw( parameter => 'provider_field or default_content' );
45
    }
46
47
    if ( $self->is_matchpoint ) {
48
49
        # Clear any existing matchpoint for this provider
50
        Koha::Auth::Identity::Provider::Mappings->search(
51
            {
52
                identity_provider_id => $self->identity_provider_id,
53
                is_matchpoint        => 1,
54
                ( $self->mapping_id ? ( mapping_id => { '!=' => $self->mapping_id } ) : () ),
55
            }
56
        )->update( { is_matchpoint => 0 } );
57
    }
58
59
    return $self->SUPER::store();
60
}
61
62
=head2 Internal methods
63
64
=head3 _type
65
66
=cut
67
68
sub _type {
69
    return 'IdentityProviderMapping';
70
}
71
72
1;
(-)a/Koha/Auth/Identity/Provider/Mappings.pm (+89 lines)
Line 0 Link Here
1
package Koha::Auth::Identity::Provider::Mappings;
2
3
# This file is part of Koha.
4
#
5
# Koha is free software; you can redistribute it and/or modify it
6
# under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
9
#
10
# Koha is distributed in the hope that it will be useful, but
11
# WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
14
#
15
# You should have received a copy of the GNU General Public License
16
# along with Koha; if not, see <https://www.gnu.org/licenses>.
17
18
use Modern::Perl;
19
20
use base qw(Koha::Objects);
21
22
use Koha::Auth::Identity::Provider::Mapping;
23
24
=head1 NAME
25
26
Koha::Auth::Identity::Provider::Mappings - Koha Identity Provider field mappings collection
27
28
=head1 API
29
30
=head2 Class methods
31
32
=head3 get_matchpoint
33
34
    my $matchpoint_mapping = $mappings->get_matchpoint;
35
36
Returns the mapping row flagged as the matchpoint, or undef if none.
37
38
=cut
39
40
sub get_matchpoint {
41
    my ($self) = @_;
42
    return $self->search( { is_matchpoint => 1 } )->next;
43
}
44
45
=head3 as_auth_mapping
46
47
    my $mapping_hashref = $mappings->as_auth_mapping;
48
49
Returns a hashref suitable for use by the authentication layer:
50
C<{ koha_field => provider_field, ... }> plus C<matchpoint> key.
51
52
=cut
53
54
sub as_auth_mapping {
55
    my ($self) = @_;
56
57
    my %mapping;
58
    my $matchpoint;
59
60
    while ( my $m = $self->next ) {
61
        $mapping{ $m->koha_field } = {
62
            is      => $m->provider_field,
63
            content => $m->default_content,
64
        };
65
        $matchpoint = $m->koha_field if $m->is_matchpoint;
66
    }
67
68
    return ( \%mapping, $matchpoint );
69
}
70
71
=head2 Internal methods
72
73
=head3 _type
74
75
=cut
76
77
sub _type {
78
    return 'IdentityProviderMapping';
79
}
80
81
=head3 object_class
82
83
=cut
84
85
sub object_class {
86
    return 'Koha::Auth::Identity::Provider::Mapping';
87
}
88
89
1;
(-)a/Koha/Auth/Identity/Providers.pm (-2 / +68 lines)
Lines 19-26 package Koha::Auth::Identity::Providers; Link Here
19
19
20
use Modern::Perl;
20
use Modern::Perl;
21
21
22
use Koha::Database;
23
use Koha::Auth::Identity::Provider;
22
use Koha::Auth::Identity::Provider;
23
use Koha::Auth::Identity::Provider::Hostnames;
24
use Koha::Auth::Identity::Provider::OAuth;
25
use Koha::Auth::Identity::Provider::OIDC;
24
26
25
use base qw(Koha::Objects);
27
use base qw(Koha::Objects);
26
28
Lines 30-35 Koha::Auth::Identity::Providers - Koha Auth Provider Object class Link Here
30
32
31
=head1 API
33
=head1 API
32
34
35
=head2 Class methods
36
37
=head3 find_forced_provider
38
39
    my $provider = Koha::Auth::Identity::Providers->find_forced_provider($hostname);
40
41
Returns the I<Koha::Auth::Identity::Provider> object if the given I<hostname> is configured to
42
force SSO, or C<undef> if no forced provider is found.
43
44
=cut
45
46
sub find_forced_provider {
47
    my ( $self, $hostname ) = @_;
48
    return unless $hostname;
49
50
    my $hostname_link = Koha::Auth::Identity::Provider::Hostnames->search(
51
        {
52
            'hostname.hostname'         => $hostname,
53
            'identity_provider.enabled' => 1,
54
            'me.is_enabled'             => 1,
55
            'me.force_sso'              => 1,
56
        },
57
        { join => [ 'hostname', 'identity_provider' ] }
58
    )->next;
59
60
    return unless $hostname_link;
61
62
    my $ip_result = $hostname_link->_result->identity_provider;
63
    return $self->object_class($ip_result)->_new_from_dbic($ip_result);
64
}
65
33
=head2 Internal methods
66
=head2 Internal methods
34
67
35
=cut
68
=cut
Lines 42-53 sub _type { Link Here
42
    return 'IdentityProvider';
75
    return 'IdentityProvider';
43
}
76
}
44
77
78
=head3 _polymorphic_field
79
80
Return the field in the table that defines the polymorphic class to be built
81
82
=cut
83
84
sub _polymorphic_field {
85
    return 'protocol';
86
}
87
88
=head3 _polymorphic_map
89
90
Return the mapping from protocol value to implementing class name
91
92
=cut
93
94
sub _polymorphic_map {
95
    return {
96
        OAuth => 'Koha::Auth::Identity::Provider::OAuth',
97
        OIDC  => 'Koha::Auth::Identity::Provider::OIDC',
98
    };
99
}
100
45
=head3 object_class
101
=head3 object_class
46
102
103
Return object class dynamically based on protocol
104
47
=cut
105
=cut
48
106
49
sub object_class {
107
sub object_class {
50
    return 'Koha::Auth::Identity::Provider';
108
    my ( $self, $object ) = @_;
109
110
    return 'Koha::Auth::Identity::Provider' unless $object;
111
112
    my $field = $self->_polymorphic_field;
113
    my $map   = $self->_polymorphic_map;
114
115
    return $map->{ $object->$field } || 'Koha::Auth::Identity::Provider';
51
}
116
}
52
117
53
1;
118
1;
119
(-)a/Koha/Config/SysPref.pm (-1 / +8 lines)
Lines 46-52 sub store { Link Here
46
46
47
    C4::Log::logaction( 'SYSTEMPREFERENCE', $action, undef, $self->variable . ' | ' . $self->value );
47
    C4::Log::logaction( 'SYSTEMPREFERENCE', $action, undef, $self->variable . ' | ' . $self->value );
48
48
49
    return $self->SUPER::store($self);
49
    my $result = $self->SUPER::store($self);
50
51
    if ( $self->variable =~ /\A(?:opacbaseurl|staffclientbaseurl)\z/i ) {
52
        require Koha::Auth::Hostname;
53
        Koha::Auth::Hostname->sync_from_syspref( $self->variable, $self->value );
54
    }
55
56
    return $result;
50
}
57
}
51
58
52
=head3 delete
59
=head3 delete
(-)a/Koha/Template/Plugin/AuthClient.pm (-2 / +15 lines)
Lines 55-62 sub get_providers { Link Here
55
55
56
    # Handle database upgrade state where schema might be out of sync
56
    # Handle database upgrade state where schema might be out of sync
57
    try {
57
    try {
58
        my $providers =
58
        my $hostname  = $ENV{SERVER_NAME};
59
            Koha::Auth::Identity::Providers->search( { "domains.allow_$interface" => 1 }, { prefetch => 'domains' } );
59
        my $providers = Koha::Auth::Identity::Providers->search(
60
            {
61
                "domains.allow_$interface" => 1,
62
                "me.enabled"               => 1,
63
                -or                        => [
64
                    { 'hostname.hostname'                       => $hostname, 'hostnames.is_enabled' => 1 },
65
                    { 'hostnames.identity_provider_hostname_id' => undef }
66
                ]
67
            },
68
            {
69
                join     => [ 'domains', { 'hostnames' => 'hostname' } ],
70
                distinct => 1
71
            }
72
        );
60
        my $base_url = ( $interface eq 'staff' ) ? "/api/v1/oauth/login" : "/api/v1/public/oauth/login";
73
        my $base_url = ( $interface eq 'staff' ) ? "/api/v1/oauth/login" : "/api/v1/public/oauth/login";
61
74
62
        while ( my $provider = $providers->next ) {
75
        while ( my $provider = $providers->next ) {
(-)a/Makefile.PL (-1 / +1 lines)
Lines 363-368 my $target_map = { Link Here
363
    './serials'                                        => 'INTRANET_CGI_DIR',
363
    './serials'                                        => 'INTRANET_CGI_DIR',
364
    './services'                                       => 'INTRANET_CGI_DIR',
364
    './services'                                       => 'INTRANET_CGI_DIR',
365
    './sip2'                                           => 'INTRANET_CGI_DIR',
365
    './sip2'                                           => 'INTRANET_CGI_DIR',
366
    './shibboleth'                                     => 'INTRANET_CGI_DIR',
366
    './skel'                                           => 'NONE',
367
    './skel'                                           => 'NONE',
367
    './skel/var/lock/koha'                             => { target => 'LOCK_DIR',       trimdir => -1 },
368
    './skel/var/lock/koha'                             => { target => 'LOCK_DIR',       trimdir => -1 },
368
    './skel/var/log/koha'                              => { target => 'LOG_DIR',        trimdir => -1 },
369
    './skel/var/log/koha'                              => { target => 'LOG_DIR',        trimdir => -1 },
369
- 

Return to bug 39224