Create a Password Generator with GTK and JavaScript

kenliten

Otoniel Reyes

Posted on June 7, 2021

Create a Password Generator with GTK and JavaScript

State 0: The Setup

To follow up this tutorial, you'll need this:
[ ] A text editor
[ ] GTK
[ ] glade, and
[ ] Gjs

Stage 1: The Algorithm

We're going to use a pretty simple algorithm to generate passwords based in some character collections group by type.

First, let's create the collections:

lowerCase = 'qwertyuiopasdfghjklzxcvbnm'.split('');
upperCase = 'QWERTYUIOPASDFGHJKLZXCVBNM'.split('');
numbers = '1234567890'.split('');
specialSymbols = `!@#$%^&*-_+=`.split('');
ambiguousSymbols = `{}[]()/\\'"?,.:;~\`|<>`.split('');
Enter fullscreen mode Exit fullscreen mode

Note I'm creating arrays from the strings since it's quicker that way.

Now, lets move to our main problem: Generate a Password.

function generate(
    length = 16, 
    useLower = true, 
    useUpper = true, 
    useNumbers = true, 
    useSpecials = false, 
    useAmbiguous = false
    ) {

    const source = [];
    const password = [];

    if (useLower) {
        source.push(...lowerCase);
    }
    if (useUpper) {
        source.push(...upperCase);
    }
    if (useNumbers) {
        source.push(...numbers);
    }
    if (useSpecials) {
        source.push(...specialSymbols);
    }
    if (useAmbiguous) {
        source.push(...ambiguousSymbols);
    }

    for (let i = 0; i < length; i++) {
        const char = source[Math.floor(Math.random() * (source.length - 1))];
        password.push(char);
    }

    return password.join('');
}
Enter fullscreen mode Exit fullscreen mode

Done! Yeah! That's it!

You can test the generator in this codepen.

Stage 2: The GUI

Now, that we have a working logic for our little tool, let's create the GUI with glade.

  1. Start creating a window and set an ID to it, mine is MainWindow:

Step 1
Step 1

  1. Add a GtkBox and set the following properties:
  2. Orientation: Vertical
  3. Spacing: 0
  4. Baseline: Center
  5. Homogeneous => on
  6. Number of items: 3

Step 2

  1. Add a GtkHeaderBar and set it up like this: (General)
  2. Title: Password Generator
  3. Reserve space for subtitle => off
  4. Spacing: 6
  5. Number of items: 0 (Packing)
  6. Position: 0
  7. Padding: 0
  8. Fill => on
  9. Expand => on
  10. Pack type: Start

Step 3

  1. A GtkGrid for some checkboxes:

Step 4

General Properties:
Step 4

Packing Properties:
Step 4

  1. Another GtkBox at the bottom:

Step 5

Packing Properties:
Step 5

  1. Add, customize 5 GtkCheckButton and give a unique ID to each one:

Step 6
Step 6

Mine have those ID's:

  • lowerCaseCheck
  • upperCaseCheck
  • numbersCheck
  • specialSymbolsCheck
  • ambiguousCharsCheck
  1. Add a GtkLabel and a GtkEntry inside a GtkBox:

Step 7
Step 7

  1. Add A GtkButton to call the password generator:

Step 8

  1. Two more GtkLabel to show the generated password:

Step 9

Leave the second one without text and make it selectable to allow user to copy the password. Don't forget to assign it an ID, in my case is 'generatedPasswordLabel'.

Now just save your new GUI in the project folder. You can save it as a .ui or a .glade file. Any way remember to call it exactly as it is. In my case is mainWindow.ui

Stage 3: Wrapping it up

Ok! Now all we need to do is make this two things work together.

  1. Import the Gtk functions. Create a new file and save it with this content as main.js.
#!/usr/bin/gjs

imports.gi.versions.Gtk = '3.0';
const Gtk = imports.gi.Gtk;
Enter fullscreen mode Exit fullscreen mode

Here the first line tells the interpreter we want to use to run this program, Gjs.
Next we ensure we are working with GTK+ v3.0 and import it as Gtk.

  1. Using OOP Now is time to add our logic, you can work this with a functional programming workflow, but we're going to use a OOP approach:
class PasswordGenerator {
  constructor() {
    this.app = new Gtk.Application();
    this.app.connect('activate', this._onActivate.bind(this));
    this.app.connect('startup', this._onStartup.bind(this));
  }

  _onActivate() {
    this._window.show_all();
  }

  _onStartup() {
    const builder = new Gtk.Builder();
    builder.add_from_file('mainWindow.ui');
    this._window = builder.get_object('mainWindow');
    this.app.add_window(this._window);
  }
}

const generator = new PasswordGenerator();

generator.app.run(ARGV);
Enter fullscreen mode Exit fullscreen mode

If you run this, you're going to see your previously designed GUI, but no actions except quit.

Ok, But... What's going on here?
Well, here, we create a Gtk Application in the class constructor, and connect two events, (activate, and startup) to our methods, _onActivate and _onStartup.

Inside _onStartup we just show up our GUI.
Inside _onActivate we create a builder object, so we can just 'require' our .ui file to charge the GUI. Then we get the UI from the file and save it to a property: _window. Lastly, we add the window to the app. This method will be called before _onStartup, so _window will be set just before be used.

  1. Linking the Widgets.

Now, we're going to load all the other widgets we neet to collect the info to generate a password.

Let's modify the constructor and the _onActivate methods:

constructor() {
  this.app = new Gtk.Application();
  this.app.connect('activate', this._onActivate.bind(this));
  this.app.connect('startup', this._onStartup.bind(this));
  this._lowerCase = 'qwertyuiopasdfghjklzxcvbnm'.split('');
  this._upperCase = 'QWERTYUIOPASDFGHJKLZXCVBNM'.split('');
  this._numbers = '1234567890'.split('');
  this._specialSymbols = `!@#$%^&*-_+=`.split('');
  this._ambiguousSymbols = `{}[]()/\\'"?,.:;~\`|<>`.split('');
}

_onStartup() {
  const builder = new Gtk.Builder();
  builder.add_from_file('mainWindow.ui');
  this._window = builder.get_object('mainWindow');
  this._generateButton = builder.get_object('generateButton');
  this._generateButton.connect('clicked', this._generatePassword.bind(this));
  this._lowerCaseCheck = builder.get_object('lowerCaseCheck');
  this._upperCaseCheck = builder.get_object('upperCaseCheck');
  this._numbersCheck = builder.get_object('numbersCheck');
  this._specialSymbolsCheck = builder.get_object('specialSymbolsCheck');
  this._ambiguousCharsCheck = builder.get_object('ambiguousCharsCheck');
  this._passwordLengthEntry = builder.get_object('passwordLengthEntry');
  this._generatedPasswordLabel = builder.get_object('generatedPasswordLabel');
  this.app.add_window(this._window);
}
Enter fullscreen mode Exit fullscreen mode
  1. Add the logic:

Now, all we need to do is add a new version of our generator:

_generatePassword() {
  const source = [];
  const password = [];
  const length = +this._passwordLengthEntry.text;

  if (this._lowerCaseCheck.active) {
    source.push(...this._lowerCase);
  }
  if (this._upperCaseCheck.active) {
    source.push(...this._upperCase);
  }
  if (this._numbersCheck.active) {
    source.push(...this._numbers);
  }
  if (this._specialSymbolsCheck.active) {
    source.push(...this._specialSymbols);
  }
  if (this._ambiguousCharsCheck.active) {
    source.push(...this._ambiguousSymbols);
  }

  for (let i = 0; i < length; i++) {
    const char = source[Math.floor(Math.random() * (source.length - 1))];
    password.push(char);
  }

  this._generatedPasswordLabel.label = password.join('');
}
Enter fullscreen mode Exit fullscreen mode

DONE!

Go to your terminal and run this:

chmod +x ./main.js # this make the file executable
./main.js # this executes the program
Enter fullscreen mode Exit fullscreen mode

Resources

Here is the full project, including a .desktop file and a simple icon. Check it out.

💖 💪 🙅 🚩
kenliten
Otoniel Reyes

Posted on June 7, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related