lemonldap-ng/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/Main/Issuer.pm

311 lines
8.0 KiB
Perl
Raw Normal View History

2016-06-10 13:50:37 +02:00
# Base package for simple issuers plugins
#
# Issuer should just implement a run() method that will be called only for
# authenticated users when PATH_INFO starts with issuerDBXXPath
#
# run() should just return a Lemonldap::NG::Portal::Main::Constants value. It
# is called using process() method (Lemonldap::NG::Portal::Main::Process)
package Lemonldap::NG::Portal::Main::Issuer;
use strict;
use Mouse;
2017-03-26 07:26:25 +02:00
use MIME::Base64;
use IO::String;
2017-03-23 21:49:52 +01:00
use Lemonldap::NG::Portal::Main::Constants qw(
PE_OK
PE_RENEWSESSION
2018-05-16 11:06:25 +02:00
PE_SENDRESPONSE
2017-03-23 21:49:52 +01:00
);
2016-06-10 13:50:37 +02:00
extends 'Lemonldap::NG::Portal::Main::Plugin';
our $VERSION = '2.0.0';
# PROPERTIES
has type => ( is => 'rw' );
2016-12-21 23:39:12 +01:00
has path => ( is => 'rw' );
has _ott => (
is => 'rw',
lazy => 1,
default => sub {
my $ott = $_[0]->{p}->loadModule('::Lib::OneTimeToken');
$ott->timeout( $_[0]->{conf}->{formTimeout} );
return $ott;
}
);
2016-06-12 18:52:37 +02:00
# INTERFACE
# Only logout is called in normal use. Issuer that inherits from this
# package are called only by their path
sub beforeLogout { 'logout' }
2016-06-10 13:50:37 +02:00
# INITIALIZATION
sub init {
my ($self) = @_;
my $type = ref( $_[0] );
$type =~ s/.*:://;
$self->type($type);
if ( my $path = $self->conf->{"issuerDB${type}Path"} ) {
2016-06-12 21:26:14 +02:00
$path =~ s/^.*?(\w+).*?$/$1/;
2016-12-21 23:39:12 +01:00
$self->path($path);
2017-02-15 07:41:50 +01:00
$self->addUnauthRoute(
$path => { '*' => '_redirect' },
[ 'GET', 'POST' ]
);
$self->addAuthRoute(
$path => { '*' => "_forAuthUser" },
[ 'GET', 'POST' ]
);
2016-06-10 13:50:37 +02:00
}
else {
2017-02-15 07:41:50 +01:00
$self->logger->debug("No path declared for issuer $type. Skipping");
2016-06-10 13:50:37 +02:00
}
}
# RUNNING METHODS
# Case 1: Unauthentified users are redirected to the main portal
sub _redirect {
2016-12-21 19:06:23 +01:00
my ( $self, $req, @path ) = @_;
2017-02-15 07:41:50 +01:00
$self->logger->debug('Processing _redirect');
$self->logger->debug('Store issuer request');
2017-03-21 17:06:44 +01:00
my $ir =
$req->param( 'issuerRequest' . $self->path ) || $self->storeRequest($req);
$self->p->setHiddenFormValue( $req, 'issuerRequest' . $self->path,
2017-03-23 21:49:52 +01:00
$ir, '' );
$req->{urldc} = $self->conf->{portal};
$req->{urldc} =~ s#/*$##;
$req->{urldc} .= $req->path . "?issuerRequest$self->{path}=$ir";
$self->p->setHiddenFormValue( $req, 'issuerUrldc', $req->urldc, '', 0 );
2017-03-21 08:59:54 +01:00
if ( my $t = $req->param( 'issuerRequest' . $self->path ) ) {
$ir = $t;
2016-11-28 22:15:57 +01:00
}
2016-06-12 21:38:02 +02:00
2016-06-12 18:52:37 +02:00
# TODO: launch normal process with 'run' at the end
2016-06-12 21:38:02 +02:00
return $self->p->do(
$req,
[
'controlUrl',
@{ $self->p->beforeAuth },
$self->p->authProcess,
@{ $self->p->betweenAuthAndDatas },
$self->p->sessionDatas,
@{ $self->p->afterDatas },
2016-06-12 18:52:37 +02:00
sub {
# Restore urldc if auth doesn't need to dial with browser
$self->restoreRequest( $req, $ir );
return $self->run( @_, @path );
2018-03-13 07:14:01 +01:00
}
2016-06-12 21:38:02 +02:00
]
);
2016-06-10 13:50:37 +02:00
}
# Case 3: authentified user, launch
sub _forAuthUser {
my ( $self, $req, @path ) = @_;
2017-02-15 07:41:50 +01:00
$self->logger->debug('Processing _forAuthUser');
2017-03-21 08:59:54 +01:00
if ( my $r = $req->param( 'issuerRequest' . $self->path ) ) {
$self->restoreRequest( $req, $r );
}
2017-10-26 22:21:15 +02:00
$req->urlNotBase64(1) if ( ref($self) =~ /::CAS$/ );
2016-06-10 13:50:37 +02:00
return $self->p->do(
$req,
[
'importHandlerDatas',
'controlUrl',
@{ $self->p->forAuthUser },
sub {
return $self->run( @_, @path );
2016-06-10 13:50:37 +02:00
},
]
);
}
sub storeRequest {
my ( $self, $req ) = @_;
my $info = {};
$info->{content} = $req->content;
foreach ( keys %{ $req->env } ) {
$info->{$_} = $req->env->{$_} unless ( ref $req->env->{$_} );
}
return $self->_ott->createToken($info);
}
sub restoreRequest {
my ( $self, $req, $token ) = @_;
my $env = $self->_ott->getToken($token);
if ($env) {
$self->logger->debug("Restoring request from $token");
if ( my $c = delete $env->{content} ) {
$env->{'psgix.input.buffered'} = 0;
$env->{'psgi.input'} = IO::String->new($c);
}
$req->{env} = {};
2017-03-21 08:59:54 +01:00
foreach ( keys %$env ) {
$self->logger->debug("Restore $_");
2017-03-21 08:59:54 +01:00
$req->env->{$_} = $env->{$_} unless /^plack/;
}
}
return $req;
}
2017-03-23 21:49:52 +01:00
sub reAuth {
my ( $self, $req ) = @_;
my $url =
2017-03-26 07:26:25 +02:00
$self->conf->{portal}
. $req->path_info
. '?issuerRequest'
. $self->path . '='
. $self->storeRequest($req);
if ( $self->conf->{skipRenewConfirmation} ) {
$req->response( [ 302, [ Location => $url ], [] ] );
return PE_SENDRESPONSE;
}
$req->datas->{_url} = encode_base64( $url, '' );
2017-03-23 21:49:52 +01:00
return PE_RENEWSESSION;
}
2016-06-10 13:50:37 +02:00
1;
2016-12-19 17:15:31 +01:00
__END__
=pod
=encoding utf8
=head1 NAME
Lemonldap::NG::Portal::Main::Issuer - Base class for identity providers.
=head1 SYNOPSIS
package Lemonldap::NG::Portal::Issuer::My;
use strict;
use Mouse;
extends 'Lemonldap::NG::Portal::Main::Issuer';
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK);
# Optional initialization method
sub init {
my ($self) = @_;
...
# Must return 1 (succeed) or 0 (failure)
}
# Required methods are run() and logout(), they are launched only for
# authenticated users
# $req is a Lemonldap::NG::Portal::Main::Request object
# They must return a Lemonldap::NG::Portal::Main::Constants constant
sub run {
my ( $self, $req ) = @_
...
return PE_OK
}
sub logout {
my ( $self, $req ) = @_
...
return PE_OK
}
1;
=head1 DESCRIPTION
Lemonldap::NG::Portal::Main::Issuer is a base class to write identity providers
for Lemonldap::NG web-SSO system. It provide several methods to write easily
an IdP and manage authentication if the identity request comes before
authentication.
=head1 WRITING AN IDENTITY PROVIDER
To write a classic identity provider, you just have to inherit this class and
write run() and logout() methods. These methods must return a
Lemonldap::NG::Portal::Main::Constants constant.
A classic identity provider needs a "issuerDBE<gt>XXXE<lt>Path" parameter in
LLNG configuration to declare its base URI path (see
L<Lemonldap::NG::Manager::Build>). Example: /saml/. All requests that starts
2016-12-19 21:51:51 +01:00
with /saml/ will call run() after authentication if needed, and no one else.
2016-12-19 17:15:31 +01:00
The logout() function is called when user asks for logout on this server. If
you want to write an identity provider, you must implement a single logout
system.
=head2 managing other URI path
Lemonldap::NG::Portal::Main::Issuer provides methods to bind a method to an
URI path:
=over
=item addAuthRoute() for authenticated users
=item addUnauthRoute() for unauthenticated users
=back
They must be called during initialization process (so you must write the
optional init() sub).
Example:
sub init {
my ($self) = @_;
...
$self->addUnauthRoute( saml => { soap => 'soapServer' }, [ 'POST' ] );
return 1;
}
sub soapServer {
my ( $self, $req ) = @_;
...
# You must return a valid PSGI response
return [ 200, [ 'Content-Type' => 'application/xml' ], [] ];
}
=head1 SEE ALSO
L<http://lemonldap-ng.org/>
2017-01-04 21:51:46 +01:00
=head1 AUTHORS
2016-12-19 17:15:31 +01:00
=over
2017-01-04 21:51:46 +01:00
=item LemonLDAP::NG team L<http://lemonldap-ng.org/team>
2016-12-19 17:15:31 +01:00
=back
=head1 BUG REPORT
Use OW2 system to report bug or ask for features:
2017-11-11 14:06:23 +01:00
L<https://gitlab.ow2.org/lemonldap-ng/lemonldap-ng/issues>
2016-12-19 17:15:31 +01:00
=head1 DOWNLOAD
Lemonldap::NG is available at
L<http://forge.objectweb.org/project/showfiles.php?group_id=274>
=head1 COPYRIGHT AND LICENSE
2017-01-04 21:51:46 +01:00
See COPYING file for details.
2016-12-19 17:15:31 +01:00
This library 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, 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, see L<http://www.gnu.org/licenses/>.
=cut