Last active
January 11, 2019 15:41
-
-
Save nerdstrike/55634af73bb9054af699dc11eaa985a4 to your computer and use it in GitHub Desktop.
Why no authenticate?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package Catalyst::Authentication::Store::ElasticSearch; | |
use strict; | |
use warnings; | |
use Moose; | |
use MooseX::NonMoose; | |
use Catalyst::Exception; | |
use Catalyst::Authentication::Store::ElasticSearch::User; # The default user storage class | |
has user_class => ( is => 'ro', isa => 'Str', default => 'Catalyst::Authentication::Store::ElasticSearch::User'); | |
has config => (is => 'rw'); | |
# Catalyst::Authentication modules must accept non-moosey arguments | |
# 1. $config, a hashref containing the server config | |
# 2. $app, sometimes $c, the plack app itself | |
# 3. $realm, a regime to enforce particular security constraints | |
around BUILDARGS => sub { | |
my ($call, $class, $config, $app, $realm) = @_; | |
return $class->$call( | |
config => $config | |
); # In case we should need the various options later | |
}; | |
=head2 from_session | |
Get me a user hashref with a session ID | |
=cut | |
sub from_session { | |
my ($self, $c, $frozenuser) = @_; | |
return $self->user_class->from_session($frozenuser); | |
} | |
=head2 for_session | |
Create a session for this user | |
=cut | |
sub for_session { | |
my ($self, $c, $user) = @_; | |
return $user->for_session(); | |
} | |
=head2 find_user | |
Provide authentication and a request context to get a user hashref back | |
=cut | |
sub find_user { | |
my ($self, $authinfo, $c) = @_; | |
use Data::Dumper; | |
print Dumper $c; # This returns the application with a request and server params | |
my $user = $c->model('ElasticSearch')->authenticate_user($authinfo); | |
return $self->user_class->load($user); | |
} | |
=head2 user_supports | |
The user class supports particular functionality, e.g. sessions. | |
For consumption by the web framework | |
=cut | |
sub user_supports { | |
my $self = shift; | |
# this must be a class method on the user_class | |
return $self->user_class->supports( @_ ); | |
} | |
__PACKAGE__->meta->make_immutable; | |
1; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package Catalyst::Model::ElasticSearch; | |
use Moose; | |
use namespace::autoclean; | |
use Search::Elasticsearch; | |
use Registry::Utils::File qw/slurp_file/; | |
use Registry; | |
use Carp; | |
use JSON; | |
extends 'Catalyst::Model'; | |
has 'nodes' => ( | |
is => 'rw', | |
lazy => 1, | |
default => "localhost:9200", | |
); | |
=head2 transport | |
The transport to use to interact with the Elasticsearch API. See L<Search::Elasticsearch::Transport|Search::Elasticsearch::Transport> for options. | |
=cut | |
has 'transport' => ( | |
is => 'rw', | |
lazy => 1, | |
default => "+Search::Elasticsearch::Transport", | |
); | |
=head2 _additional_opts | |
Stores other key/value pairs to pass to L<Search::Elasticsearch|Search::Elasticsearch>. | |
=cut | |
has '_additional_opts' => ( | |
is => 'rw', | |
lazy => 1, | |
isa => 'HashRef', | |
default => sub { { send_get_body_as => 'POST', cxn_pool => 'Static'} }, | |
); | |
=head2 _es | |
The L<Search::Elasticsearch> object. | |
The follwing helper methods have been replaced by the Search::Elasticsearch::Bulk | |
class. Similarly, scrolled_search() has been replaced by the Search::Elasticsearch::Scroll. | |
These helper classes are accessible as: | |
$bulk = $e->bulk_helper( %args_to_new ); | |
$scroll = $e->scroll_helper( %args_to_new ); | |
==> | |
- remove bulk_(index|create|delete) and reindex | |
- add bulk_helper, scroll_helper | |
- remove searchqs, scrolled_search (not supported) | |
- add indices (returns Search::Elasticsearch::Client::Indices | |
- add cluster (returns Search::Elasticsearch::Client::Cluster) | |
- other? | |
Given the method returns a Search::Elasticsearch::Client::Direct it's better | |
to look at what it now supports. | |
See https://metacpan.org/pod/Search::Elasticsearch::Client::Direct for a list of methods | |
grouped according to category | |
=cut | |
has '_es' => ( | |
is => 'ro', | |
lazy => 1, | |
required => 1, | |
builder => '_build_es', | |
handles => { | |
map { $_ => $_ } | |
qw( | |
search scrolled_search count index get get_source mget create delete | |
bulk bulk_helper scroll_helper indices | |
) | |
}, | |
); | |
sub _build_es { | |
my $self = shift; | |
return Search::Elasticsearch->new( | |
nodes => $self->nodes, | |
transport => $self->transport, | |
%{ $self->_additional_opts }, | |
); | |
} | |
around BUILDARGS => sub { | |
my $orig = shift; | |
my $class = shift; | |
my $params = $class->$orig(@_); | |
# NOTE: also update this: other stuff deprecated? | |
# See https://metacpan.org/pod/Search::Elasticsearch#MIGRATING-FROM-ElasticSearch.pm | |
if (defined $params->{servers}) { | |
carp "Passing 'servers' is deprecated, use 'nodes' now"; | |
$params->{nodes} = delete $params->{servers}; | |
} | |
my %additional_opts = %{$params}; | |
delete $additional_opts{$_} for qw/ nodes transport /; | |
$params->{_additional_opts} = \%additional_opts; | |
return $params; | |
}; | |
# Automatically deploy schemas to the configured backend if it is required | |
around _build_es => sub { | |
my $orig = shift; | |
my $self = shift; | |
my $client = $self->$orig(@_); | |
foreach my $schema_name (qw/authentication reports trackhub/) { | |
my $schema_path = File::Spec->catfile( | |
$self->config->{schema_location}, # Defined in Registry.pm | |
$schema_name.'_mappings.json' | |
); | |
# Alias user file name to its schema name. These should have been the same | |
my $index_name = $schema_name; | |
if ($schema_name eq 'authentication') { | |
$index_name = 'users'; | |
} | |
print "Creating index '$schema_name' with mapping $schema_path\n"; | |
print "App location = ".$self->config->{wtf}."\n"; | |
# Create indexes and load mappings if they're not present | |
unless ($client->indices->exists( index => $schema_name ) ) { | |
$client->indices->create( | |
index => $index_name.'_v1', | |
# FIXME, this should reflect actual schema version, but is deeply | |
# embedded into deployment | |
body => decode_json( slurp_file( $schema_path ) ) | |
); | |
} | |
} | |
return $client; | |
}; | |
=head2 authenticate_user | |
The anonymous user has provided authentication credentials. Fetch their | |
profile from the user index, constrained on their credentials. | |
=cut | |
sub authenticate_user { | |
my ($self, $authinfo) = @_; | |
my %query = ( bool => { must => [] } ); | |
# TODO: Constrain to specific set of keys and nothing else to prevent | |
# improper login attempts | |
foreach my $key (keys %{$authinfo}) { | |
push @{ $query{bool}{must} }, { term => { $key => $authinfo->{$key} } }; | |
} | |
%query = $self->_decorate_query(%query); | |
my $response = $self->_es->search(%query); | |
if ($response->{hits}{total} == 1) { | |
return $response->{hits}{hits}[0]; | |
} else { | |
return; | |
} | |
} | |
__PACKAGE__->meta->make_immutable; | |
1; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package Catalyst::Authentication::Store::ElasticSearch::User; | |
use strict; | |
use warnings; | |
use Moose; | |
use MooseX::NonMoose; | |
use Catalyst::Exception; | |
use Catalyst::Utils; | |
use LWP; | |
use JSON; | |
use Try::Tiny; | |
use namespace::autoclean; | |
extends 'Catalyst::Authentication::User'; | |
has supported_features => (is => 'ro', default => sub { { session => 1, roles => 1 } }); | |
has id => (is => 'rw', isa => 'Str'); # Unique ID for user | |
has roles => (is => 'rw', isa => 'ArrayRef'); # Roles of the user | |
has raw => (is => 'rw'); # Verbatim decoded copy of JSON from _source field of the result | |
=head2 load | |
Takes a single result from a user search in Elasticsearch and unpacks the relevant elements. | |
Required by Catalyst::Plugin::Authentication | |
=cut | |
sub load { | |
my ($self, $user_result) = @_; | |
return unless $user_result; | |
my %thing = JSON::decode_json($user_result); | |
$self->id( $thing{_id} ); # Delegate unique IDs to the storage engine | |
$self->roles( $thing{_source}{roles} ); | |
$self->raw( $thing{_source} ); | |
return $self; | |
} | |
=head2 get | |
Return an element of configuration for the user | |
Required by Catalyst::Plugin::Authentication | |
=cut | |
sub get { | |
my ($self, $field) = @_; | |
if (exists $self->raw->{$field}) { | |
return $self->raw->{$field}; | |
} | |
} | |
=head2 get_object | |
Return representation for user | |
Required by Catalyst::Plugin::Authentication | |
=cut | |
sub get_object { | |
my ($self) = @_; | |
return $self->raw; | |
} | |
=head2 for_session | |
Returns a serialised user for storage in the session. This is a JSON | |
representation of the user. | |
Required by Catalyst::Plugin::Authentication | |
=cut | |
sub for_session { | |
my ($self) = @_; | |
return JSON::encode_json($self->raw); | |
} | |
=head2 from_session | |
Receives the cached user from the session (produced via for_session), and deserialises. | |
It also unpacks the interesting bits for easy access | |
Required by Catalyst::Plugin::Authentication | |
=cut | |
sub from_session { | |
my ($self, $frozen_user) = @_; | |
my %user = JSON::decode_json($frozen_user); | |
$self->id( $user{_id}); | |
$self->roles( $user{_source}{roles}); | |
$self->raw($user{_source}); | |
return $self; | |
} | |
__PACKAGE__->meta->make_immutable(inline_constructor => 0); | |
1; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment