LP#1936422: RemoteAuth: handle "not permitted" and "has penalties" auth failures... user/jeffdavis/lp1936422-remoteauth-more-error-codes
authorJeff Davis <jdavis@sitka.bclibraries.ca>
Thu, 15 Jul 2021 18:56:49 +0000 (11:56 -0700)
committerJeff Davis <jdavis@sitka.bclibraries.ca>
Thu, 15 Jul 2021 18:56:49 +0000 (11:56 -0700)
Previously, a user who lacked permission to authenticate was treated as
not found by RemoteAuth, and a user with standing penalites was treated
as blocked.  This commit adds the ability to handle those failure modes
separately, using the EZProxyCGI handler as an example; the default
behavior for other RemoteAuth handlers is unchanged.

Signed-off-by: Jeff Davis <jdavis@sitka.bclibraries.ca>
Open-ILS/src/perlmods/lib/OpenILS/WWW/RemoteAuth.pm
Open-ILS/src/perlmods/lib/OpenILS/WWW/RemoteAuth/EZProxyCGI.pm
Open-ILS/src/sql/Pg/150.remoteauth.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.remoteauth-more-error-codes.sql [new file with mode: 0644]
Open-ILS/src/templates/remoteauth/ezproxycgi/error.tt2

index 863796d..1956820 100644 (file)
@@ -183,6 +183,10 @@ sub do_patron_auth {
         return $self->patron_not_found;
     } elsif ($permit_test eq 'expired') {
         return $self->patron_is_expired;
+    } elsif ($permit_test eq 'not_permitted') {
+        return $self->patron_not_permitted;
+    } elsif ($permit_test eq 'has_penalties') {
+        return $self->patron_has_penalties;
     } else {
         return $self->patron_is_blocked;
     }
@@ -268,6 +272,10 @@ sub get_patron_info {
         return $self->patron_is_expired;
     } elsif ($permit_test eq 'blocked') {
         return $self->patron_is_blocked;
+    } elsif ($permit_test eq 'not_permitted') {
+        return $self->patron_not_permitted;
+    } elsif ($permit_test eq 'has_penalties') {
+        return $self->patron_has_penalties;
     } else {
         return $self->backend_error;
     }
@@ -318,5 +326,19 @@ sub patron_is_expired {
     return Apache2::Const::DECLINED;
 }
 
+# patron does not have required permission to authenticate;
+# treat them as being not found
+sub patron_not_permitted {
+    my $self = shift;
+    return $self->patron_not_found;
+}
+
+# patron has blocking penalties;
+# treat them as being blocked
+sub patron_has_penalties {
+    my $self = shift;
+    return $self->patron_is_blocked;
+}
+
 1;
 
index f18063e..cf3a9d3 100644 (file)
@@ -185,12 +185,24 @@ sub patron_not_found {
     return $self->error('patron_not_found');
 }
 
-# patron is barred or has blocking penalties
+# patron is barred/blocked
 sub patron_is_blocked {
     my $self = shift;
     return $self->error('patron_is_blocked');
 }
 
+# patron does not have permission to authenticate
+sub patron_not_permitted {
+    my $self = shift;
+    return $self->error('patron_not_permitted');
+}
+
+# patron has blocking penalties
+sub patron_has_penalties {
+    my $self = shift;
+    return $self->error('patron_has_penalties');
+}
+
 # patron is expired
 sub patron_is_expired {
     my $self = shift;
index 0b36c49..445be88 100644 (file)
@@ -43,7 +43,7 @@ BEGIN
 
     SELECT INTO perm code FROM permission.perm_list WHERE id = profile.perm;
     IF permission.usr_has_perm(usr.id, perm, profile.context_org) IS FALSE THEN
-        RETURN 'not_found';
+        RETURN 'not_permitted';
     END IF;
     
     IF usr.expire_date < NOW() AND profile.allow_expired IS FALSE THEN
@@ -71,7 +71,7 @@ BEGIN
             IF penalty_count > 0 THEN
                 -- User has penalties that match this block, so auth is not permitted.
                 -- Don't bother testing the rest of the block list.
-                RETURN 'blocked';
+                RETURN 'has_penalties';
             END IF;
         END LOOP;
     END IF;
diff --git a/Open-ILS/src/sql/Pg/upgrade/XXXX.remoteauth-more-error-codes.sql b/Open-ILS/src/sql/Pg/upgrade/XXXX.remoteauth-more-error-codes.sql
new file mode 100644 (file)
index 0000000..2d57902
--- /dev/null
@@ -0,0 +1,75 @@
+BEGIN;
+
+SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
+
+CREATE OR REPLACE FUNCTION actor.permit_remoteauth (profile_name TEXT, userid BIGINT) RETURNS TEXT AS $func$
+DECLARE
+    usr               actor.usr%ROWTYPE;
+    profile           config.remoteauth_profile%ROWTYPE;
+    perm              TEXT;
+    context_org_list  INT[];
+    home_prox         INT;
+    block             TEXT;
+    penalty_count     INT;
+BEGIN
+
+    SELECT INTO usr * FROM actor.usr WHERE id = userid AND NOT deleted;
+    IF usr IS NULL THEN
+        RETURN 'not_found';
+    END IF;
+
+    IF usr.barred IS TRUE THEN
+        RETURN 'blocked';
+    END IF;
+
+    SELECT INTO profile * FROM config.remoteauth_profile WHERE name = profile_name;
+    SELECT INTO context_org_list ARRAY_AGG(id) FROM actor.org_unit_full_path( profile.context_org );
+
+    -- user's home library must be within the context org
+    IF profile.restrict_to_org IS TRUE AND usr.home_ou NOT IN (SELECT * FROM UNNEST(context_org_list)) THEN
+        RETURN 'not_found';
+    END IF;
+
+    SELECT INTO perm code FROM permission.perm_list WHERE id = profile.perm;
+    IF permission.usr_has_perm(usr.id, perm, profile.context_org) IS FALSE THEN
+        RETURN 'not_permitted';
+    END IF;
+    
+    IF usr.expire_date < NOW() AND profile.allow_expired IS FALSE THEN
+        RETURN 'expired';
+    END IF;
+
+    IF usr.active IS FALSE AND profile.allow_inactive IS FALSE THEN
+        RETURN 'blocked';
+    END IF;
+
+    -- Proximity of user's home_ou to context_org to see if penalties should be ignored.
+    SELECT INTO home_prox prox FROM actor.org_unit_proximity WHERE from_org = usr.home_ou AND to_org = profile.context_org;
+
+    -- Loop through the block list to see if the user has any matching penalties.
+    IF profile.block_list IS NOT NULL THEN
+        FOR block IN SELECT UNNEST(STRING_TO_ARRAY(profile.block_list, '|')) LOOP
+            SELECT INTO penalty_count COUNT(DISTINCT csp.*)
+                FROM  actor.usr_standing_penalty usp
+                        JOIN config.standing_penalty csp ON (csp.id = usp.standing_penalty)
+                WHERE usp.usr = usr.id
+                        AND usp.org_unit IN ( SELECT * FROM UNNEST(context_org_list) )
+                        AND ( usp.stop_date IS NULL or usp.stop_date > NOW() )
+                        AND ( csp.ignore_proximity IS NULL OR csp.ignore_proximity < home_prox )
+                        AND csp.block_list ~ block;
+            IF penalty_count > 0 THEN
+                -- User has penalties that match this block, so auth is not permitted.
+                -- Don't bother testing the rest of the block list.
+                RETURN 'has_penalties';
+            END IF;
+        END LOOP;
+    END IF;
+
+    -- User has passed all tests.
+    RETURN 'success';
+
+END;
+$func$ LANGUAGE plpgsql;
+
+COMMIT;
+
index d078a08..ad17c06 100644 (file)
@@ -6,6 +6,10 @@
     Patron not found.
     [% ELSIF ctx.error_msg == 'patron_is_blocked' %]
     Your account is blocked.
+    [% ELSIF ctx.error_msg == 'patron_has_penalties' %]
+    Your account has penalties which prevent authentication.
+    [% ELSIF ctx.error_msg == 'patron_not_permitted' %]
+    Your account is not permitted to authenticate.
     [% ELSIF ctx.error_msg == 'patron_is_expired' %]
     Your account is expired.
     [% ELSE %]