Acme-CPANModulesBundle-Import-MojoliciousAdvent-2018

 view release on metacpan or  search on metacpan

devdata/https_mojolicious.io_blog_2018_12_01_welcome-mojoconf-recap_  view on Meta::CPAN

Well this time I hit the most embarrassing of them all.</p>

<p>I realize the problems inherent with live demos and so I do what I can to prevent them: I practice, over and over.
This time, gentle reader, I learned a new lesson:</p>

<blockquote>
  <p>Practicing your live demo includes practicing logging in.
  <cite>Joel Berger, today</cite></p>
</blockquote>

<p>That&#39;s right, I forgot the login credentials to my own demo.</p>

<p>That said, most of the talk still worked.
So beyond that first lesson, here&#39;s one more: even experienced speakers mess up, we shrug and move on.
Don&#39;t let fear of failure stop you from speaking to groups of like minded colleagues about the work you do.</p>

<p>The talk is about migrating from a Lite app to a full app.
If you find yourself feeling afraid or confused in moving to a full app, or if you read the Mojolicious documentation and wonder how it applies to a full app, give this a watch.
Bonus material about modern Javascript at the end too.</p>

<p><iframe allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen frameborder="0" height="480" src="https://www.youtube.com/embed/ycAXeOKLCGc" width="854"></iframe></p>

devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_  view on Meta::CPAN

The result is that this post is about authenticating a <strong>Full App</strong> and isn&#39;t as
svelte as the other posts talking about their Lite apps.</p>

<p>Jumping straight in, let&#39;s assume that you already have a Login page
in your templates and it has a form which posts data to <code>/login</code>.
If you&#39;ve got a route like this</p>

<pre><code>$r-&gt;post(&#39;/login&#39;)-&gt;name(&#39;do_login&#39;)-&gt;to(&#39;Secure#on_user_login&#39;);
</code></pre>

<p>to send the credentials to your controller. Or if you&#39;re cool with
<a href="https://mojolicious.org/perldoc/Mojolicious/Guides/Routing#Named-routes">named routes</a>,
your template might include this line</p>

<pre><code>&lt;form action=&quot;&lt;%= url_for &#39;do_login&#39; %&gt;&quot; method=&quot;POST&quot;&gt;
</code></pre>

<p>Pro tip: You can even simplify it to</p>

<pre><code>%= form_for &#39;do_login&#39;
</code></pre>

devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_  view on Meta::CPAN


<pre><code>package MyApp::Controller::Secure;
use Mojo::Base &#39;Mojolicious::Controller&#39;;

sub on_user_login {
  my $self = shift;

  my $username = $self-&gt;param(&#39;username&#39;);
  my $password = $self-&gt;param(&#39;password&#39;);

  if (check_credentials($username, $password)) {
    $self-&gt;render(text =&gt; &#39;Hello Bender!&#39;);
  }
  else {
    $self-&gt;render(
        text =&gt; &#39;&lt;h2&gt;Login failed&lt;/h2&gt;&lt;a href=&quot;/login&quot;&gt;Try again&lt;/a&gt;&#39;,
        format =&gt; &#39;html&#39;,
        status =&gt; 401
    );
  }
}

sub check_credentials {
  my ($username, $password) = @_;

  return  $username eq &#39;Bender&#39; &amp;&amp; $password eq &#39;rocks&#39;;
}
</code></pre>

<h2>Storing passwords - MojoX::Auth::Simple</h2>

<p>We can agree that hard-coding usernames and passwords is not sustainable.
If you can connect to a database, any database that your Perl
<a href="https://metacpan.org/pod/DBI">DBI</a> module can connect to,
then you might think that
<a href="https://metacpan.org/pod/MojoX::Auth::Simple">MojoX::Auth::Simple</a>
will solve your problems.  Further reading will tell you that it only
provides the helper methods <code>log_in</code>, <code>is_logged_in</code> and <code>log_out</code>
which are useful for everything around the authentication, but not the
authentication itself.  But, since you&#39;re using a database now, you
could change the <code>check_credentials</code> to something better than this
(wot was cooked up on a Friday afternoon and not tested)</p>

<pre><code>sub check_credentials {
  my ($username, $password) = @_;

  my $statement = &lt;&lt;&#39;SQL&#39;;      # NO! Don&#39;t do this!
SELECT username FROM user_passwd
WHERE username = ? AND password = ?
SQL

  my $sth = $dbh-&gt;prepare($statement);
  $sth-&gt;execute($username, $password) or return;
  my @row = $sth-&gt;fetchrow_array();

devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_  view on Meta::CPAN

<pre><code>  my $statement = &lt;&lt;&#39;SQL&#39;;      # better
SELECT username FROM user_passwd
WHERE username = ? AND password = SHA2(?, 256)
SQL
</code></pre>

<p>or encrypt with Perl</p>

<pre><code>use Crypt::Digest::SHA256 qw/ sha256 /;

sub check_credentials {
  my ($username, $password) = @_;
  my $encrypted = sha256($password);

...

  $sth-&gt;execute($username, $encrypted) or return;
</code></pre>

<p>Technically, AES is an encryption algorithm and SHA-2 is a hashing algorithm,
meaning that the transformation is effectively one-way and is more secure.

devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_  view on Meta::CPAN

and stored the <code>$hash</code> value in your database like this</p>

<pre><code>my $hash = password_hash($initial_password);

my $sth = $dbh-&gt;prepare(&#39;INSERT INTO user_passwd (username, password) VALUES (?, ?)&#39;);
$sth-&gt;do($username, $hash);
</code></pre>

<p>you should be ok to change the sub to</p>

<pre><code>sub check_credentials {
  my ($username, $password) = @_;

  my $statement = &#39;SELECT password FROM user_passwd WHERE username = ?&#39;;

  my $sth = $dbh-&gt;prepare($statement);
  $sth-&gt;execute($username) or return;
  my ($encoded) = $sth-&gt;fetchrow_array();
  $sth-&gt;finish();

  return password_verify($password, $encoded);
}
</code></pre>

<p><a href="https://metacpan.org/pod/Mojolicious::Plugin::Scrypt">Mojolicious::Plugin::Scrypt</a>
will use the Scrypt algorithm,
but can also use Argon2 (which was recommended to me at LPW), Bcrypt and more.
So, assuming that you&#39;ve stored your password with
<code>my $encoded = $app-&gt;scrypt($password);</code>
the <code>on_user_login</code> sub becomes</p>

<pre><code>sub check_credentials {
  my ($username, $password) = @_;

  my $statement = &#39;SELECT password FROM user_passwd WHERE username = ?&#39;;

  my $sth = $dbh-&gt;prepare($statement);
  $sth-&gt;execute($username) or return;
  my ($encoded) = $sth-&gt;fetchrow_array();
  $sth-&gt;finish();

  # WAIT! where did $self come from
  return $self-&gt;scrypt_verify($password, $encoded);
}
</code></pre>

<p>Oh, dear.  The above crashes because of a design decision made early on in the writing process.
I invoked <code>check_credentials</code> as a plain sub, not the method of an object.
Using a Plugin depends on having the controller available, so the following changes are necessary.</p>

<pre><code>sub on_user_login {
  my $self = shift;
...

  if ($self-&gt;check_credentials($username, $password)) {
...
}

sub check_credentials {
  my ($self, $username, $password) = @_;
...
  return $self-&gt;scrypt_verify($password, $encoded);
}
</code></pre>

<p>Y&#39;know, I&#39;m sitting here on the Group W bench thinkin&#39; ...
if I&#39;m going to re-write this whole tutorial, maybe I should&#39;ve started with
<a href="https://metacpan.org/pod/Mojolicious::Plugin::Authentication">Mojolicious::Plugin::Authentication</a>
and taken you through the code you needed for the <code>validate_user</code> option in the Plugin.

devdata/https_mojolicious.io_blog_2018_12_08_authenticating-with-ldap_  view on Meta::CPAN

use YAML qw/LoadFile/;

my $config_file = &#39;ldap_config.yml&#39;;
my $config = LoadFile($config_file);

my ($LDAP_server, $base_DN, $user_attr, $user_id, )
        = @{$config}{ qw/server baseDN username id/ };

...

sub check_credentials {
  my ($username, $password) = @_;
  return unless $username;

  my $ldap = Net::LDAP-&gt;new( $LDAP_server )
        or warn(&quot;Couldn&#39;t connect to LDAP server $LDAP_server: $@&quot;), return;
  my $message = $ldap-&gt;bind( $base_DN );

  my $search = $ldap-&gt;search( base =&gt; $base_DN,
                              filter =&gt; join(&#39;=&#39;, $user_attr, $username),
                              attrs =&gt; [$user_id],



( run in 0.778 second using v1.01-cache-2.11-cpan-a5abf4f5562 )