Initial external authentication support via proxy
authorDan Wells <dbw2@calvin.edu>
Thu, 3 Nov 2011 18:17:24 +0000 (14:17 -0400)
committerDan Wells <dbw2@calvin.edu>
Sat, 10 Mar 2012 02:51:39 +0000 (21:51 -0500)
This is the initial commit to support an authentication proxy module
to facilitate external authentication.  It is a work in progress.

What is does so far:
  - Optionally redirects all JSOPAC login requests over SSL by building
    on the 'forceLoginSSL' configuration bool (you MUST enable this
    option for proper use of the auth proxy)
  - Provides a basic plug-in framework for external authentication
    implementations, including configuration options for segregating
    authenticators based on login type or org_unit
  - Allows for multiple cascading authentication tests, including
    simultaneous support for external and internal (EG 'native')
    authentication
  - Provides a 'master switch' to easily revert to using the native EG
    authentication routines only
  - Includes an example LDAP plug-in which supports bind-style auth
    checks

Biggest outstanding known needs:
  - TTOPAC integration, including SSL redirection
  - Tying of login attempts to current brute-force prevention setup
  - Treatment of end-user 'change password' interfaces
  - Support TT/Conifer style authentication prompt

Missing but desirable feature:
  - Allow for manual selection of authenticator by end-user, including
    localization support

Signed-off-by: Dan Wells <dbw2@calvin.edu>
Signed-off-by: Dan Scott <dscott@laurentian.ca>

13 files changed:
Open-ILS/examples/opensrf.xml.example
Open-ILS/examples/opensrf_core.xml.example
Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy.pm [new file with mode: 0644]
Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/AuthBase.pm [new file with mode: 0644]
Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth.pm [new file with mode: 0644]
Open-ILS/web/opac/common/js/config.js
Open-ILS/web/opac/common/js/init.js
Open-ILS/web/opac/common/js/opac_utils.js
Open-ILS/web/opac/common/js/utils.js
Open-ILS/web/opac/skin/default/js/rdetail.js
Open-ILS/web/opac/skin/default/js/sidebar.js
Open-ILS/xul/staff_client/chrome/content/auth/session.js
Open-ILS/xul/staff_client/chrome/content/main/constants.js

index 325b4c2..82c55e7 100644 (file)
@@ -190,6 +190,7 @@ vim:et:ts=4:sw=4:
                 <service>open-ils.circ</service>
                 <service>open-ils.actor</service>
                 <service>open-ils.auth</service>
+                <service>open-ils.auth_proxy</service>
                 <service>open-ils.collections</service>
                 <service>open-ils.justintime</service>
             </allowed_services>
@@ -361,6 +362,57 @@ vim:et:ts=4:sw=4:
                 </app_settings>
             </open-ils.auth>
 
+            <!-- Authentication proxy server -->
+            <open-ils.auth_proxy>
+                <keepalive>5</keepalive>
+                <stateless>1</stateless>
+                <language>perl</language>
+                <implementation>OpenILS::Application::AuthProxy</implementation>
+                <max_requests>93</max_requests>
+
+                <unix_config>
+                    <max_requests>1000</max_requests>
+                    <unix_log>open-ils.auth-proxy_unix.log</unix_log>
+                    <unix_sock>open-ils.auth-proxy_unix.sock</unix_sock>
+                    <unix_pid>open-ils.auth-proxy_unix.pid</unix_pid>
+                    <min_children>1</min_children>
+                    <max_children>15</max_children>
+                    <min_spare_children>1</min_spare_children>
+                    <max_spare_children>5</max_spare_children>
+                </unix_config>
+
+                <app_settings>
+                    <!-- 'enabled' is the master switch; set to 'true' to enable proxied logins -->
+                    <enabled>false</enabled>
+                    <authenticators>
+                        <!-- the following is a sample configuration for the LDAP_Auth module; please adjust as needed -->
+                        <!--
+                        <authenticator>
+                            <name>ldap</name>
+                            <module>OpenILS::Application::AuthProxy::LDAP_Auth</module>
+                            <hostname>name.domain.com</hostname>
+                            <basedn>ou=people,dc=domain,dc=com</basedn>
+                            <authid>cn=username,ou=specials,dc=domain,dc=com</authid>
+                            <password>my_ldap_password_for_authid_user</password>
+                            <login_types>
+                                <type>staff</type>
+                                <type>opac</type>
+                            </login_types>
+                            <org_units>
+                                <unit>103</unit>
+                                <unit>104</unit>
+                            </org_units>
+                        </authenticator>
+                        -->
+                        <!-- 'native' is a proxied version of Evergreen's standard authentication -->
+                        <authenticator>
+                            <name>native</name>
+                            <!-- you can add 'login_types' and 'org_units' limits to this authenticator as well, if needed -->
+                        </authenticator>
+                    </authenticators>
+                </app_settings>
+            </open-ils.auth_proxy>
+
             <!-- Generic search server -->
             <open-ils.search>
                 <keepalive>5</keepalive>
@@ -1146,6 +1198,7 @@ vim:et:ts=4:sw=4:
                 <appname>open-ils.circ</appname> 
                 <appname>open-ils.actor</appname> 
                 <appname>open-ils.auth</appname> 
+                <appname>open-ils.auth_proxy</appname> 
                 <appname>open-ils.storage</appname>  
                 <appname>open-ils.penalty</appname>  
                 <appname>open-ils.justintime</appname>  
index cebffee..440bd8b 100644 (file)
@@ -22,6 +22,7 @@ Example OpenSRF bootstrap configuration file for Evergreen
           <service>open-ils.actor</service>
           <service>open-ils.acq</service>
           <service>open-ils.auth</service>
+          <service>open-ils.auth_proxy</service>
           <service>open-ils.booking</service>
           <service>open-ils.cat</service>
           <service>open-ils.circ</service>
@@ -89,6 +90,7 @@ Example OpenSRF bootstrap configuration file for Evergreen
       <service>open-ils.circ</service>
       <service>open-ils.actor</service>
       <service>open-ils.auth</service>
+      <service>open-ils.auth_proxy</service>
       <service>open-ils.collections</service>
       <service>open-ils.reporter</service>
     </services>
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy.pm
new file mode 100644 (file)
index 0000000..c165462
--- /dev/null
@@ -0,0 +1,292 @@
+#!/usr/bin/perl
+
+# This program 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 2
+# of the License, or (at your option) any later version.
+#
+# This program 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 this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+
+=head1 NAME
+
+OpenILS::Application::AuthProxy - Negotiator for proxy-style authentication
+
+=head1 AUTHOR
+
+Dan Wells, dbw2@calvin.edu
+
+=cut
+
+package OpenILS::Application::AuthProxy;
+
+use strict;
+use warnings;
+use OpenILS::Application;
+use base qw/OpenILS::Application/;
+use OpenSRF::Utils::Logger qw(:logger);
+use OpenSRF::Utils::SettingsClient;
+use OpenILS::Application::AppUtils;
+use OpenILS::Utils::Fieldmapper;
+use OpenILS::Event;
+use UNIVERSAL::require;
+use Digest::MD5 qw/md5_hex/;
+my $U = 'OpenILS::Application::AppUtils';
+
+# NOTE: code assumes throughout that '0' is never a valid username, barcode,
+# or password; some logic will need to be tweaked to support it if needed.
+
+my @authenticators;
+my %authenticators_by_name;
+my $enabled = 'false';
+
+sub initialize {
+    my $conf = OpenSRF::Utils::SettingsClient->new;
+    my @pfx = ( "apps", "open-ils.auth_proxy", "app_settings" );
+
+    $enabled = $conf->config_value( @pfx, 'enabled' );
+
+    my $auth_configs = $conf->config_value( @pfx, 'authenticators', 'authenticator' );
+    $auth_configs = [$auth_configs] if ref($auth_configs) eq 'HASH';
+
+    if ( !@$auth_configs ) {
+        $logger->error("AuthProxy: authenticators list not found!");
+    } else {
+        foreach my $auth_config (@$auth_configs) {
+            my $auth_handler;
+            if ($auth_config->{'name'} eq 'native') {
+                $auth_handler = 'OpenILS::Application::AuthProxy::Native';
+            } else {
+                $auth_handler = $auth_config->{module};
+                next unless $auth_handler;
+
+                $logger->debug("Attempting to load AuthProxy handler: $auth_handler");
+                $auth_handler->use;
+                if($@) {
+                    $logger->error("Unable to load AuthProxy handler [$auth_handler]: $@");
+                    next;
+                }
+            }
+
+            &_make_option_array($auth_config, 'login_types', 'type');
+            &_make_option_array($auth_config, 'org_units', 'unit');
+
+            my $authenticator = $auth_handler->new($auth_config);
+            push @authenticators, $authenticator;
+            $authenticators_by_name{$authenticator->name} = $authenticator;
+            $logger->debug("Successfully loaded AuthProxy handler: $auth_handler");
+        }
+        $logger->debug("AuthProxy: authenticators loaded");
+    }
+}
+
+# helper function to simplify the config structure
+sub _make_option_array {
+    my ($auth_config, $container_name, $node_name) = @_;
+
+    if (exists $auth_config->{$container_name}
+        and ref $auth_config->{$container_name} eq 'HASH') {
+        my $nodes = $auth_config->{$container_name}{$node_name};
+        if ($nodes) {
+            if (ref $nodes ne 'ARRAY') {
+                $auth_config->{$container_name} = [$nodes];
+            } else {
+                $auth_config->{$container_name} = $nodes;
+            }
+        } else {
+            delete $auth_config->{$container_name};
+        }
+    } else {
+        delete $auth_config->{$container_name};
+    }
+}
+
+
+
+__PACKAGE__->register_method(
+    method    => "enabled",
+    api_name  => "open-ils.auth_proxy.enabled",
+    api_level => 1,
+    stream    => 1,
+    argc      => 0,
+    signature => {
+        desc => q/Check if AuthProxy is enabled/,
+        return => {
+            desc => "True if enabled, false if not",
+            type => "bool"
+        }
+    }
+);
+sub enabled {
+    return (!$enabled or $enabled eq 'false') ? 0 : 1;
+}
+
+__PACKAGE__->register_method(
+    method    => "login",
+    api_name  => "open-ils.auth_proxy.login",
+    api_level => 1,
+    stream    => 1,
+    argc      => 1,
+    signature => {
+        desc => q/Basic single-factor login method/,
+        params => [
+            {name=> "args", desc => q/A hash of arguments.  Valid keys and their meanings:
+    username := Username to authenticate.
+    barcode  := Barcode of user to authenticate (currently supported by 'native' only!)
+    password := Password for verifying the user.
+    type     := Type of login being attempted (Staff Client, OPAC, etc.).
+    org      := Org unit id
+/,
+                type => "hash"}
+        ],
+        return => {
+            desc => "Authentication seed or failure event",
+            type => "mixed"
+        }
+    }
+);
+sub login {
+    my ( $self, $conn, $args ) = @_;
+
+    return OpenILS::Event->new( 'LOGIN_FAILED' )
+      unless (&enabled() and ($args->{'username'} or $args->{'barcode'}));
+
+    my @error_events;
+    my $authenticated = 0;
+    my $auths;
+
+    # if they specify an authenticator by name, only try that one
+    if ($args->{'name'}) {
+        $auths = [$authenticators_by_name{$args->{'name'}}];
+    } else {
+        $auths = \@authenticators;
+    }
+
+    foreach my $authenticator (@$auths) {
+        # skip authenticators specified for a different login type
+        # or org unit id
+        if ($authenticator->login_types and $args->{'type'}) {
+            next unless grep(/^(all|$args->{'type'})$/, @{$authenticator->{'login_types'}});
+        }
+        if ($authenticator->org_units and $args->{'org'}) {
+            next unless grep(/^(all|$args->{'org'})$/, @{$authenticator->{'org_units'}});
+        }
+
+        my $event;
+        # treat native specially
+        if ($authenticator->name eq 'native') {
+            $event = &_do_login($args);
+        } else {
+            $event = $authenticator->authenticate($args);
+        }
+        my $code = $U->event_code($event);
+        if ($code) {
+            push @error_events, $event;
+        } elsif (defined $code) { # code is '0', i.e. SUCCESS
+            if (exists $event->{'payload'}) { # we have a complete native login
+                return $event;
+            } else { # do a 'forced' login
+                return &_do_login($args, 1);
+            }
+        }
+    }
+
+    # if we got this far, we failed
+    # TODO: send back some form of collected error events
+    return OpenILS::Event->new( 'LOGIN_FAILED' );
+}
+
+sub _do_login {
+    my $args = shift;
+    my $authenticated = shift;
+
+    my $seeder = $args->{'username'} ? $args->{'username'} : $args->{'barcode'};
+    my $seed =
+      OpenSRF::AppSession->create("open-ils.auth")
+      ->request( 'open-ils.auth.authenticate.init', $seeder )->gather(1);
+
+    return OpenILS::Event->new( 'LOGIN_FAILED' )
+      unless $seed;
+
+    my $real_password = $args->{'password'};
+    # if we have already authenticated, look up the password needed to finish
+    if ($authenticated) {
+        # barcode-based login is supported only for 'native' logins
+        return OpenILS::Event->new( 'LOGIN_FAILED' ) if !$args->{'username'};
+        my $user = $U->cstorereq(
+            "open-ils.cstore.direct.actor.user.search.atomic",
+            { usrname => $args->{'username'} }
+        );
+        $args->{'password'} = md5_hex( $seed . $user->[0]->passwd );
+    } else {
+        $args->{'password'} = md5_hex( $seed . md5_hex($real_password) );
+    }
+    my $response = OpenSRF::AppSession->create("open-ils.auth")->request(
+        'open-ils.auth.authenticate.complete',
+        $args
+    )->gather(1);
+    $args->{'password'} = $real_password;
+
+    return OpenILS::Event->new( 'LOGIN_FAILED' )
+      unless $response;
+
+    return $response;
+}
+
+__PACKAGE__->register_method(
+    method    => "authenticators",
+    api_name  => "open-ils.auth_proxy.authenticators",
+    api_level => 1,
+    stream    => 1,
+    argc      => 1,
+    signature => {
+        desc => q/Get a list of viable authenticators/,
+        params => [
+            {name=> "args", desc => q/A hash of arguments.  Valid keys and their meanings:
+    type     := Type of login being attempted (Staff Client, OPAC, etc.).
+    org      := Org unit id
+/,
+                type => "hash"}
+        ],
+        return => {
+            desc => "List of viable authenticators",
+            type => "array"
+        }
+    }
+);
+sub authenticators {
+    my ( $self, $conn, $args ) = @_;
+
+    my @viable_auths;
+
+    foreach my $authenticator (@authenticators) {
+        # skip authenticators specified for a different login type
+        # or org unit id
+        if ($authenticator->login_types and $args->{'type'}) {
+            next unless grep(/^(all|$args->{'type'})$/, @{$authenticator->login_types});
+        }
+        if ($authenticator->org_units and $args->{'org'}) {
+            next unless grep(/^(all|$args->{'org'})$/, @{$authenticator->org_units});
+        }
+
+        push @viable_auths, $authenticator->name;
+    }
+
+    return \@viable_auths;
+}
+
+
+# --------------------------------------------------------------------------
+# Stub package for 'native' authenticator
+# --------------------------------------------------------------------------
+package OpenILS::Application::AuthProxy::Native;
+use strict; use warnings;
+use base 'OpenILS::Application::AuthProxy::AuthBase';
+
+1;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/AuthBase.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/AuthBase.pm
new file mode 100644 (file)
index 0000000..338a7eb
--- /dev/null
@@ -0,0 +1,48 @@
+package OpenILS::Application::AuthProxy::AuthBase;
+use strict;
+use warnings;
+use vars '$AUTOLOAD';
+use OpenSRF::Utils::Logger qw(:logger);
+
+sub new {
+    my( $class, $args ) = @_;
+    $class = ref $class || $class;
+    return bless($args, $class);
+}
+
+# --------------------------------------------------------------------------
+# Add automatic getter/setter methods
+# --------------------------------------------------------------------------
+my @AUTOLOAD_FIELDS = qw/
+    name
+    org_units
+    login_types
+/;
+sub AUTOLOAD {
+    my $self = shift;
+    my $type = ref($self) or die "$self is not an object";
+    my $data = shift;
+    my $name = $AUTOLOAD;
+    $name =~ s/.*://o;
+
+    # return immediately if called as the DESTROY method
+    return if $name eq 'DESTROY';
+
+    unless (grep { $_ eq $name } @AUTOLOAD_FIELDS) {
+        $logger->error("$type: invalid autoload field: $name");
+        die "$type: invalid autoload field: $name\n"
+    }
+
+    {
+        no strict 'refs';
+        *{"${type}::${name}"} = sub {
+            my $s = shift;
+            my $v = shift;
+            $s->{$name} = $v if defined $v;
+            return $s->{$name};
+        }
+    }
+    return $self->$name($data);
+}
+
+1;
diff --git a/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth.pm b/Open-ILS/src/perlmods/lib/OpenILS/Application/AuthProxy/LDAP_Auth.pm
new file mode 100644 (file)
index 0000000..0a4a0b0
--- /dev/null
@@ -0,0 +1,77 @@
+package OpenILS::Application::AuthProxy::LDAP_Auth;
+use strict;
+use warnings;
+use base 'OpenILS::Application::AuthProxy::AuthBase';
+use OpenILS::Event;
+use Net::LDAP;
+use OpenSRF::Utils::SettingsClient;
+use OpenSRF::Utils::Logger qw(:logger);
+
+# default config var (override in configuration xml)
+my $id_attr = 'uid';
+
+sub authenticate {
+    my ( $self, $args ) = @_;
+    my $username = $args->{'username'};
+    my $password = $args->{'password'};
+
+    if (!$username) {
+        $logger->debug("User login failed: No username provided");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    }
+    if (!$password) {
+        $logger->debug("User login failed: No password provided");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    }
+
+    my $hostname_is_ldap = 0;
+    my $reached_ldap     = 0;
+    my $user_in_ldap     = 0;
+    my $login_succeeded  = 0;
+
+    my $hostname    = $self->{'hostname'};
+    my $basedn      = $self->{'basedn'};
+    my $authid      = $self->{'authid'};
+    my $authid_pass = $self->{'password'};
+    $id_attr        = $self->{'id_attr'} || $id_attr;
+
+    my $ldap;
+    if ( $ldap = Net::LDAP->new($hostname) ) {
+        $hostname_is_ldap = 1;
+        if ( $ldap->bind( $authid, password => $authid_pass )->code == 0 ) {
+            $reached_ldap = 1;
+            # verify username
+            if ( $ldap
+                ->search( base => $basedn, filter => "($id_attr=$username)" )
+                ->count != 0 ) {
+                $user_in_ldap = 1;
+
+                # verify password (bind check)
+                my $binddn = "$id_attr=$username,$basedn";
+                if ( $ldap->bind( $binddn, password => $password )
+                    ->code == 0 ) {
+                    $login_succeeded = 1;
+                }
+            }
+        }
+    }
+
+    if ( $login_succeeded ) {
+        return OpenILS::Event->new('SUCCESS');
+    } elsif ( !$hostname_is_ldap ) {
+        # TODO: custom failure events?
+        $logger->debug("User login failed: Incorrect LDAP hostname");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    } elsif ( !$reached_ldap ) {
+        $logger->debug("User login failed: The LDAP server is misconfigured or unavailable");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    } elsif ( !$user_in_ldap ) {
+        $logger->debug("User login failed: Username $username not in LDAP");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    } else {
+        $logger->debug("User login failed: Incorrect LDAP password");
+        return OpenILS::Event->new( 'LOGIN_FAILED' );
+    }
+}
+
+1;
index 6c87b73..c70bbae 100644 (file)
@@ -391,6 +391,8 @@ var FETCH_BIB_IDS_BY_BARCODE = 'open-ils.search:open-ils.search.multi_home.bib_i
 var FETCH_ORG_SETTING = 'open-ils.actor:open-ils.actor.ou_setting.ancestor_default';
 var TEST_PEER_BIBS                             = 'open-ils.search:open-ils.search.peer_bibs.test';
 var FETCH_PEER_BIBS                            = 'open-ils.search:open-ils.search.peer_bibs';
+var AUTH_PROXY_ENABLED                 = 'open-ils.auth_proxy:open-ils.auth_proxy.enabled';
+var AUTH_PROXY_LOGIN                   = 'open-ils.auth_proxy:open-ils.auth_proxy.login';
 
 /* ---------------------------------------------------------------------------- */
 
index 9ad1ed8..b25b994 100644 (file)
@@ -44,7 +44,20 @@ function init() {
                }
        }
 
-       runEvt("common", "run");
+       // show_login trumps normal page running
+       if(location.href.match(/&show_login=1/)) {
+               function reload() {
+                       var src = location.href.replace(/&show_login=1/, '');
+                       // forceLoginSSL setting (indicated by show_login)
+                       // assumes we are not SSL on normal pages
+                       src = src.replace(/https:/, 'http:');
+                       goTo(src);
+               }
+               attachEvt("common", "loginCanceled", reload);
+               initLogin();
+       } else {
+               runEvt("common", "run");
+       }
        //checkUserSkin();
 
        var loc = findOrgLasso(getLasso());
index bf1f3d6..801360d 100644 (file)
@@ -712,17 +712,16 @@ function doLogin(suppressEvents) {
 
        abortAllRequests();
 
-       var uname = G.ui.login.username.value;
-       var passwd = G.ui.login.password.value; 
-
-       var init_request = new Request( LOGIN_INIT, uname );
-   init_request.send(true);
-   var seed = init_request.result();
+       var auth_proxy_enabled = false;
+       var auth_proxy_enabled_request = new Request( AUTH_PROXY_ENABLED );
+       auth_proxy_enabled_request.request.alertEvent = false;
+       auth_proxy_enabled_request.send(true);
+       if (auth_proxy_enabled_request.result() == 1) {
+               auth_proxy_enabled = true;
+       }
 
-   if( ! seed || seed == '0') {
-      alert( "Error Communicating with Authentication Server" );
-      return null;
-   }
+       var uname = G.ui.login.username.value;
+       var passwd = G.ui.login.password.value;
 
        var args = {
                password : hex_md5(seed + hex_md5(passwd)), 
@@ -731,13 +730,29 @@ function doLogin(suppressEvents) {
                agent : 'opac'
        };
 
-    r = fetchOrgSettingDefault(globalOrgTree.id(), 'opac.barcode_regex');
-    if(r) REGEX_BARCODE = new RegExp(r);
-    
-    if( uname.match(REGEX_BARCODE) ) args.barcode = uname;
+       r = fetchOrgSettingDefault(globalOrgTree.id(), 'opac.barcode_regex');
+       if(r) REGEX_BARCODE = new RegExp(r);
+
+       if( uname.match(REGEX_BARCODE) ) args.barcode = uname;
        else args.username = uname;
 
-   var auth_request = new Request( LOGIN_COMPLETE, args );
+       var auth_request;
+       if (!auth_proxy_enabled) {
+               var init_request = new Request( LOGIN_INIT, uname );
+               init_request.send(true);
+               var seed = init_request.result();
+
+               if( ! seed || seed == '0') {
+                       alert( "Error Communicating with Authentication Server" );
+                       return null;
+               }
+
+               args.password = hex_md5(seed + hex_md5(passwd));
+               auth_request = new Request( LOGIN_COMPLETE, args );
+       } else {
+               args.password = passwd;
+               auth_request = new Request( AUTH_PROXY_LOGIN, args );
+       }
 
        auth_request.request.alertEvent = false;
    auth_request.send(true);
index aeae80d..444d9cf 100644 (file)
@@ -70,8 +70,16 @@ function userPressedEnter(evt) {
    return false;
 }   
 
-
+/* Using setTimeout in the following function means that goTo is threaded,
+   and multiple calls to it will be processed indeterminately.  Since goTo
+   should effectively end the page, we will only honor the first call. */
+var goToHasRun = false;
 function goTo(url) {
+       if (goToHasRun) {
+               return false;
+       }
+
+       goToHasRun = true;
        /* setTimeout because ie sux */
        setTimeout( function(){ location.href = url; }, 0 );
 }
index ee77624..c4f1616 100644 (file)
@@ -46,7 +46,7 @@ var rdetailEnd = null;
 var mfhdDetails = [];
 var orgHiding = false;
 
-if(location.href.match(/&place_hold=1/)) {
+if(location.href.match(/&place_hold=1/) || location.href.match(/&show_login=1/)) {
     // prevent load flicker between canvases
     hideMe(dojo.byId('canvas_main'));
 }
index 638fc79..02d18ac 100644 (file)
@@ -83,11 +83,13 @@ function loginDance() {
 }
 
 function loggedInOK() {
-       showCanvas();
-       G.ui.sidebar.username_dest.appendChild(text(G.user.usrname()));
-       unHideMe(G.ui.sidebar.logoutbox);
-       unHideMe(G.ui.sidebar.logged_in_as);
-       hideMe(G.ui.sidebar.loginbox);
+       if (!location.href.match(/&show_login=1/)) {
+               showCanvas();
+               G.ui.sidebar.username_dest.appendChild(text(G.user.usrname()));
+               unHideMe(G.ui.sidebar.logoutbox);
+               unHideMe(G.ui.sidebar.logged_in_as);
+               hideMe(G.ui.sidebar.loginbox);
+       }
        runEvt( 'common', 'loggedIn');
        
        var org = G.user.prefs[PREF_DEF_LOCATION];
@@ -98,6 +100,11 @@ function loggedInOK() {
                depth = findOrgDepth(org);
 
        runEvt( "common", "locationChanged", org, depth);
+       if (location.href.match(/&show_login=1/)) {
+               // this redirect should only happen if the runEvt above didn't already
+               // trigger one
+               goTo(location.href.replace(/&show_login=1/, ''));
+       }
 }
 
 
@@ -157,6 +164,16 @@ function strongPassword(pass, alrt) {
 }
 
 function initLogin() {
+    var src = location.href;
+    if(forceLoginSSL && src.match(/^http:/)) {
+        src = src.replace(/^http:/, 'https:');
+        if(!src.match(/&show_login=1/)) {
+            src += '&show_login=1';
+        }
+        goTo(src);
+        return false;
+    }
+
     swapCanvas(G.ui.login.box);
     try{G.ui.login.username.focus();} catch(e) {}
 
index c4f0ae8..4b560b0 100644 (file)
@@ -18,25 +18,36 @@ auth.session.prototype = {
 
         var obj = this;
 
+        /* This request is done manually in a try block to allow it to fail
+         * silently if auth_proxy is not even running.  TODO: Move this check
+         * to a module which should be always running, perhaps 'auth'.
+         */
+        var auth_proxy_enabled = false;
         try {
-            var init = this.network.request(
-                api.AUTH_INIT.app,
-                api.AUTH_INIT.method,
-                [ this.view.name_prompt.value ]
-            );
+            var request = new RemoteRequest( api.AUTH_PROXY_ENABLED.app, api.AUTH_PROXY_ENABLED.method );
+            request.send(true);
+            request.setSecure(true);
+            if (request.getResultObject() == 1) {
+                auth_proxy_enabled = true;
+            }
+        } catch(E) {
+        }
+
+        try {
+            if (!auth_proxy_enabled) {
+                var init = this.network.request(
+                    api.AUTH_INIT.app,
+                    api.AUTH_INIT.method,
+                    [ this.view.name_prompt.value ]
+                );
+            }
 
-            if (init) {
+            if (init || auth_proxy_enabled) {
                 if (xulG._data) { delete xulG._data; } // quick kludge; we were re-using a poisoned OpenILS.data (from ws_info.xul?) where js2JSON (and maybe other stuff) does not exist
                 JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
 
                 var params = { 
                     'username' : this.view.name_prompt.value,
-                    'password' : hex_md5(
-                        init +
-                        hex_md5(
-                            this.view.password_prompt.value
-                        )
-                    ),
                     'type' : 'temp',
                     'agent' : 'staffclient'
                 };
@@ -47,7 +58,19 @@ auth.session.prototype = {
                     data.ws_name = params.workstation; data.stash('ws_name');
                 }
 
-                var robj = this.network.simple_request( 'AUTH_COMPLETE', [ params ]);
+                var robj;
+                if (init) {
+                    params['password'] = hex_md5(
+                        init +
+                        hex_md5(
+                            this.view.password_prompt.value
+                        )
+                    );
+                    robj = this.network.simple_request( 'AUTH_COMPLETE', [ params ]);
+                } else if (auth_proxy_enabled) { // safety double-check
+                    params['password'] = this.view.password_prompt.value;
+                    robj = this.network.simple_request( 'AUTH_PROXY_LOGIN', [ params ] );
+                }
 
                 switch (Number(robj.ilsevent)) {
                     case 0:
index 4120b6f..eea83a4 100644 (file)
@@ -64,6 +64,8 @@ var api = {
     'AUTH_INIT' : { 'app' : 'open-ils.auth', 'method' : 'open-ils.auth.authenticate.init' },
     'AUTH_COMPLETE' : { 'app' : 'open-ils.auth', 'method' : 'open-ils.auth.authenticate.complete' },
     'AUTH_DELETE' : { 'app' : 'open-ils.auth', 'method' : 'open-ils.auth.session.delete' },
+    'AUTH_PROXY_ENABLED' : { 'app' : 'open-ils.auth_proxy', 'method' : 'open-ils.auth_proxy.enabled' },
+    'AUTH_PROXY_LOGIN' : { 'app' : 'open-ils.auth_proxy', 'method' : 'open-ils.auth_proxy.login' },
     'AUTH_WORKSTATION' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.workstation.register' },
     'AUTH_VERIFY_CREDENTIALS' : { 'app' : 'open-ils.actor', 'method' : 'open-ils.actor.verify_user_password' },
     'AUTOGENERATE_BARCODES' : { 'app' : 'open-ils.cat', 'method' : 'open-ils.cat.item.barcode.autogen' },