use CGI;
use DateTime;
use DateTime::Format::ISO8601;
-use OpenSRF::Utils::JSON;
+use JSON::XS;
use OpenSRF::Utils::Cache;
use OpenSRF::System;
-use OpenSRF::Utils::SettingsClient;
use OpenILS::Utils::CStoreEditor q/:funcs/;
use OpenSRF::Utils::Logger q/$logger/;
use OpenILS::Application::AppUtils;
use OpenILS::Utils::DateTime qw/:datetime/;
my $U = 'OpenILS::Application::AppUtils';
-
-# TODO: config file which maps SIP logins to ILS logins and
-# applies various flags.
-
-my $bs_config;
+my $cache;
+
+my $json = JSON::XS->new;
+$json->ascii(1);
+$json->allow_nonref(1);
+
+my $config = { # TODO: move to external config / database settings
+ options => {
+ # Allow 99 (sc status) message before successful 93 (login) message
+ allow_sc_status_before_login => 1
+ },
+ accounts => [{
+ sip_username => 'sip',
+ sip_password => 'sip',
+ ils_username => 'admin',
+ ils_password => 'demo123',
+ ils_workstation => 'BR1-gamma'
+ }],
+ institutions => [{
+ id => 'example',
+ supports => [ # Supported Messages (BX)
+ 'Y', # patron status request,
+ 'Y', # checkout,
+ 'Y', # checkin,
+ 'N', # block patron,
+ 'Y', # acs status,
+ 'N', # request sc/acs resend,
+ 'Y', # login,
+ 'Y', # patron information,
+ 'N', # end patron session,
+ 'Y', # fee paid,
+ 'Y', # item information,
+ 'N', # item status update,
+ 'N', # patron enable,
+ 'N', # hold,
+ 'Y', # renew,
+ 'N', # renew all,
+ ],
+ options => {}
+ }]
+};
+
+my $osrf_config;
sub import {
- $bs_config = shift;
+ $osrf_config = shift;
+ warn "OSRF CONFIG IS $osrf_config\n";
}
my $init_complete = 0;
-sub child_init {
+sub init {
+ return if $init_complete;
$init_complete = 1;
- OpenSRF::System->bootstrap_client(config_file => $bs_config);
+ OpenSRF::System->bootstrap_client(config_file => $osrf_config);
OpenILS::Utils::CStoreEditor->init;
+ $cache = OpenSRF::Utils::Cache->new;
+
return Apache2::Const::OK;
}
my $r = shift;
my $cgi = CGI->new;
- child_init() unless $init_complete;
+ init();
- my $session = $cgi->param('session');
- my $message = OpenSRF::Utils::JSON->JSON2perl($cgi->param('message'));
+ my $seskey = $cgi->param('session');
+ my $message = $json->decode($cgi->param('message'));
my $msg_code = $message->{code};
my $response;
if ($msg_code eq '93') {
- $response = handle_login($session, $message);
+ $response = handle_login($seskey, $message);
} elsif ($msg_code eq '99') {
- $response = handle_sc_status($session, $message);
+ $response = handle_sc_status($seskey, $message);
}
unless ($response) {
}
$r->content_type('application/json');
- $r->print(OpenSRF::Utils::JSON->perl2JSON($response));
+ $r->print($json->encode($response));
return Apache2::Const::OK;
}
-sub handle_login {
- my ($session, $message) = @_;
+# Returns the value of the first occurrence of the requested field by SIP code.
+sub get_field_value {
+ my ($message, $code) = @_;
+ for my $field (@{$message->{fields}}) {
+ my ($c, $v) = each(%$field); # one key/value pair per field
+ return $v if $c eq $code;
+ }
- # TODO: login and cache the authtoken vis the $session
+ return undef;
+}
+
+sub get_auth_account {
+ my ($seskey) = @_;
+ my $account = $cache->get_cache("sip2_$seskey");
+
+ if ($account) {
+ return $account->{authtoken};
+ } else {
+ $logger->info("SIP2 no cached session for seskey=$seskey");
+ return undef;
+ }
+}
+
+# Logs in to Evergreen and caches the authtoken with the SIP account.
+# Returns true on success, false on failure to authenticate.
+sub set_auth_token {
+ my ($seskey, $account) = @_;
+
+ my $auth = $U->simplereq(
+ 'open-ils.auth',
+ 'open-ils.auth.login', {
+ username => $account->{ils_username},
+ password => $account->{ils_password},
+ workstation => $account->{ils_workstation},
+ type => 'staff'
+ });
+
+ if ($auth->{textcode} eq 'SUCCESS') {
+ $account->{authtoken} = $auth->{payload}->{authtoken};
+ $cache->put_cache("sip2_$seskey", $account);
+ return 1;
+
+ } else {
+ $logger->warn("SIP2 login failed for ils_username".$account->{ils_username});
+ return 0;
+ }
+}
+
+sub handle_login {
+ my ($seskey, $message) = @_;
my $response = {
code => '94',
- fixed_fields => ['1']
+ fixed_fields => ['0'] # default to login failed.
};
+ my $sip_username = get_field_value($message, 'CN');
+ my $sip_password = get_field_value($message, 'CO');
+
+ my ($account) = grep {
+ $_->{sip_username} eq $sip_username &&
+ $_->{sip_password} eq $sip_password
+ } @{$config->{accounts}};
+
+ if ($account) {
+ $response->{fixed_fields}->[0] = '1'
+ if set_auth_token($seskey, $account);
+
+ } else {
+ $logger->info("SIP2 login failed for user=$sip_username")
+ }
+
return $response;
}
+# NOTE: response should be modified as message handlers are implemented.
sub handle_sc_status {
- my ($session, $message) = @_;
+ my ($seskey, $message) = @_;
+
+ return undef unless (
+ $config->{options}->{allow_sc_status_before_login} ||
+ get_auth_account($seskey)
+ );
- # TODO: If we don't want to allow sc_status requests without
- # authentication, check for the authtoken.
+ # The SC Status message does not include an institution, but expects
+ # one in return. Use the configuration for the first institution.
+ # Maybe the SIP server itself should track which institutoin its
+ # instance is configured to use. That may multiple servers could,
+ # one per institution.
+ my $instconf = $config->{institutions}->[0];
+ my $instname = $instconf->{id};
my $response = {
code => '98',
fixed_fields => [
'Y', # online_status
- 'N', # checkin_ok
- 'N', # checkout_ok
- 'N', # acs_renewal_policy
+ 'Y', # checkin_ok
+ 'Y', # checkout_ok
+ 'Y', # acs_renewal_policy
'N', # status_update_ok
'N', # offline_ok
'999', # timeout_period
'999', # retries_allowed
sipdate(), # transaction date
'2.00' # protocol_version
+ ],
+ fields => [
+ {AO => $instname},
+ {BX => join('', @{$instconf->{supports}})}
]
}
}