Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I take credentials from the command line in a Mojolicious app?

I am developing a Mojolcious standalone application. In the code I am contacting an internal backend where the user should provide some credentials.

Currently I am using the credentials inside the code as variables. It looks some something like:

$password = 'somthing';

I tried to use the config plugin to store the credentials there, but is there an option with Mojolicious to let the user provide his credentials when running the daemon? Maybe like:

perl myapp.pl daemon -user username -password password

What is the best strategy with Mojolicious to handle this use case?

like image 932
smith Avatar asked Feb 22 '17 12:02

smith


1 Answers

I will provide two solutions to this problem. One is rather easy while the other one uses a few advanced techniques.

Easy: use environment variables

You can simply pass environment variables to your app when you start it.

$ USERNAME=foo PASSWORD=bar perl app.pl daemon

Mojolicious is Perl, so they end up in %ENV. We can access them with $ENV{USERNAME} and $ENV{PASSWORD}. Typically those environment variables are all-caps. It would make sense to pick something app-specific, so MYAPP_USERNAME is probably a better choice than USERNAME.

The good thing about this is that you can also set it in your shell environment and then you never have to worry about it.

package MyApp;
use Mojolicious::Lite;
use Data::Printer;

helper( 
    credentials => sub { 
        state $credentials = { 
            username => $ENV{USERNAME}, 
            password => $ENV{PASSWORD}, 
        } 
    }
);

get '/' => sub {
    my $c = shift;
    $c->render( text => np $c->app->credentials );
};

app->start;

In the above example I have created a helper that keeps the credentials for us so it's easy to access them. Of course you could just use $ENV{USERNAME} throughout the code, but I believe it's nicer to have it properly encapsulated.

If we curl localhost:3000 the daemon we get the this output.

$ curl localhost:3000
\ {
    password   "bar",
    username   "foo"
}

Advanced: subclass the daemon Command

Command line arguments all handled by Mojolicious::Command.

You can create your own Mojolicious::Command subclass. This is documented in the Mojolicious::Cookbook. If you name it Mojolicious::Command::foo then you don't even need to worry about adding another namespace for Mojo to look for commands in.

But you can unfortunately not pass several commands at the same time. So creating a subclass credentials and then doing this does not work.

$ perl myapp.pl daemon credentials --username foo --password bar

So we need to do something else. A quick look at the Mojolicious::Command::daemon tells us that it only has the run method, which gets called automatically when that command is called. We want it to do something additional, so we subclass that specific command.

package Mojolicious::Command::mydaemon;
use Mojo::Base 'Mojolicious::Command::daemon';
use Mojo::Util 'getopt';

sub run {
    my ( $self, @args ) = @_;

    getopt
        'u|username=s' => \my $username,
        'p|password=s' => \my $password;

    $self->app->credentials->{username} = $username;
    $self->app->credentials->{password} = $password;

    return $self->SUPER::run;
}

We need to import getopt, which helps us read stuff from the command line. We store the username and password in the credentials helper just like in the first approach because encapsulation is nice.

Finally, we need to hand over to the original run method. That's done by calling $self->SUPER::run, which is Perl's way of saying call run on my super-class from which I have inherited. It calls run on the SUPER:: pseudo class. You can learn more about that in perlobj.

Now the actual app is pretty much the same.

package MyApp;
use Mojolicious::Lite;
use Data::Printer;

helper(
    credentials => sub {
        state $credentials = { username => q{}, password => q{}, };
    }
);

get '/' => sub {
    my $c = shift;
    $c->render( text => np $c->app->credentials );
};

app->start;

We run it like this:

$ perl code/scratch.pl mydaemon --username foo --password bar

And if we curl localhost:3000 we get the same output.

$ curl localhost:3000
\ {
    password   "bar",
    username   "foo"
}

Which of those ways you choose is up to you. I am not sure which one I would prefer myself. Both have merits.

The environment variables are easier to implement and more portable, but the code is not as clear. The command subclass can document itself when done properly, but maybe your users would not expect something else than daemon. It's also a lot more code.

like image 160
simbabque Avatar answered Oct 12 '22 11:10

simbabque