Articulate

 view release on metacpan or  search on metacpan

lib/Articulate/Authentication.pm  view on Meta::CPAN

package Articulate::Authentication;
use strict;
use warnings;

use Moo;
with 'Articulate::Role::Component';

use Articulate::Syntax qw(new_credentials instantiate_array);

=head1 NAME

Articulate::Authentication - determine if a user who they claim to be

=head1 SYNOPSIS

  # in config:
  components:
    authentication:
      Articulate::Authentication:
        providers:
          - Articulate::Authentication::AlwaysAllow

  # then any component can dp
  $component->authentication->login($credentials);
  $component->authentication->login($user_id, $password);

=head1 ATTRIBUTE

=head3 providers

A list of providers which can respond to C<login>.

=cut

has providers => (
  is      => 'rw',
  default => sub { [] },
  coerce  => sub { instantiate_array(@_) },
);

=head3 login

  $authentication->login($credentials);
  $authentication->login( $user_id, $password );

Asks each provider if the credentials supplied match a known user.
Credentials may be in whatever form will satisfy the C<credentials>
function in L<Articulate::Credentials> (username and password, hashref
or credentials object).

Each provider must respond true, false, or undef. A true value means
the user is authenticated. A false value means that the user exists but
is explicitly refused access (this should only be used in exceptional
circumstances) and an undef value means the user cannot be
authenticated by the provider (but could be authenticated by some other
provider).

=cut

sub login {
  my $self        = shift;
  my $credentials = new_credentials @_;
  foreach my $provider ( @{ $self->providers } ) {
    return $credentials if $provider->authenticate($credentials);
    return $credentials if $credentials->rejected;
  }
  return $credentials->deny('No provider authenticated these credentials');
}

=head3 create_user

  $authentication->create_user( $user_id, $password );

Requests that a new user is created. Each provider must respond true,
false, or undef.

=cut

lib/Articulate/Authentication/Internal.pm  view on Meta::CPAN

Articulate::Authentication::Internal

=cut

=head1 METHODS

=cut

=head3 authenticate

  $self->authenticate( $credentials );

Accepts and returns the credentials if the C<password> matches the C<user_id>. Always returns the credentials passed in.

=cut

has extra_salt => (
  is      => 'rw',
  default => "If you haven't already, try powdered vegetable bouillon"
);

sub authenticate {
  my $self        = shift;
  my $credentials = shift;
  my $user_id     = $credentials->fields->{user_id} // return;
  my $password    = $credentials->fields->{password} // return;

  if ( $self->verify_password( $user_id, $password ) ) {
    return $credentials->accept('Passwords match');
  }

  # if we ever need to know if the user does not exist, now is the time to ask,
  # but we do not externally expose the difference between
  # "user not found" and "password doesn't match"
  return $credentials;
}

sub _password_salt_and_hash {
  my $self = shift;
  return Digest::SHA::sha512_base64(
    $_[0] . $_[1] #:5.10 doesn't like shift . shift
  );
}

sub _generate_salt {

lib/Articulate/Authentication/Preconfigured.pm  view on Meta::CPAN


=cut

has passwords => (
  is      => 'rw',
  default => sub { {} },
);

=head3 authenticate

  $self->authenticate( $credentials );

Accepts and returns the credentials if the C<password> matches the C<user_id>. Always returns the credentials passed in.

=cut

sub authenticate {
  my $self        = shift;
  my $credentials = shift;
  my $user_id     = $credentials->fields->{user_id} // return;
  my $password    = $credentials->fields->{password} // return;

  if ( exists $self->passwords->{$user_id} ) {
    return $credentials->accept('Passwords match')
      if $password eq $self->passwords->{$user_id};
  }

  # if we ever need to know if the user does not exist, now is the time to ask,
  # but we do not externally expose the difference between
  # "user not found" and "password doesn't match"
  return $credentials;
}

1;

lib/Articulate/Credentials.pm  view on Meta::CPAN

use overload bool => sub { shift->accepted }, '0+' => sub { shift->rejected };

=head1 NAME

Articulate::Credentials - represent an authentication request/response

=cut

=head1 FUNCTIONS

=head3 new_credentials

  my $credentials = new_credentials $user_id, $password;
  my $credentials = new_credentials { email => $email, api_key => $key };

Creates a new request, using the user_id and password supplied as the
respective arguments; or other fields if they are supplied instead.

=cut

use Exporter::Declare;
default_exports qw(new_credentials);

sub new_credentials {
  return shift if ref $_[0] eq __PACKAGE__;
  __PACKAGE__->new(
    {
      fields => (
        ( ref $_[0] eq ref {} )
        ? $_[0]
        : {
          user_id  => shift,
          password => shift,
        }

lib/Articulate/Credentials.pm  view on Meta::CPAN

=head1 METHODS

=head3 new

An unremarkable Moo constructor.

=cut

=head3 accept

  $credentials->accept('Password matched');

Declares that the credentials are valid, for the reason given; sets
C<accpeted> and C<rejected> and populates the stack trace.

=cut

sub accept {
  my $self   = shift;
  my $reason = shift;

  # die if granted or denied are already set?
  $self->accepted(1);
  $self->reason($reason);
  $self->stack_trace( Devel::StackTrace->new );
  return $self;
}

=head3 reject

  $credentials->reject('User not found');

Declares that the credentials are invalid, for the reason given; sets
C<accpeted> and C<rejected> and populates the stack trace.

=cut

sub reject {
  my $self   = shift;
  my $reason = shift;

  # die if granted or denied are already set?
  $self->accepted(0);
  $self->rejected(1);
  $self->reason($reason);
  $self->stack_trace( Devel::StackTrace->new );
  return $self;
}

=head1 ATTRIBUTES

=head3 fields

The credentials provided, typically user_id and password.

=cut

has fields => (
  is      => 'rw',
  default => sub { {} },
);

=head3 accepted

Whether or not the credentials have been explicitly accepted. The value
of this is used for overload behaviour.

Please do not explicitly set this. Use C<accept> instead.

=cut

has accepted => (
  is      => 'rw',
  default => sub { 0 },
);

=head3 rejected

Whether the credentials have been explicitly rejected.

Please do not explicitly set this. Use C<reject> instead.

=cut

has rejected => (
  is      => 'rw',
  default => sub { 0 },
);

=head3 reason

The reason for the acceptance or rejection of credentials.

Please do not explicitly set this. Use C<accept> or C<reject> instead.

=cut

has reason => (
  is      => 'rw',
  default => sub { '' },
);

=head3 stack_trace

The stack trace at the point of acceptance or rejection of credentials.

Please do not explicitly set this. Use C<accept> or C<reject> instead.

=cut

has stack_trace => (
  is      => 'rw',
  default => sub { '' },
);

lib/Articulate/Service/Login.pm  view on Meta::CPAN


  my $user_id  = $request->data->{user_id};
  my $password = $request->data->{password};

  if ( defined $user_id ) {
    if ( $self->authentication->login( $user_id, $password ) ) {
      $self->framework->user_id($user_id);
      return new_response success => { user_id => $user_id };
    } # Can we handle all the exceptions with 403s?
    else {
      throw_error Forbidden => 'Incorrect credentials';
    }
  }
  else {
    # todo: see if we have email and try to identify a user and verify with that
    throw_error Forbidden => 'Missing user id';
  }

}

sub handle_logout {

lib/Articulate/Syntax.pm  view on Meta::CPAN

use strict;
use warnings;

use Scalar::Util qw(blessed);
use Module::Load ();

use Exporter::Declare;
default_exports qw(
  instantiate instantiate_array instantiate_selection instantiate_array_selection
  new_response    new_request
  new_credentials new_permission
  new_location    new_location_specification
  dpath_get   dpath_set
  hash_merge
  throw_error
  select_from
  is_single_key_hash
);

use Articulate::Error;
use Data::DPath qw(dpath dpathr);

lib/Articulate/Syntax.pm  view on Meta::CPAN

=head1 FUNCTIONS

=head3 new_response

See L<Artciulate::Response>.

=head3 new_request

See L<Artciulate::Request>.

=head3 new_credentials

See L<Artciulate::Credentials>.

=head3 new_permission

See L<Artciulate::Permission>.

=head3 new_location

See L<Artciulate::Location>.



( run in 0.266 second using v1.01-cache-2.11-cpan-4d50c553e7e )