Skip to content

Commit

Permalink
openid support added
Browse files Browse the repository at this point in the history
  • Loading branch information
oetiker committed Feb 20, 2023
1 parent 40a98b3 commit f46e3f6
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 4 deletions.
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.9.4
0.10.0
6 changes: 6 additions & 0 deletions etc/extopus.cfg.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ mojo_secret = d0duj3mfjfviasasdfasdf
log_file = /scratch/extopus-demo/extopus-full.log
default_user = admin
update_interval = 5
openid_url = http://keycloak.example.com
openid_realm = extopus-realm
openid_client_id = extopus-client
openid_client_secret = 1234567890
openid_callback = http://extopus.example.com/openid/callback
openid_epuser_attribute = ep_user

*** FRONTEND ***
#logo_large = http://www.upc-cablecom.biz/en/cablecom_logo_b2b.jpg
Expand Down
16 changes: 14 additions & 2 deletions lib/EP.pm
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use EP::RpcService;
use EP::Config;
use EP::DocPlugin;
use EP::Visualizer;
use EP::Controller::OpenId;

use Mojo::Base 'Mojolicious';

=head2 cfg
Expand Down Expand Up @@ -80,7 +82,6 @@ Mojolicious calls the startup method at initialization time.

sub startup {
my $app = shift;

@{$app->commands->namespaces} = (__PACKAGE__.'::Command');

my $gcfg = $app->cfg->{GENERAL};
Expand Down Expand Up @@ -135,7 +136,18 @@ sub startup {
$self->redirect_to('/'.$app->prefix);
});
}

if ($gcfg->{openid_url}) {
# load openid config
EP::Controller::OpenId::loadConfig($app);
$routes->get('/openid/auth')->to(
controller => 'OpenId',
action => 'auth',
);
$routes->get('/openid/callback')->to(
controller => 'OpenId',
action => 'callback',
);
}
$routes->get('/' => sub { shift->redirect_to('/'.$app->prefix)});

$app->plugin('EP::DocPlugin', {
Expand Down
15 changes: 14 additions & 1 deletion lib/EP/Config.pm
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ ${E}head1 SYNOPSIS
localguide = /home/doc/extopusguide.pod
update_interval = 86400
# default_user = admin
openid_url = http://keycloak.example.com
openid_realm = extopus-realm
openid_client_id = extopus-client
openid_client_secret = 1234567890
openid_callback = http://extopus.example.com/openid/callback
openid_epuser_attribute = ep_user
*** FRONTEND ***
logo_large = http://www.upc-cablecom.biz/en/cablecom_logo_b2b.jpg
Expand Down Expand Up @@ -241,7 +247,7 @@ sub _make_parser {
_mandatory => [qw(GENERAL FRONTEND ATTRIBUTES TABLES)],
GENERAL => {
_doc => 'Global configuration settings for Extopus',
_vars => [ qw(cache_dir mojo_secret log_file log_level default_user update_interval localguide auto_update) ],
_vars => [ qw(cache_dir mojo_secret log_file log_level default_user update_interval localguide auto_update openid_url openid_realm openid_client_id openid_client_secret openid_callback openid_epuser_attribute) ],
_mandatory => [ qw(cache_dir mojo_secret log_file) ],
cache_dir => { _doc => 'directory to cache information gathered via the inventory plugins',
_sub => sub {
Expand All @@ -260,6 +266,13 @@ sub _make_parser {
_doc => 'check for updates every x seconds. 1 day by default. Set the update_interval to 0 to disable automatic updateing and relie on the populate command.',
_default => 24*3600,
},
openid_url => { _doc => 'url to the openid provider' },
openid_realm => { _doc => 'realm to use for openid authentication' },
openid_client_id => { _doc => 'client id for openid authentication' },
openid_client_secret => { _doc => 'client secret for openid authentication' },
openid_callback => { _doc => 'callback url for openid authentication' },
openid_epuser_attribute => { _doc => 'attribute to use for the user name' },
auto_update => { _doc => 'automatically update the inventory when the app starts' },
},
FRONTEND => {
_doc => 'Frontend tuneing parameters',
Expand Down
168 changes: 168 additions & 0 deletions lib/EP/Controller/OpenId.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
package EP::Controller::OpenId;

use Mojo::Base 'Mojolicious::Controller', -signatures;
use Mojo::URL;
use Mojo::Util qw(dumper);

=head1 NAME
EP::Controller::OpenId - OpenID Controller
=head1 SYNOPSIS
$routes->get('/openid/auth')->to(
controller => 'OpenId',
action => 'auth',
);
$routes->get('/openid/callback')->(
controller => 'OpenId',
action => 'cb',
);
=head1 DESCRIPTION
Provide OpenID authentication for EP using the OpenID Connect protocol.
=head1 ATTRIBUTES
All attributes inherited from L<Mojo::Base>. As well as the following:
=cut

=head2 gcfg
Access the GENERAL configuraiton. See L<EP::Config> for details.especially the C<openid_*> attributes.
Examples:
openid_url = http://keycloak.example.com
openid_realm = extopus-realm
openid_client_id = extopus-client
openid_client_secret = 1234567890
openid_callback = http://extopus.example.com/openid/callback
openid_epuser_attribute = ep_user
=cut

has gcfg => sub ($self) { $self->app->cfg->{GENERAL} };

=head1 METHODS
All methods inherited from L<Mojo::Base> as well as the following:
=head2 loadConfig($app)
Load the OpenID configuration from the OpenID provider. This should be called
once at startup time.
=cut

my $openIdCfg;

sub loadConfig ($app) {
return if $openIdCfg;
my $ua = $app->ua;
my $gcfg = $app->cfg->{GENERAL};
my $cfgurl = Mojo::URL->new($gcfg->{openid_url}
.'/realms/'.$gcfg->{openid_realm}
.'/.well-known/openid-configuration');
$app->log->debug("loading openid config from $cfgurl");
my $cfg = $ua->get($cfgurl)->res;
if (not $cfg->is_success) {
$app->log->error($cfg->to_string);
return;
}
# $self->log->debug($cfg->to_string);
$openIdCfg = $cfg->json;
};

=head2 auth
Redirect the user to the OpenID provider for authentication.
=cut

sub auth ($self) {
my $url = Mojo::URL->new($openIdCfg->{authorization_endpoint});
$self->redirect_to(
$url->query(
client_id => $self->gcfg->{openid_client_id},
response_type => 'code',
scope => 'openid',
redirect_uri => $self->gcfg->{openid_callback}
)
);
}

=head2 callback
Handle the callback from the OpenID provider. This will extract the user
information from the OpenID provider and store it in the session.
=cut

sub callback ($self) {
my $ua = $self->app->ua;
my $gcfg = $self->gcfg;
my $auth = $ua->post($openIdCfg->{token_endpoint} => form => {
code => $self->param('code'),
client_id => $gcfg->{openid_client_id},
client_secret => $gcfg->{openid_client_secret},
redirect_uri => $gcfg->{openid_callback},
grant_type => 'authorization_code'
})->res;
if (not $auth->is_success) {
$self->log->error($auth->to_string);
return $self->render(text => 'auth error', code => 403);
}
my $userInfo = $ua->get($openIdCfg->{userinfo_endpoint} => {
authorization => 'Bearer '.$auth->json->{access_token}
})->res;
if (not $userInfo->is_success) {
$self->log->error($userInfo->to_string);
return $self->render(text => 'userinfo error', code => 403);
}
$self->log->debug(dumper $userInfo->json);
my ($user,$login) = split /:/, ($userInfo->json->{$gcfg->{openid_epuser_attribute}} // '');
if (not $user) {
$self->log->error("no $gcfg->{openid_epuser_attribute} attribute found in userinfo (".dumper($userInfo->json).")");
return $self->render(text => 'userinfo not found', code => 403);
}
$self->session->{epUser} = $user;
$self->session->{epLogin} =$login;
$self->redirect_to('/'.$self->app->prefix);
}

1;

=head1 COPYRIGHT
Copyright (c) 2023 by OETIKER+PARTNER AG. All rights reserved.
=head1 LICENSE
This program 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 3 of the License, 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, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
=head1 AUTHOR
S<Tobias Oetiker E<lt>tobi@oetiker.chE<gt>>
=head1 HISTORY
2023-02-20 to 1.0 first version
=cut


0 comments on commit f46e3f6

Please sign in to comment.