21
Create a Password Generator with GTK and JavaScript
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('');
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('');
}
Done! Yeah! That's it!
You can test the generator in this codepen.
Now, that we have a working logic for our little tool, let's create the GUI with glade.
MainWindow
:
GtkBox
and set the following properties:
GtkHeaderBar
and set it up like this:
(General)
GtkGrid
for some checkboxes:
GtkBox
at the bottom:
GtkCheckButton
and give a unique ID to each one:
Mine have those ID's:
GtkLabel
and a GtkEntry
inside a GtkBox
:
GtkButton
to call the password generator:
GtkLabel
to show the generated password:
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
Ok! Now all we need to do is make this two things work together.
main.js
.
#!/usr/bin/gjs
imports.gi.versions.Gtk = '3.0';
const Gtk = imports.gi.Gtk;
Here the first line tells the interpreter we want to use to run this program,
Next we ensure we are working with GTK+ v3.0 and import it as
Gjs
.Next we ensure we are working with GTK+ v3.0 and import it as
Gtk
.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);
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, (
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
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.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);
}
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('');
}
DONE!
Go to your terminal and run this:
chmod +x ./main.js # this make the file executable
./main.js # this executes the program
21