Merge branch 'v2.0' into CheckUser_history
This commit is contained in:
commit
01ef4c98d8
|
@ -12,6 +12,9 @@ Common use cases for plugins are:
|
|||
* Implementing additional controls or steps during login
|
||||
* Adjusting the behavior of SAML, OIDC or CAS protocols to work around application bugs
|
||||
|
||||
Creating a plugin can be as simple as writing a short Perl module file and
|
||||
declaring it in your configuration. See below for an example.
|
||||
|
||||
Plugin API
|
||||
----------
|
||||
|
||||
|
@ -140,9 +143,10 @@ Example
|
|||
Plugin Perl module
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Create for example the MyPlugin module:
|
||||
This example creates a ``Lemonldap::NG::Portal::MyPlugin`` plugin that
|
||||
showcases some features of the plugin system.
|
||||
|
||||
::
|
||||
First, create a file to contain the plugin code ::
|
||||
|
||||
vi /usr/share/perl5/Lemonldap/NG/Portal/MyPlugin.pm
|
||||
|
||||
|
@ -150,7 +154,8 @@ Create for example the MyPlugin module:
|
|||
|
||||
If you do not want to mix files from the distribution with
|
||||
your own work, put your own code in
|
||||
``/usr/local/lib/site_perl/Lemonldap/NG/Portal/MyPlugin.pm``\
|
||||
``/usr/local/lib/site_perl/Lemonldap/NG/Portal/MyPlugin.pm``.
|
||||
Or you can use your own namespace such as ``ACME::Corp::MyPlugin``.
|
||||
|
||||
.. code-block:: perl
|
||||
|
||||
|
@ -162,28 +167,46 @@ Create for example the MyPlugin module:
|
|||
use Lemonldap::NG::Portal::Main::Constants;
|
||||
extends 'Lemonldap::NG::Portal::Main::Plugin';
|
||||
|
||||
# Declare when LemonLDAP::NG must call your functions
|
||||
use constant beforeAuth => 'verifyIP';
|
||||
use constant hook => { passwordAfterChange => 'logPasswordChange' };
|
||||
|
||||
sub init {
|
||||
# Every plugin must have an init function that returns 1
|
||||
# You can define your custom initialization here
|
||||
my ($self) = @_;
|
||||
$self->addUnauthRoute( mypath => 'hello', [ 'GET', 'PUT' ] );
|
||||
$self->addAuthRoute( mypath => 'welcome', [ 'GET', 'PUT' ] );
|
||||
return 1;
|
||||
}
|
||||
|
||||
# This function will be called at the "beforeAuth" login step
|
||||
sub verifyIP {
|
||||
my ($self, $req) = @_;
|
||||
return PE_ERROR if($req->address !~ /^10/);
|
||||
return PE_OK;
|
||||
}
|
||||
|
||||
# This function will be called when changing passwords
|
||||
sub logPasswordChange {
|
||||
my ( $self, $req, $user, $password, $old ) = @_;
|
||||
$self->userLogger->info("Password changed for $user");
|
||||
return PE_OK;
|
||||
}
|
||||
|
||||
# You can define your custom initialization in the
|
||||
# init method.
|
||||
# Before LemonLDAP::NG 2.0.14, this function was mandatory
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
|
||||
# This is how you declare HTTP routes
|
||||
$self->addUnauthRoute( mypath => 'hello', [ 'GET', 'PUT' ] );
|
||||
$self->addAuthRoute( mypath => 'welcome', [ 'GET', 'PUT' ] );
|
||||
|
||||
# The function can return 0 to indicate failure
|
||||
return 1;
|
||||
}
|
||||
|
||||
# This method will be called to handle unauthenticated requests to /mypath
|
||||
sub hello {
|
||||
my ($self, $req) = @_;
|
||||
...
|
||||
return $self->p->sendJSONresponse($req, { hello => 1 });
|
||||
}
|
||||
|
||||
# This method will be called to handle authenticated requests to /mypath
|
||||
sub welcome {
|
||||
my ($self, $req) = @_;
|
||||
|
||||
|
@ -193,6 +216,7 @@ Create for example the MyPlugin module:
|
|||
...
|
||||
return $self->p->sendHtml($req, 'template', params => { WELCOME => 1 });
|
||||
}
|
||||
|
||||
# Your file must return 1, or Perl will complain.
|
||||
1;
|
||||
|
||||
|
|
|
@ -1104,7 +1104,7 @@ sub _handleClientCredentialsGrant {
|
|||
$self->userLogger->warn( 'Client '
|
||||
. $client_id
|
||||
. " was not granted any requested scopes ($req_scope) for $rp" );
|
||||
return $self->sendOIDCError( $req, 'invalid_scope', 403 );
|
||||
return $self->sendOIDCError( $req, 'invalid_scope', 400 );
|
||||
}
|
||||
|
||||
my $infos = {
|
||||
|
@ -1239,7 +1239,7 @@ sub _handlePasswordGrant {
|
|||
$self->userLogger->warn( 'User '
|
||||
. $req->sessionInfo->{ $self->conf->{whatToTrace} }
|
||||
. " was not granted any requested scopes ($req_scope) for $rp" );
|
||||
return $self->sendOIDCError( $req, 'invalid_scope', 403 );
|
||||
return $self->sendOIDCError( $req, 'invalid_scope', 400 );
|
||||
}
|
||||
|
||||
my $user_id = $self->getUserIDForRP( $req, $rp, $req->sessionInfo );
|
||||
|
|
|
@ -577,7 +577,11 @@ sub loadModule {
|
|||
$self->error("Unable to build $module object: $@");
|
||||
return 0;
|
||||
}
|
||||
unless ( $obj and $obj->init ) {
|
||||
unless ($obj) {
|
||||
$self->error("$module instanciation failed");
|
||||
return 0;
|
||||
}
|
||||
if ( $obj->can("init") and ( !$obj->init ) ) {
|
||||
$self->error("$module init failed");
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,21 @@ my $op = LLNG::Manager::Test->new( {
|
|||
oidcRPMetaDataOptionsIDTokenForceClaims => 1,
|
||||
oidcRPMetaDataOptionsRule => '$_scope =~ /\bread\b/',
|
||||
},
|
||||
scopelessrp => {
|
||||
oidcRPMetaDataOptionsDisplayName => "RP",
|
||||
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsClientID => "scopelessrp",
|
||||
oidcRPMetaDataOptionsAllowOffline => 1,
|
||||
oidcRPMetaDataOptionsAllowClientCredentialsGrant => 1,
|
||||
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
|
||||
oidcRPMetaDataOptionsClientSecret => "rpsecret",
|
||||
oidcRPMetaDataOptionsUserIDAttr => "",
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsBypassConsent => 1,
|
||||
oidcRPMetaDataOptionsRefreshToken => 1,
|
||||
oidcRPMetaDataOptionsIDTokenForceClaims => 1,
|
||||
oidcRPMetaDataOptionsRule => '',
|
||||
},
|
||||
pubrp => {
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsAllowClientCredentialsGrant => 1,
|
||||
|
@ -103,6 +118,13 @@ my $badquery2 = buildForm( {
|
|||
}
|
||||
);
|
||||
|
||||
my $badquery3 = buildForm( {
|
||||
client_id => 'scopelessrp',
|
||||
client_secret => 'rpsecret',
|
||||
grant_type => 'client_credentials',
|
||||
}
|
||||
);
|
||||
|
||||
my $goodquery = buildForm( {
|
||||
client_id => 'rpid',
|
||||
client_secret => 'rpsecret',
|
||||
|
@ -129,6 +151,15 @@ $res = $op->_post(
|
|||
);
|
||||
expectBadRequest($res);
|
||||
|
||||
## Test empty scope
|
||||
$res = $op->_post(
|
||||
"/oauth2/token",
|
||||
IO::String->new($badquery3),
|
||||
accept => 'application/json',
|
||||
length => length($badquery3),
|
||||
);
|
||||
expectReject($res, 400, "invalid_scope");
|
||||
|
||||
## Test a confidential RP
|
||||
$res = $op->_post(
|
||||
"/oauth2/token",
|
||||
|
|
|
@ -37,6 +37,21 @@ my $op = LLNG::Manager::Test->new( {
|
|||
}
|
||||
},
|
||||
oidcRPMetaDataOptions => {
|
||||
scopelessrp => {
|
||||
oidcRPMetaDataOptionsDisplayName => "RP",
|
||||
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
|
||||
oidcRPMetaDataOptionsClientID => "scopelessrp",
|
||||
oidcRPMetaDataOptionsAllowOffline => 1,
|
||||
oidcRPMetaDataOptionsAllowPasswordGrant => 1,
|
||||
oidcRPMetaDataOptionsIDTokenSignAlg => "HS512",
|
||||
oidcRPMetaDataOptionsClientSecret => "rpsecret",
|
||||
oidcRPMetaDataOptionsUserIDAttr => "",
|
||||
oidcRPMetaDataOptionsAccessTokenExpiration => 120,
|
||||
oidcRPMetaDataOptionsBypassConsent => 1,
|
||||
oidcRPMetaDataOptionsRefreshToken => 1,
|
||||
oidcRPMetaDataOptionsIDTokenForceClaims => 1,
|
||||
oidcRPMetaDataOptionsRule => '$uid eq "french"',
|
||||
},
|
||||
rp => {
|
||||
oidcRPMetaDataOptionsDisplayName => "RP",
|
||||
oidcRPMetaDataOptionsIDTokenExpiration => 3600,
|
||||
|
@ -92,6 +107,24 @@ $res = $op->_post(
|
|||
|
||||
expectReject( $res, 400, "invalid_grant" );
|
||||
|
||||
# Empty scope should fail
|
||||
my $query = buildForm( {
|
||||
client_id => 'scopelessrp',
|
||||
client_secret => 'rpsecret',
|
||||
grant_type => 'password',
|
||||
username => 'french',
|
||||
password => 'french',
|
||||
}
|
||||
);
|
||||
$res = $op->_post(
|
||||
"/oauth2/token",
|
||||
IO::String->new($query),
|
||||
accept => 'application/json',
|
||||
length => length($query),
|
||||
);
|
||||
|
||||
expectReject( $res, 400, "invalid_scope" );
|
||||
|
||||
$query = buildForm( {
|
||||
client_id => 'rpid',
|
||||
client_secret => 'rpsecret',
|
||||
|
|
|
@ -9,11 +9,6 @@ use constant hook => {
|
|||
'casGenerateValidateResponse' => 'genResponse',
|
||||
};
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub filterService {
|
||||
my ( $self, $req, $cas_request ) = @_;
|
||||
if ( $cas_request->{service} eq "http://auth.sp.com/" ) {
|
||||
|
|
|
@ -17,11 +17,6 @@ use constant hook => {
|
|||
oidcGotClientCredentialsGrant => 'oidcGotClientCredentialsGrant',
|
||||
};
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub addClaimToIDToken {
|
||||
my ( $self, $req, $payload, $rp ) = @_;
|
||||
$payload->{"id_token_hook"} = 1;
|
||||
|
@ -43,7 +38,7 @@ sub addScopeToRequest {
|
|||
|
||||
sub addHardcodedScope {
|
||||
my ( $self, $req, $scopeList, $rp ) = @_;
|
||||
push @{$scopeList}, "myscope";
|
||||
push @{$scopeList}, "myscope" if $rp ne "scopelessrp";
|
||||
|
||||
return PE_OK;
|
||||
}
|
||||
|
|
|
@ -10,10 +10,6 @@ use constant hook => {
|
|||
passwordAfterChange => 'afterChange',
|
||||
};
|
||||
|
||||
sub init {
|
||||
1;
|
||||
}
|
||||
|
||||
sub beforeChange {
|
||||
my ( $self, $req, $user, $password, $old ) = @_;
|
||||
if ( $password eq "12345" ) {
|
||||
|
|
|
@ -5,11 +5,6 @@ extends 'Lemonldap::NG::Portal::Main::Plugin';
|
|||
|
||||
use constant hook => { samlGotAuthnRequest => 'gotRequest', };
|
||||
|
||||
sub init {
|
||||
my ($self) = @_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
sub gotRequest {
|
||||
my ( $self, $res, $login ) = @_;
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user