Store server in db, password in secret and display server list

This commit is contained in:
oupson 2023-03-01 10:30:34 +01:00
parent 7359a03f90
commit d7b4940608
Signed by: oupson
GPG Key ID: 3BD88615552EFCB7
10 changed files with 167 additions and 25 deletions

View File

@ -12,7 +12,8 @@
"--share=ipc",
"--socket=fallback-x11",
"--device=dri",
"--socket=wayland"
"--socket=wayland",
"--talk-name=org.freedesktop.secrets"
],
"build-options" : {
"append-path" : "/usr/lib/sdk/vala/bin",

View File

@ -20,12 +20,14 @@
namespace Footerm.Model {
public class Server {
public int? id;
public string name;
public string hostname;
public ushort port;
public string username;
public Server(string name, string hostname, ushort port, string username) {
public Server(int? id, string name, string hostname, ushort port, string username) {
this.id = id;
this.name = name;
this.hostname = hostname;
this.port = port;

View File

@ -52,6 +52,17 @@ namespace Footerm {
try {
var config = Footerm.Services.Config.get_instance ();
var stored_server_list = config.get_server_list ();
foreach (var server in stored_server_list) {
var action_row = new Adw.ActionRow ();
action_row.set_title (server.name);
action_row.set_activatable (true);
action_row.activated.connect (() => {
this.on_server_selected (server);
});
server_list.add (action_row);
}
} catch (Error e) {
GLib.warning ("Failed to read server list : %s", e.message);
}

View File

@ -9,6 +9,11 @@
<property name="orientation">vertical</property>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwEntryRow" id="name_entry">
<property name="title" translatable="yes">Name</property>
</object>
</child>
<child>
<object class="AdwEntryRow" id="hostname_entry">
<property name="title" translatable="yes">Hostname</property>

View File

@ -22,6 +22,8 @@ namespace Footerm {
[GtkTemplate(ui = "/fr/oupson/FooTerm/newserver.ui")]
public class NewServer : Gtk.Box {
public signal void on_new_server(Footerm.Model.Server server);
[GtkChild]
private unowned Adw.EntryRow name_entry;
[GtkChild]
private unowned Adw.EntryRow hostname_entry;
@ -42,7 +44,8 @@ namespace Footerm {
add_server_button.clicked.connect(this.on_add_button_clicked);
}
private void on_add_button_clicked() {
private async void on_add_button_clicked() {
var name = this.name_entry.get_text();
var hostname = this.hostname_entry.get_text();
var port = int.parse(this.port_entry.get_text());
var username = this.username_entry.get_text();
@ -52,7 +55,11 @@ namespace Footerm {
// Port is invalid
}
// this.on_new_server(new Footerm.Model.Server(hostname, (ushort)port, username, password));
var server = new Footerm.Model.Server(null, name, hostname, (ushort)port, username);
var config_service = Footerm.Services.Config.get_instance();
yield config_service.save_server(server, password);
this.on_new_server(server);
}
}
}

View File

@ -111,7 +111,7 @@ namespace Footerm.Services {
public List<Footerm.Model.Server> get_server_list() throws ConfigError {
List<Footerm.Model.Server> list = new List<Footerm.Model.Server> ();
var stm_str = "SELECT (serverId, serverName, serverHostName, serverPort) FROM SERVER";
var stm_str = "SELECT serverId, serverName, serverHostName, serverPort, serverUsername FROM SERVER;";
Sqlite.Statement stm;
var ec = this.db.prepare_v2(stm_str, stm_str.length, out stm);
if (ec != Sqlite.OK) {
@ -119,10 +119,48 @@ namespace Footerm.Services {
}
while (stm.step() == Sqlite.ROW) {
list.append(new Footerm.Model.Server(stm.column_text(0), stm.column_text(1), (ushort) stm.column_int(2), stm.column_text(3)));
list.append(new Footerm.Model.Server(stm.column_int(0), stm.column_text(1), stm.column_text(2), (ushort) stm.column_int(3), stm.column_text(4)));
}
return list;
}
public async void save_server(Footerm.Model.Server server, string password) throws ConfigError, SecretError, Error {
var secrets = Secrets.get_instance();
yield secrets.store_password(server, password);
var stm_str = "INSERT INTO SERVER (serverName, serverHostName, serverPort, serverUsername, serverAuthentificationType) VALUES(?, ?, ?, ?, 'password')";
Sqlite.Statement stm;
var ec = this.db.prepare_v2(stm_str, stm_str.length, out stm);
if (ec != Sqlite.OK) {
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
}
ec = stm.bind_text(1, server.name);
if (ec != Sqlite.OK) {
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
}
ec = stm.bind_text(2, server.hostname);
if (ec != Sqlite.OK) {
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
}
ec = stm.bind_int(3, server.port);
if (ec != Sqlite.OK) {
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
}
ec = stm.bind_text(4, server.username);
if (ec != Sqlite.OK) {
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
}
ec = stm.step();
stm.reset();
if (ec != Sqlite.DONE) {
throw new ConfigError.DATABASE(@"Can't insert server: $(db.errcode ()): $(db.errmsg ())");
}
server.id = (int)this.db.last_insert_rowid();
}
}
}

68
src/services/Secrets.vala Normal file
View File

@ -0,0 +1,68 @@
/* Secrets.vala
*
* Copyright 2023 oupson
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
namespace Footerm.Services {
public errordomain SecretError {
FAILED_TO_STORE
}
public class Secrets {
private static Secrets instance = null;
public static Secrets get_instance() {
if (Secrets.instance == null) {
Secrets.instance = new Secrets();
}
return Secrets.instance;
}
private Secret.Schema password_schema;
private Secrets() {
this.password_schema = new Secret.Schema("fr.oupson.FooTerm.Password", Secret.SchemaFlags.NONE,
"hostname", Secret.SchemaAttributeType.STRING,
"port", Secret.SchemaAttributeType.INTEGER,
"username", Secret.SchemaAttributeType.STRING);
}
public async void store_password(Footerm.Model.Server server, string password) throws SecretError, Error {
var attributes = new GLib.HashTable<string, string> (str_hash, str_equal);
attributes["hostname"] = server.hostname;
attributes["port"] = server.port.to_string();
attributes["username"] = server.username;
var res = yield Secret.password_storev(this.password_schema, attributes, Secret.COLLECTION_DEFAULT, @"$(server.username)@$(server.hostname):$(server.port)", password, null);
if (!res) {
throw new SecretError.FAILED_TO_STORE("Failed to store the password");
}
debug("Password is stored");
}
public async string get_password(Footerm.Model.Server server) throws Error {
var attributes = new GLib.HashTable<string, string> (str_hash, str_equal);
attributes["hostname"] = server.hostname;
attributes["port"] = server.port.to_string();
attributes["username"] = server.username;
return yield Secret.password_lookupv(this.password_schema, attributes, null);
}
}
}

View File

@ -1,3 +1,4 @@
footerm_sources += files(
'Config.vala'
'Config.vala',
'Secrets.vala'
)

View File

@ -34,7 +34,13 @@ namespace Footerm {
public TerminalPane(Footerm.Model.Server server) {
this.server = server;
this.terminal.set_enable_sixel(true);
this.connect_to_server();
this.connect_to_server.begin((obj, res) => {
try {
this.connect_to_server.end(res);
} catch (Error e) {
warning("Failed to connect to the server : %s", e.message);
}
});
this.terminal.char_size_changed.connect(() => {
int rows = 0;
int columns = 0;
@ -51,7 +57,7 @@ namespace Footerm {
// stdout.printf("all done!\n");)
}
private void connect_to_server() throws GLib.IOError, GLib.Error {
private async void connect_to_server() throws GLib.IOError, GLib.Error {
var addrs = new NetworkAddress(this.server.hostname, this.server.port);
var addr = addrs.enumerate().next();
this.socket = new Socket(addr.get_family(), SocketType.STREAM, SocketProtocol.TCP);
@ -73,7 +79,10 @@ namespace Footerm {
stdout.printf("\n");
// TODO QUERY PASSWORD FROM libsecret
if (session.auth_password(this.server.username, null) != SSH2.Error.NONE) {
var secrets = Footerm.Services.Secrets.get_instance();
var password = yield secrets.get_password(this.server);
if (session.auth_password(this.server.username, password) != SSH2.Error.NONE) {
stdout.printf("\tAuthentication by password failed!\n");
session.disconnect("Normal Shutdown, Thank you for playing");
session = null;