Storing credentials of your Python programs in the keyring

err_speech

As I introduced in previous posts I’m a happy user of ErrBot (My second bot).
My main concern was to be able to control some functions in some machines without having to connect via ssh or exposing web pages to the world.

I couldn’t feel comfortable with the idea of storing my credentials in the config.py file which is the suggested way to connect to the services with the backend selected (‘username’ and ‘password’).

In order to avoid this I’m proposing here an alternative approach, based on the keyring module. Provided you have it correctly installed, you can store your credentials there and use them from your Python programs. In this case, from ErrBot.

You can see there the way to store your credentials and so on, we will not replicate them here.

Notice that the credentials storage will rely on the operating system and depending on the configuration anybody logged into your account will have access to them without password (you’ll need this if you want a bot that can start in an unattended way). We are only obtaining the added security of not having the credentials stored in the config.py file but not much more.

Then you need to make some changes in the config.py file. First of all, importing the module:

import keyring

Later, before the BOT_IDENTITY section you can add three variables, for the server (needed in order to select the account in the keyring) and the username:

server = 'jabber-fernand0movilizado'
username = 'fernand0movilizado@gmail.com'
password = keyring.get_password(server,username)

Finally, in the BOT_IDENTITY structure, in the backend you have selected, you
can put (XMPP backend, for example):

    'username': username,  # The JID of the user you have created for the bot
    'password': password,       # The corresponding password for this user

In this way when the bot starts it gets the credential from the keyring. You can use a similar approach in your programs and, if you do not need to autostart the program you can protect with a password the keyring entries.

My second bot

In Raspberry Pi: ¿qué temperatura hace en mi casa? (only in Spanish, sorry) we presented our first attempt at doing a bot. It allowed us to interact with the Raspberry Pi from our location, provided we had an internet connection. In this video we can see that interaction using IRC.

I tested SleekXMPP and phenny but I found some limits and continued my search. When I found err I discovered that it was in that moment under develoment and that it has a somewhat active community in Google+, Err. It provides a modular architecture for adding features to the bot.

My first steps were to adapt some tests I programmed for phenny and to add the possibility to take pictures with my cameras and sending them by email The code is at: err-plugins (it will change in the future, so we will pay atention to the current version):

The first one is pruebas.plug. It contains some meta-information needed to define the module following the bot syntax:

[Core]
Name = Pruebas
Module = pruebas

[Documentation]
Description = let's try things !

And the file pruebas.py contains the actual code for the programmed actions. For example, the following code takes a pictures and then sends it by mail:

<br />
@botcmd<br />
def foto(self, msg, args):<br />
	"""Take a picture"""<br />
	quien=msg.getFrom().getStripped()<br />
	yield "I'm taking the picture, wait a second "<br />
	if(args):<br />
		try:<br />
			cam=int(args)<br />
		except:<br />
			cam=0<br />
	else:<br />
		cam=0<br />
	yield "Camera %s"%cam<br />
	self.camera("/tmp/imagen.png",cam)<br />
	yield "Now I'm sending it"<br />
	self.mail("/tmp/imagen.png", quien)<br />
	my_msg = "I've sent it to ... %s"%quien<br />
	yield my_msg<br />

The first line indicates that this funcion defines an instruction for the bot. The name of the funcion will be the command that we will need to send by IM (we will need a configurable prefix, that serves to differenciate among instructions for the bot and other strings),

In our case, the instruction

.foto

would execute a function that is almost the same as the one commented in Sending an image by mail in Python.

The main differences are:

  • It gets its parameters from the function call (Err manages this)

    def foto(self, msg, args):

  • It replies to the mail of the person who sent the order:

    quien=msg.getFrom().getStripped()

  • The argument can be 0, 1 or no argument (no validation is done) because we have two cameras attached to our raspi. By default (no parameters provided or some uninterpretable paramenter proviede) it uses camera 0.
  • Now it replies telling us the chosen camera:

    yield "Camera %s"%cam

  • And now it calls the actual funciont in charge of taking the picture; its parameters are very similar to the ones commented in a previous post (the name of the file and the chosen camera):

    self.camera("/tmp/imagen.png",cam)

  • Now it calls the funciont that will send the picture to the previously defined mail address, so the parameters are the name of the file and this address.

    self.mail("/tmp/imagen.png", quien)

  • Finally, it uses again yield to reply, finishin the process.

If we look at the code, the main difference for these two functions are that they do not have a @bootcmd line; they are internal funcions, and they are not available as bot commands. They need some configuration options (as presented in Sending an image by mail in Python ).

Errbot manages this by means of:

<br />
def get_configuration_template(self):<br />
return{'ADDRESS' : u'kk@kk.com', 'FROMADD' : u'kk@kk.com',<br />
'TOADDRS' : u'kk@kk.com', 'SUBJECT' : u'Imagen',<br />
'SMTPSRV' : u'smtp.gmail.com:587', 'LOGINID' : u'changeme',<br />
'LOGINPW' : u'changeme'}<br />

It is a dictionary with the parameters we need to configure.

If we send the order via IM:

.config Pruebas

In this case, Pruebas is the name of the module and we have selected the dot (.) as the indicator that the following string is an instruction for the bot. The config instruction returns the current configuration (if it has not been configured it returns the defined template; if it is configured it returns the actual values). These values can be used as a template for the module configuration.

.config Pruebas {'TOADDRS': u'mydir@mail.com', 'SMTPSRV':
u'smtp.gmail.com:587', 'LOGINPW': u'mypassword',
...
}

We are almost done, soon we will be able to show the whole thing.