Add REST Auth/UserDB/Password backend (closes: #1174)
parent
18c9215724
commit
d7025a2251
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,61 @@ |
||||
package Lemonldap::NG::Portal::Auth::REST; |
||||
|
||||
use strict; |
||||
use Mouse; |
||||
use Lemonldap::NG::Portal::Main::Constants qw( |
||||
PE_ERROR |
||||
PE_BADCREDENTIALS |
||||
PE_OK |
||||
); |
||||
|
||||
our $VERSION = '2.0.0'; |
||||
|
||||
extends 'Lemonldap::NG::Portal::Auth::_WebForm', |
||||
'Lemonldap::NG::Portal::Lib::REST'; |
||||
|
||||
# INITIALIZATION |
||||
|
||||
sub init { |
||||
my $self = shift; |
||||
|
||||
# Add warning in log |
||||
unless ( $self->conf->{restAuthUrl} ) { |
||||
$self->logger->error('No Auth REST URL given'); |
||||
return 0; |
||||
} |
||||
|
||||
return $self->Lemonldap::NG::Portal::Auth::_WebForm::init(); |
||||
} |
||||
|
||||
sub authenticate { |
||||
my ( $self, $req ) = @_; |
||||
my $res = eval { |
||||
$self->restCall( $self->conf->{restAuthUrl}, |
||||
{ user => $req->user, password => $req->datas->{password} } ); |
||||
}; |
||||
if ($@) { |
||||
$self->logger("Auth error: $@"); |
||||
return PE_ERROR; |
||||
} |
||||
unless ( $res->{result} ) { |
||||
$self->userLogger->warn( |
||||
"Bad credentials for " . $req->user . ' (' . $req->address . ')' ); |
||||
return PE_BADCREDENTIALS; |
||||
} |
||||
$req->datas->{restAuthInfo} = $res->{info}; |
||||
return PE_OK; |
||||
} |
||||
|
||||
sub setAuthSessionInfo { |
||||
my ( $self, $req ) = @_; |
||||
$self->SUPER::setAuthSessionInfo($req); |
||||
$req->sessionInfo->{$_} = $req->datas->{restAuthInfo}->{$_} |
||||
foreach ( keys %{ $req->datas->{restAuthInfo} } ); |
||||
return PE_OK; |
||||
} |
||||
|
||||
sub authLogout { |
||||
PE_OK; |
||||
} |
||||
|
||||
1; |
@ -0,0 +1,29 @@ |
||||
package Lemonldap::NG::Portal::Lib::REST; |
||||
|
||||
use strict; |
||||
use Mouse; |
||||
use Lemonldap::NG::Common::UserAgent; |
||||
use JSON qw(from_json to_json); |
||||
|
||||
has ua => ( |
||||
is => 'rw', |
||||
default => sub { |
||||
return Lemonldap::NG::Common::UserAgent->new( $_[0]->{conf} ); |
||||
} |
||||
); |
||||
|
||||
sub restCall { |
||||
my($self,$url,$content) = @_; |
||||
my $hreq = HTTP::Request->new( POST => $url ); |
||||
$hreq->header( 'Content-Type' => 'application/json' ); |
||||
$hreq->content(to_json($content)); |
||||
my $resp = $self->ua->request($hreq); |
||||
unless ( $resp->is_success ) { |
||||
die $resp->status_line; |
||||
} |
||||
my $res = eval { from_json( $resp->content ) }; |
||||
die "Bad REST response: $@" if($@); |
||||
return $res; |
||||
} |
||||
|
||||
1; |
@ -0,0 +1,52 @@ |
||||
package Lemonldap::NG::Portal::Password::REST; |
||||
|
||||
use strict; |
||||
use Mouse; |
||||
use Lemonldap::NG::Portal::Main::Constants qw( |
||||
PE_ERROR |
||||
PE_PASSWORD_OK |
||||
); |
||||
|
||||
extends 'Lemonldap::NG::Portal::Password::Base', |
||||
'Lemonldap::NG::Portal::Lib::REST'; |
||||
|
||||
our $VERSION = '2.0.0'; |
||||
|
||||
sub init { |
||||
my ($self) = @_; |
||||
unless ($self->conf->{restPwdConfirmUrl} |
||||
and $self->conf->{restPwdModifyUrl} ) |
||||
{ |
||||
$self->logger->error('Missing REST password URL'); |
||||
return 0; |
||||
} |
||||
return $self->SUPER::init; |
||||
} |
||||
|
||||
sub confirm { |
||||
my ( $self, $req, $pwd ) = @_; |
||||
my $res = eval { |
||||
$self->restCall( $self->conf->{restPwdConfirmUrl}, |
||||
{ user => $req->user, password => $pwd } ); |
||||
}; |
||||
if ($@) { |
||||
$self->logger("Pwd confirm error: $@"); |
||||
return 0; |
||||
} |
||||
return ( $res->{result} ? 1 : 0 ); |
||||
} |
||||
|
||||
sub modifyPassword { |
||||
my ( $self, $req, $pwd ) = @_; |
||||
my $res = eval { |
||||
$self->restCall( $self->conf->{restPwdModifyUrl}, |
||||
{ user => $req->user, password => $pwd } ); |
||||
}; |
||||
if ($@) { |
||||
$self->logger("Pwd confirm error: $@"); |
||||
return PE_ERROR; |
||||
} |
||||
return ( $res->{result} ? PE_PASSWORD_OK : PE_ERROR ); |
||||
} |
||||
|
||||
1; |
@ -0,0 +1,58 @@ |
||||
package Lemonldap::NG::Portal::UserDB::REST; |
||||
|
||||
use strict; |
||||
use Mouse; |
||||
use Lemonldap::NG::Portal::Main::Constants qw( |
||||
PE_ERROR |
||||
PE_OK |
||||
PE_USERNOTFOUND |
||||
); |
||||
|
||||
extends 'Lemonldap::NG::Common::Module', 'Lemonldap::NG::Portal::Lib::REST'; |
||||
|
||||
our $VERSION = '2.0.0'; |
||||
|
||||
# INITIALIZATION |
||||
|
||||
sub init { |
||||
my $self = shift; |
||||
|
||||
# Add warning in log |
||||
unless ( $self->conf->{restUserDBUrl} ) { |
||||
$self->logger->error('No User REST URL given'); |
||||
return 0; |
||||
} |
||||
return 1; |
||||
} |
||||
|
||||
# RUNNING METHODS |
||||
|
||||
sub getUser { |
||||
my ( $self, $req ) = @_; |
||||
my $res = eval { |
||||
$self->restCall( $self->conf->{restUserDBUrl}, { user => $req->user } ); |
||||
}; |
||||
if ($@) { |
||||
$self->logger->error("UserDB REST error: $@"); |
||||
return PE_ERROR; |
||||
} |
||||
unless ( $res->{result} ) { |
||||
$self->userLogger->warn( 'User ' . $req->user . ' not found' ); |
||||
return PE_USERNOTFOUND; |
||||
} |
||||
$req->datas->{restUserDBInfo} = $res->{info}; |
||||
return PE_OK; |
||||
} |
||||
|
||||
sub setSessionInfo { |
||||
my ( $self, $req ) = @_; |
||||
$req->sessionInfo->{$_} = $req->datas->{restUserDBInfo}->{$_} |
||||
foreach ( keys %{ $req->datas->{restUserDBInfo} } ); |
||||
PE_OK; |
||||
} |
||||
|
||||
sub setGroups { |
||||
PE_OK; |
||||
} |
||||
|
||||
1; |
@ -0,0 +1,97 @@ |
||||
use strict; |
||||
use IO::String; |
||||
use Test::More; |
||||
use LWP::UserAgent; |
||||
use JSON qw(to_json from_json); |
||||
|
||||
BEGIN { |
||||
require 't/test-lib.pm'; |
||||
} |
||||
|
||||
my $res; |
||||
|
||||
my $client = LLNG::Manager::Test->new( |
||||
{ |
||||
ini => { |
||||
logLevel => 'error', |
||||
useSafeJail => 1, |
||||
authentication => 'REST', |
||||
userDB => 'Same', |
||||
passwordDB => 'REST', |
||||
restAuthUrl => 'http://ws/auth', |
||||
restUserDBUrl => 'http://ws/user', |
||||
restPwdConfirmUrl => 'http://ws/confirm', |
||||
restPwdModifyUrl => 'http://ws/modify', |
||||
} |
||||
} |
||||
); |
||||
|
||||
ok( |
||||
$res = $client->_post( |
||||
'/', IO::String->new('user=dwho&password=dwho'), |
||||
length => 23, |
||||
accept => 'text/html', |
||||
), |
||||
'Auth query' |
||||
); |
||||
count(1); |
||||
expectRedirection( $res, 'http://auth.example.com/' ); |
||||
my $id = expectCookie($res); |
||||
ok( |
||||
$res = $client->_post( |
||||
'/', |
||||
IO::String->new( |
||||
'oldpassword=dwho&newpassword=test&confirmpassword=test'), |
||||
cookie => "lemonldap=$id", |
||||
accept => 'application/json', |
||||
length => 54 |
||||
), |
||||
'Change password' |
||||
); |
||||
count(1); |
||||
expectOK($res); |
||||
$client->logout($id); |
||||
|
||||
clean_sessions(); |
||||
|
||||
done_testing( count() ); |
||||
|
||||
no warnings 'redefine'; |
||||
|
||||
sub LWP::UserAgent::request { |
||||
my ( $self, $req ) = @_; |
||||
ok( $req->uri =~ m#^http://ws/(auth|user|confirm|modify)#, |
||||
' ' . ucfirst($1) . ' REST request' ) |
||||
or explain( $req->uri, 'http://ws/(auth|user)' ); |
||||
my $type = $1; |
||||
count(1); |
||||
my $res = from_json( $req->content ); |
||||
ok( $res->{user} eq 'dwho', ' User is dwho' ); |
||||
count(1); |
||||
my $resp = HTTP::Response->new( 200, 'OK' ); |
||||
|
||||
if ( $type eq 'auth' ) { |
||||
ok( $res->{password} eq 'dwho', ' Password is dwho' ) |
||||
or explain( $res, 'password: dwho' ); |
||||
count(1); |
||||
$resp->content('{"result":true,"info":{"uid":"dwho"}}'); |
||||
} |
||||
elsif ( $type eq 'modify' ) { |
||||
ok( $res->{password} eq 'test', ' Password is test' ); |
||||
count(1); |
||||
$resp->content('{"result":true}'); |
||||
} |
||||
elsif ( $type eq 'confirm' ) { |
||||
ok( $res->{password} eq 'dwho', ' Password is dwho' ); |
||||
count(1); |
||||
$resp->content('{"result":true}'); |
||||
} |
||||
elsif ( $type eq 'user' ) { |
||||
$resp->content('{"result":true,"info":{"cn":"dwho"}}'); |
||||
} |
||||
else { |
||||
fail('Unknwon URL'); |
||||
count(1); |
||||
} |
||||
return $resp; |
||||
} |
Loading…
Reference in new issue