From f03c59bf2d508137aae80a4d612da4e4df454412 Mon Sep 17 00:00:00 2001 From: Compass Date: Mon, 30 Jan 2012 13:08:29 -0800 Subject: [PATCH 1/2] Added Shibboleth authentication module --- lib/WeBWorK/Authen/Shibboleth.pm | 200 +++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) create mode 100644 lib/WeBWorK/Authen/Shibboleth.pm diff --git a/lib/WeBWorK/Authen/Shibboleth.pm b/lib/WeBWorK/Authen/Shibboleth.pm new file mode 100644 index 0000000000..a579c7fbcb --- /dev/null +++ b/lib/WeBWorK/Authen/Shibboleth.pm @@ -0,0 +1,200 @@ +################################################################################ +# WeBWorK Online Homework Delivery System +# Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of either: (a) the GNU General Public License as published by the +# Free Software Foundation; either version 2, or (at your option) any later +# version, or (b) the "Artistic License" which comes with this package. +# +# 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 either the GNU General Public License or the +# Artistic License for more details. +################################################################################ + +package WeBWorK::Authen::Shibboleth; +use base qw/WeBWorK::Authen/; + +=head1 NAME + +WeBWorK::Authen::Shibboleth - Authentication plug in for Shibboleth. +This is basd on Cosign.pm + +to use: include in global.conf or course.conf + $authen{user_module} = "WeBWorK::Authen::Shibboleth"; +and add /webwork2/courseName as a Shibboleth Protected +Location + +if $r->ce->{shiboff} is set for a course, authentication reverts +to standard WeBWorK authentication. + +add the following to global.conf to setup the Shibboleth + +$shibboleth{logout_script} = "/Shibboleth.sso/Logout"?return=".$server_root_url.$webwork_url; # return URL after logout +$shibboleth{session_header} = "Shib-Session-ID"; # the header to identify if there is an existing shibboleth session +$shibboleth{manage_session_timeout} = 1; # allow shib to manage session time instead of webwork +$shibboleth{hash_user_id_method} = "MD5"; # possible values none, MD5. Use it when you want to hide real user_ids from showing in url. +$shibboleth{hash_user_id_salt} = ""; # salt for hash function +#define mapping between shib and webwork +$shibboleth{mapping}{user_id} = "username"; + +=cut + +use strict; +use warnings; +use WeBWorK::Debug; +use Data::Dumper; + +# this is similar to the method in the base class, except that Shibboleth +# ensures that we don't get to the address without a login. this means +# that we can't allow guest logins, but don't have to do any password +# checking or cookie management. + +sub get_credentials { + my ($self) = @_; + my $r = $self->{r}; + my $ce = $r->ce; + my $db = $r->db; + + if ( $ce->{shiboff} || $r->param('bypassShib')) { + return $self->SUPER::get_credentials( @_ ); + } else { + debug("Shib is on!"); + + # set external auth parameter so that Login.pm knows + # not to rely on internal logins if there's a check_user + # failure. + $self->{external_auth} = 1; + + if ( $r->param("user") && ! $r->param("force_passwd_authen") ) { + return $self->SUPER::get_credentials( @_ ); + } + + if ( defined ($ENV{$ce->{shibboleth}{session_header}}) && defined( $ENV{$ce->{shibboleth}{mapping}{user_id}} ) ) { + debug('Got shib header and user_id'); + my $user_id = $ENV{$ce->{shibboleth}{mapping}{user_id}}; + if ( defined ($ce->{shibboleth}{hash_user_id_method}) && + $ce->{shibboleth}{hash_user_id_method} ne "none" && + $ce->{shibboleth}{hash_user_id_method} ne "" ) { + use Digest; + my $digest = Digest->new($ce->{shibboleth}{hash_user_id_method}); + $digest->add(uc($user_id). ( defined $ce->{shibboleth}{hash_user_id_salt} ? $ce->{shibboleth}{hash_user_id_salt} : "")); + $user_id = $digest->hexdigest; + } + $self->{'user_id'} = $user_id; + $self->{r}->param("user", $user_id); + + # set external auth parameter so that login.pm + # knows not to rely on internal logins if + # there's a check_user failure. + $self->{session_key} = undef; + $self->{password} = "youwouldneverpickthispassword"; + $self->{login_type} = "normal"; + $self->{credential_source} = "params"; + } else { + debug("Couldn't shib header or user_id"); + return 0; + } + + # the session key isn't used (Shibboleth is managing this + # for us), and we want to force checking against the + # site_checkPassword + $self->{'session_key'} = undef; + $self->{'password'} = 1; + $self->{'credential_source'} = "params"; + + return 1; + } +} + +sub site_checkPassword { + my ( $self, $userID, $clearTextPassword ) = @_; + + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::checkPassword( @_ ); + } else { + # this is easy; if we're here at all, we've authenticated + # through shib + return 1; + } +} + +# disable cookie functionality +sub maybe_send_cookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::maybe_send_cookie( @_ ); + } else { + # nothing to do here + } +} +sub fetchCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::fetchCookie( @_ ); + } else { + # nothing to do here + } +} +sub sendCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::sendCookie( @_ ); + } else { + # nothing to do here + } +} +sub killCookie { + my ($self, @args) = @_; + if ( $self->{r}->ce->{shiboff} ) { + return $self->SUPER::killCookie( @_ ); + } else { + # nothing to do here + } +} + +# this is a bit of a cheat, because it does the redirect away from the +# logout script or what have you, but I don't see a way around that. +sub forget_verification { + my ($self, @args) = @_; + my $r = $self->{r}; + + if ( $r->ce->{shiboff} ) { + return $self->SUPER::forget_verification( @_ ); + } else { + $self->{was_verified} = 0; + $self->{redirect} = $r->ce->{shibboleth}{logout_script}; + } +} + +# returns ($sessionExists, $keyMatches, $timestampValid) +# if $updateTimestamp is true, the timestamp on a valid session is updated +# override function: allow shib to handle the session time out +sub check_session { + my ($self, $userID, $possibleKey, $updateTimestamp) = @_; + my $ce = $self->{r}->ce; + my $db = $self->{r}->db; + + if ( $ce->{shiboff} ) { + return $self->SUPER::check_session( @_ ); + } else { + my $Key = $db->getKey($userID); # checked + return 0 unless defined $Key; + + my $keyMatches = (defined $possibleKey and $possibleKey eq $Key->key); + my $timestampValid = (time <= $Key->timestamp()+$ce->{sessionKeyTimeout}); + if ($ce->{shibboleth}{manage_session_timeout}) { + # always valid to allow shib to take control of timeout + $timestampValid = 1; + } + + if ($keyMatches and $timestampValid and $updateTimestamp) { + $Key->timestamp(time); + $db->putKey($Key); + } + return (1, $keyMatches, $timestampValid); + } +} + +1; From 3a7c934af687dd7dffb24efaa4e6a849851d8c98 Mon Sep 17 00:00:00 2001 From: Compass Date: Wed, 1 Feb 2012 17:05:31 -0800 Subject: [PATCH 2/2] added quotes to mysql defaults file field in case of special characters are used in db username and password --- lib/WeBWorK/DB/Schema/NewSQL/Std.pm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/WeBWorK/DB/Schema/NewSQL/Std.pm b/lib/WeBWorK/DB/Schema/NewSQL/Std.pm index b022572873..3bb50b3204 100644 --- a/lib/WeBWorK/DB/Schema/NewSQL/Std.pm +++ b/lib/WeBWorK/DB/Schema/NewSQL/Std.pm @@ -278,10 +278,10 @@ sub _get_db_info { $my_cnf->unlink_on_destroy(1); chmod 0600, $my_cnf or die "failed to chmod 0600 $my_cnf: $!"; # File::Temp objects stringify with ->filename print $my_cnf "[client]\n"; - print $my_cnf "user=$username\n" if defined $username and length($username) > 0; - print $my_cnf "password=$password\n" if defined $password and length($password) > 0; - print $my_cnf "host=$dsn{host}\n" if defined $dsn{host} and length($dsn{host}) > 0; - print $my_cnf "port=$dsn{port}\n" if defined $dsn{port} and length($dsn{port}) > 0; + print $my_cnf "user=\"$username\"\n" if defined $username and length($username) > 0; + print $my_cnf "password=\"$password\"\n" if defined $password and length($password) > 0; + print $my_cnf "host=\"$dsn{host}\"\n" if defined $dsn{host} and length($dsn{host}) > 0; + print $my_cnf "port=\"$dsn{port}\"\n" if defined $dsn{port} and length($dsn{port}) > 0; return ($my_cnf, $dsn{database}); }