From f01bc6a6950340388ed12eae7d438034312b8b49 Mon Sep 17 00:00:00 2001 From: Oliver Gorwits Date: Sun, 21 Oct 2018 17:31:52 +0100 Subject: [PATCH] add token schema and validation --- lib/App/Netdisco/DB.pm | 2 +- lib/App/Netdisco/DB/Result/User.pm | 4 +++ lib/App/Netdisco/Web/Auth/Provider/DBIC.pm | 21 ++++++++++++ lib/App/Netdisco/Web/AuthN.pm | 18 ++++++++--- lib/App/Netdisco/Worker/Plugin/Delete.pm | 2 +- .../Netdisco/Worker/Plugin/SetUserToken.pm | 32 +++++++++++++++++++ share/config.yml | 1 + .../App-Netdisco-DB-53-54-PostgreSQL.sql | 7 ++++ 8 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 lib/App/Netdisco/Worker/Plugin/SetUserToken.pm create mode 100644 share/schema_versions/App-Netdisco-DB-53-54-PostgreSQL.sql diff --git a/lib/App/Netdisco/DB.pm b/lib/App/Netdisco/DB.pm index 008b5369..5c589386 100644 --- a/lib/App/Netdisco/DB.pm +++ b/lib/App/Netdisco/DB.pm @@ -11,7 +11,7 @@ __PACKAGE__->load_namespaces( ); our # try to hide from kwalitee - $VERSION = 53; # schema version used for upgrades, keep as integer + $VERSION = 54; # schema version used for upgrades, keep as integer use Path::Class; use File::ShareDir 'dist_dir'; diff --git a/lib/App/Netdisco/DB/Result/User.pm b/lib/App/Netdisco/DB/Result/User.pm index 391ece43..acf4c3bf 100644 --- a/lib/App/Netdisco/DB/Result/User.pm +++ b/lib/App/Netdisco/DB/Result/User.pm @@ -14,6 +14,10 @@ __PACKAGE__->add_columns( { data_type => "varchar", is_nullable => 0, size => 50 }, "password", { data_type => "text", is_nullable => 1 }, + "token", + { data_type => "text", is_nullable => 1 }, + "token_from", + { data_type => "integer", is_nullable => 1 }, "creation", { data_type => "timestamp", diff --git a/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm b/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm index 190a38b6..6b0f8fb6 100644 --- a/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm +++ b/lib/App/Netdisco/Web/Auth/Provider/DBIC.pm @@ -53,6 +53,27 @@ sub get_user_details { return $user; } +sub validate_api_token { + my ($self, $token) = @_; + return unless defined $token; + + my $settings = $self->realm_settings; + my $database = schema($settings->{schema_name}) + or die "No database connection"; + + my $users_table = $settings->{users_resultset} || 'User'; + my $token_column = $settings->{users_token_column} || 'token'; + + my $user = try { + $database->resultset($users_table)->find({ $token_column => $token }); + }; + + return $user->username + if $user and $user->in_storage and $user->token_from + and $user->token_from > (time - setting('api_token_lifetime')); + return undef; +} + sub get_user_roles { my ($self, $username) = @_; return unless defined $username; diff --git a/lib/App/Netdisco/Web/AuthN.pm b/lib/App/Netdisco/Web/AuthN.pm index f8b97eff..7182fb28 100644 --- a/lib/App/Netdisco/Web/AuthN.pm +++ b/lib/App/Netdisco/Web/AuthN.pm @@ -36,6 +36,14 @@ hook 'before' => sub { session(logged_in_user => $user); session(logged_in_user_realm => 'users'); } + elsif (setting('api_token_lifetime') + and index(request->path,uri_for('/api/')->path) == 0) { + + my $user = $provider->validate_api_token(param('token')) + or return; + session(logged_in_user => $user); + session(logged_in_user_realm => 'users'); + } elsif (setting('no_auth')) { session(logged_in_user => 'guest'); session(logged_in_user_realm => 'users'); @@ -56,16 +64,16 @@ post '/login' => sub { my $mode = (request->is_ajax ? 'WebData' : request->header('Authorization') ? 'API' : 'WebUI'); - # get authN data from request (HTTP BasicAuth or URL params) my $authheader = request->header('Authorization'); - my ($u, $p) = (param('username'), param('password')); if (defined $authheader and $authheader =~ /^Basic (.*)$/) { - ($u, $p) = split(m/:/, (MIME::Base64::decode($1) || ":")); + my ($u, $p) = split(m/:/, (MIME::Base64::decode($1) || ":")); + params->{username} = $u; + params->{password} = $p; } # test authN - my ($success, $realm) = authenticate_user( $u, $p ); + my ($success, $realm) = authenticate_user(param('username'),param('password')); if ($success) { my $user = schema('netdisco')->resultset('User') @@ -92,7 +100,7 @@ post '/login' => sub { $user->update({ token_from => time, token => \'md5(random()::text)', - }); + })->discard_changes(); } return 'token:'. $user->token; } diff --git a/lib/App/Netdisco/Worker/Plugin/Delete.pm b/lib/App/Netdisco/Worker/Plugin/Delete.pm index 8ecb61ca..5597b7e2 100644 --- a/lib/App/Netdisco/Worker/Plugin/Delete.pm +++ b/lib/App/Netdisco/Worker/Plugin/Delete.pm @@ -8,7 +8,7 @@ use App::Netdisco::Util::Device 'delete_device'; register_worker({ phase => 'check' }, sub { return Status->error('Missing device (-d).') - unless defined shift->device; + unless shift->device; return Status->done('Delete is able to run'); }); diff --git a/lib/App/Netdisco/Worker/Plugin/SetUserToken.pm b/lib/App/Netdisco/Worker/Plugin/SetUserToken.pm new file mode 100644 index 00000000..1458b133 --- /dev/null +++ b/lib/App/Netdisco/Worker/Plugin/SetUserToken.pm @@ -0,0 +1,32 @@ +package App::Netdisco::Worker::Plugin::SetUserToken; + +use Dancer ':syntax'; +use Dancer::Plugin::DBIC 'schema'; + +use App::Netdisco::Worker::Plugin; +use aliased 'App::Netdisco::Worker::Status'; + +register_worker({ phase => 'check' }, sub { + return Status->error('Missing user (-e).') + unless shift->extra; + return Status->done('SetUserToken is able to run'); +}); + +register_worker({ phase => 'main' }, sub { + my ($job, $workerconf) = @_; + my $username = $job->extra; + + my $user = schema('netdisco')->resultset('User') + ->find({ username => $username }); + + return Status->error("No such user") + unless $user and $user->in_storage; + + $user->update({ token_from => time, token => \'md5(random()::text)' }) + ->discard_changes(); + + return Status->done( + sprintf 'Set token for user %s: %s', $username, $user->token); +}); + +true; diff --git a/share/config.yml b/share/config.yml index a8ca750e..ceb5db5b 100644 --- a/share/config.yml +++ b/share/config.yml @@ -400,6 +400,7 @@ worker_plugins: - 'Power' - 'Psql' - 'Renumber' + - 'SetUserToken' - 'Show' - 'Stats' - 'Vlan' diff --git a/share/schema_versions/App-Netdisco-DB-53-54-PostgreSQL.sql b/share/schema_versions/App-Netdisco-DB-53-54-PostgreSQL.sql new file mode 100644 index 00000000..ab7111c1 --- /dev/null +++ b/share/schema_versions/App-Netdisco-DB-53-54-PostgreSQL.sql @@ -0,0 +1,7 @@ +BEGIN; + +ALTER TABLE users ADD COLUMN "token" text; + +ALTER TABLE users ADD COLUMN "token_from" integer; + +COMMIT;