#1036 skip API login for trust_remote_user, trust_x_remote_user, no_auth

This commit is contained in:
Oliver Gorwits
2023-06-05 17:02:20 +01:00
parent 01928fa85d
commit 65a908dcd3
4 changed files with 88 additions and 57 deletions

View File

@@ -27,13 +27,14 @@ __PACKAGE__->result_source_instance->view_definition(<<ENDSQL
WHERE tacacs
UNION
SELECT username, 'api' AS role FROM users
WHERE token IS NOT NULL AND token_from IS NOT NULL
AND token_from > (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) - ?)
WHERE ( ? ::boolean = false ) OR
( token IS NOT NULL AND token_from IS NOT NULL
AND token_from > (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) - ?) )
UNION
SELECT username, 'api_admin' AS role FROM users
WHERE token IS NOT NULL AND token_from IS NOT NULL
AND token_from > (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) - ?)
AND admin
WHERE admin AND (( ? ::boolean = false ) OR
( token IS NOT NULL AND token_from IS NOT NULL
AND token_from > (EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) - ?) ))
ENDSQL
);

View File

@@ -391,6 +391,10 @@ hook 'after' => sub {
}
};
my $api_requires_key =
(setting('trust_remote_user') or setting('trust_x_remote_user') or setting('no_auth'))
eq '1' ? false : true;
# setup for swagger API
my $swagger = Dancer::Plugin::Swagger->instance;
my $swagger_doc = $swagger->doc;
@@ -398,8 +402,8 @@ my $swagger_doc = $swagger->doc;
$swagger_doc->{consumes} = 'application/json';
$swagger_doc->{produces} = 'application/json';
$swagger_doc->{tags} = [
{name => 'General',
description => 'Log in and Log out'},
($api_requires_key ?
({name => 'General', description => 'Log in and Log out'}) : ()),
{name => 'Search',
description => 'Search Operations'},
{name => 'Objects',
@@ -409,13 +413,16 @@ $swagger_doc->{tags} = [
{name => 'Queue',
description => 'Operations on the Job Queue'},
];
$swagger_doc->{securityDefinitions} = {
if ($api_requires_key) {
$swagger_doc->{securityDefinitions} = {
APIKeyHeader =>
{ type => 'apiKey', name => 'Authorization', in => 'header' },
BasicAuth =>
{ type => 'basic' },
};
$swagger_doc->{security} = [ { APIKeyHeader => [] } ];
};
$swagger_doc->{security} = [ { APIKeyHeader => [] } ];
}
# manually install Swagger UI routes because plugin doesn't handle non-root
# hosting, so we cannot use show_ui(1)

View File

@@ -95,9 +95,19 @@ sub get_user_roles {
my $roles = $settings->{roles_relationship} || 'roles';
my $role_column = $settings->{role_column} || 'role';
# this method returns a list of current user roles
# but for API with trust_remote_user, trust_x_remote_user, and no_auth
# we need to fake that there is a valid API key
my $api_requires_key =
(setting('trust_remote_user') or setting('trust_x_remote_user') or setting('no_auth'))
eq '1' ? 'false' : 'true';
return [ try {
$user->$roles->search({}, { bind => [setting('api_token_lifetime'), setting('api_token_lifetime')] })
->get_column( $role_column )->all;
$user->$roles->search({}, { bind => [
$api_requires_key, setting('api_token_lifetime'),
$api_requires_key, setting('api_token_lifetime'),
] })->get_column( $role_column )->all;
} ];
}

View File

@@ -32,22 +32,11 @@ hook 'before' => sub {
# from the internals of Dancer::Plugin::Auth::Extensible
my $provider = Dancer::Plugin::Auth::Extensible::auth_provider('users');
# API calls must conform strictly to path and header requirements
if (request_is_api) {
# Dancer will issue a cookie to the client which could be returned and
# cause API calls to succeed without passing token. Kill the session.
session->destroy;
session->destroy if request_is_api;
my $token = request->header('Authorization');
my $user = $provider->validate_api_token($token)
or return;
session(logged_in_user => $user);
session(logged_in_user_realm => 'users');
return;
}
# after checking API, we can short circuit if Dancer reads its cookie OK
# ...otherwise, we can short circuit if Dancer reads its cookie OK
return if session('logged_in_user');
if (setting('trust_x_remote_user')
@@ -72,27 +61,27 @@ hook 'before' => sub {
session(logged_in_user => $user);
session(logged_in_user_realm => 'users');
}
# this works for API calls, too
elsif (setting('no_auth')) {
session(logged_in_user => 'guest');
session(logged_in_user_realm => 'users');
}
# API calls must conform strictly to path and header requirements
elsif (request_is_api) {
my $token = request->header('Authorization');
my $user = $provider->validate_api_token($token)
or return;
session(logged_in_user => $user);
session(logged_in_user_realm => 'users');
}
else {
# user has no AuthN - force to handler for '/'
request->path_info('/');
}
};
# override default login_handler so we can log access in the database
swagger_path {
description => 'Obtain an API Key',
tags => ['General'],
path => (setting('url_base') ? setting('url_base')->with('/login')->path : '/login'),
parameters => [],
responses => { default => { examples => {
'application/json' => { api_key => 'cc9d5c02d8898e5728b7d7a0339c0785' } } },
},
},
post '/login' => sub {
my $login_sub = sub {
my $api = ((request->accept and request->accept =~ m/(?:json|javascript)/) ? true : false);
# get authN data from BasicAuth header used by API, put into params
@@ -161,21 +150,7 @@ post '/login' => sub {
}
};
# ugh, *puke*, but D::P::Swagger has no way to set this with swagger_path
# must be after the path is declared, above.
Dancer::Plugin::Swagger->instance->doc
->{paths}->{ (setting('url_base') ? setting('url_base')->with('/login')->path : '/login') }
->{post}->{security}->[0]->{BasicAuth} = [];
# we override the default login_handler, so logout has to be handled as well
swagger_path {
description => 'Destroy user API Key and session cookie',
tags => ['General'],
path => (setting('url_base') ? setting('url_base')->with('/logout')->path : '/logout'),
parameters => [],
responses => { default => { examples => { 'application/json' => {} } } },
},
get '/logout' => sub {
my $logout_sub = sub {
my $api = ((request->accept and request->accept =~ m/(?:json|javascript)/) ? true : false);
# clear out API token
@@ -202,6 +177,44 @@ get '/logout' => sub {
redirect uri_for(setting('web_home'))->path;
};
my $api_requires_key =
(setting('trust_remote_user') or setting('trust_x_remote_user') or setting('no_auth'))
eq '1' ? false : true;
if ($api_requires_key) {
# override default login_handler so we can log access in the database
swagger_path {
description => 'Obtain an API Key',
tags => ['General'],
path => (setting('url_base') ? setting('url_base')->with('/login')->path : '/login'),
parameters => [],
responses => { default => { examples => {
'application/json' => { api_key => 'cc9d5c02d8898e5728b7d7a0339c0785' } } },
},
},
post '/login' => $login_sub;
# ugh, *puke*, but D::P::Swagger has no way to set this with swagger_path
# must be after the path is declared, above.
Dancer::Plugin::Swagger->instance->doc
->{paths}->{ (setting('url_base') ? setting('url_base')->with('/login')->path : '/login') }
->{post}->{security}->[0]->{BasicAuth} = [];
# we override the default login_handler, so logout has to be handled as well
swagger_path {
description => 'Destroy user API Key and session cookie',
tags => ['General'],
path => (setting('url_base') ? setting('url_base')->with('/logout')->path : '/logout'),
parameters => [],
responses => { default => { examples => { 'application/json' => {} } } },
},
get '/logout' => $logout_sub;
}
else {
post '/login' => $login_sub;
get '/logout' => $logout_sub;
}
# user redirected here when require_role does not succeed
any qr{^/(?:login(?:/denied)?)?} => sub {
my $api = ((request->accept and request->accept =~ m/(?:json|javascript)/) ? true : false);