Portal plugins let you customize LemonLDAP::NG’s behavior.
Common use cases for plugins are:
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.
LemonLDAP::NG comes with many plugins already, but you might want to write your owns and load them as well
In General parameters » Plugins » Custom plugins
You can now write a custom portal plugin that will hook in the authentication process:
If you need to call a method just after any standard method in authentication process, then use afterSub, for example:
use constant afterSub => {
getUser => 'mysub',
};
sub mysub {
my ( $self ,$req ) = @_;
# Do something
return PE_OK;
}
If you need to call a method instead any standard method in authentication process, then use aroundSub, for example:
use constant aroundSub => {
getUser => 'mysub',
};
sub mysub {
my ( $self, $sub, $req ) = @_;
# Do something before
my $ret = $sub->($req);
# Do something after
return $ret;
}
New in version 2.0.10.
Your plugin can also register itself to be called at some points of interest within the main LemonLDAP::NG code.
Check the list of available plugin hooks for details
The plugin can also define new routes and call actions on them.
See also Lemonldap::NG::Portal::Main::Plugin man page.
The current LemonLDAP::NG configuration can be accessed in the $self->conf hash. This variable is only meant to be read. Don’t try changing its content, or Bad Things may happen.
You can set your own parameters in General Parameters » Plugins » Custom plugins » Additional parameters and reach them through customPluginsParams
Tip
If you have got several plugins with several parameters, parameters can be passed by using JSON notation myPluginParams = {"title":"myTitle", "msg":"myMsg"} and converted into a PERL object with from_json function.
use Mouse;
use JSON 'from_json';
has myPluginParams => (
is => 'ro',
builder => sub {
my $self = $_[0];
my $defaultParams = {
title => 'title',
msg => 'msg',
path => 'path',
};
my $params = eval {
from_json( $self->conf->{customPluginsParams}->{myPluginParams} );
};
if ($@) {
my $package = __PACKAGE__;
$self->userLogger->warn(
"Bad JSON file: $package plugin will use default parameters ($@)"
);
return $defaultParams;
}
return { %$defaultParams, %$params };
}
);
sub my_function {
my ($self, $req) = @_;
# Get a standard LLNG option
my $llng_logo = $self->conf->{portalMainLogo};
# Get your custom LLNG option
my $myvar = $self->conf->{customPluginsParams}->{myvar};
# Get your custom LLNG option from your parameters hash
my $title = $self->myPluginParams->{title};
}
You can use the $self->logger and $self->userLogger objects to log information during your plugin execution. Use logger for technical logs and userLogger for accounting and tracability events.
sub my_function {
my ($self, $req) = @_;
$self->logger->debug("Debug message");
if (my_custom_test($req->user)) {
$self->userLogger->debug("User ". $req->user .
" is not allowed because XXX");
return PE_ERROR;
}
return PE_OK;
}
In order to remember data between different steps, you can use the $req->data hash.
Data will not be remembered in between requests, only in between methods that process the same HTTP request.
Plugins may declare additional session fields to be stored in the Login history.
sub init {
my ($self) = @_;
$self->addSessionDataToRemember({
# This field will be hidden from the user
_language => '__hidden__',
# This field will be displayed on the portal. The column name
# is treated like a message and can be internationalized
authenticationLevel => "Human friendly column name",
});
return 1;
}
Column names can be translated by overriding the corresponding message
You can implement custom password policies in your plugins.
In order to do this, implement the passwordBeforeChange hook.
If you want to display user feedback, you need to do two additional things
In you plugin’s init method, declare a HTML control for your policy
$self->p->addPasswordPolicyDisplay(
'ppolicy-custom1',
{
# Activation condition
condition => $self->conf->{customPluginsParams}->{myPpolicyEnabled},
# Label to show the user
label => "myppolicy",
# What value to display in front of the label
value => 1,
# Optional key-value HTML data-* elements
data => { },
# Custom HTML code to inject before the element
customHtml => qq'<script type="text/javascript" src="$self->{p}->{staticPrefix}/common/js/myppolicy.min.js"></script>',
# Custom HTML code to inject after the element
customHtmlAfter => qq'<div id="advanced_feedback"></div>',
# Optional display order, higher goes last
order => 100,
}
);
Implement a JS check and trigger it on the checkpassword event
See the Lemonldap::NG::Portal::Captcha manual page for details on how to implement a Captcha module.
Adding a new type of second factor requires one or two modules:
This module must inherit from Lemonldap::NG::Portal::Main::SecondFactor.
package My::SFA;
use strict;
use Mouse;
extends 'Lemonldap::NG::Portal::Main::SecondFactor';
This module must inherit from Lemonldap::NG::Portal::2F::Register::Base.
package My::SFARegister;
use strict;
use Mouse;
extends 'Lemonldap::NG::Portal::2F::Register::Base';
Entrypoints allow plugins to register custom actions when they are loaded. LemonLDAP::NG comes with many pre-defined entrypoints already. But you may want to add your own, especially if you are creating a plugin that can itself have plugins.
You can call the addEntryPoint method in your plugin’s init to create your custom entrypoints.
Note
Only plugins loaded after registering the entry point will trigger it. Be careful with the ordering of your customPlugins variable.
Here are a few examples:
sub init {
my ($self) = @_;
# ...
# From now on, when loading a new plugin that inherits from
# My::Base::Class, run the provided code reference, with an
# instance of the plugin being loaded as first argument
$self->addEntryPoint(
isa => "My::Base::Class",
callback => sub {
my ($plugin) = @_;
$self->do_domething_with($plugin);
}
);
# From now on, when loading a new plugin that uses My::Role
# Call a particular method of a particular service, passing the new
# plugins instance and optional extra arguments.
# This will effectively run:
# $portal->getService('myservice')
# ->mymethod($plugin, "role_entry_point")
$self->addEntryPoint(
does => "My::Role",
service => "myservice",
method => "mymethod",
args => ["role_entry_point"],
);
# ...
return 1;
}
See also Lemonldap::NG::Portal::Main::Plugin man page for full details.
This example creates a Lemonldap::NG::Portal::Plugins::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/Plugins/MyPlugin.pm
Tip
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/Plugins/MyPlugin.pm. Or you can use your own namespace such as ACME::Corp::MyPlugin.
# The package name must match the file path
# This file must be in Lemonldap/NG/Portal/Plugins/MyPlugin.pm
package Lemonldap::NG::Portal::Plugins::MyPlugin;
use Mouse;
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' };
# 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) = @_;
my $userid = $req->user;
$self->p->logger->debug("Call welcome for $userid");
...
return $self->p->sendHtml($req, 'template', params => { WELCOME => 1 });
}
# Your file must return 1, or Perl will complain.
1;
Declare the plugin in lemonldap-ng.ini:
vi /etc/lemonldap-ng/lemonldap-ng.ini
[portal]
customPlugins = Lemonldap::NG::Portal::Plugins::MyPlugin
;customPlugins = Lemonldap::NG::Portal::Plugins::MyPlugin1, Lemonldap::NG::Portal::Plugins::MyPlugin2, ...
Since 2.0.7, it can also be configured in Manager, in General Parameters > Plugins > Custom Plugins.