From f35d9c5d518b4376d636b4693c307762c23805e5 Mon Sep 17 00:00:00 2001 From: Kian Parvin <46668016+kian99@users.noreply.github.com> Date: Fri, 10 Jan 2025 17:09:34 +0200 Subject: [PATCH] feat: add ssh key table (#1515) --- .../postgres/020_add_ssh_keys_table.up.sql | 14 ++++++++ internal/dbmodel/sshkeys.go | 23 ++++++++++++ internal/dbmodel/sshkeys_test.go | 35 +++++++++++++++++++ 3 files changed, 72 insertions(+) create mode 100644 internal/dbmodel/sql/postgres/020_add_ssh_keys_table.up.sql create mode 100644 internal/dbmodel/sshkeys.go create mode 100644 internal/dbmodel/sshkeys_test.go diff --git a/internal/dbmodel/sql/postgres/020_add_ssh_keys_table.up.sql b/internal/dbmodel/sql/postgres/020_add_ssh_keys_table.up.sql new file mode 100644 index 000000000..0f78869f0 --- /dev/null +++ b/internal/dbmodel/sql/postgres/020_add_ssh_keys_table.up.sql @@ -0,0 +1,14 @@ +-- Add the ability to store users' SSH public keys + +CREATE TABLE ssh_keys ( + id SERIAL PRIMARY KEY, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE, + public_key BYTEA NOT NULL, + key_comment VARCHAR(255), + identity_name TEXT NOT NULL, + FOREIGN KEY (identity_name) REFERENCES identities(name), + CONSTRAINT unique_identity_ssh_key UNIQUE(identity_name, public_key) +); + +CREATE INDEX idx_ssh_keys_user_id ON ssh_keys (identity_name); diff --git a/internal/dbmodel/sshkeys.go b/internal/dbmodel/sshkeys.go new file mode 100644 index 000000000..8fceac153 --- /dev/null +++ b/internal/dbmodel/sshkeys.go @@ -0,0 +1,23 @@ +// Copyright 2025 Canonical. + +package dbmodel + +import "time" + +// SSHKey holds a user's public SSH key. +type SSHKey struct { + // Note this doesn't use the standard gorm.Model to avoid soft-deletes. + + ID uint `gorm:"primarykey"` + CreatedAt time.Time + UpdatedAt time.Time + + // IdentityName is the unique name (email or client-id) of this entity. + IdentityName string `gorm:"uniqueIndex:unique_identity_ssh_key"` + Identity Identity `gorm:"foreignKey:IdentityName;references:Name"` + + // PublicKey holds the user's public SSH key. + PublicKey []byte `gorm:"uniqueIndex:unique_identity_ssh_key"` + // KeyComment holds a user provided comment. + KeyComment string +} diff --git a/internal/dbmodel/sshkeys_test.go b/internal/dbmodel/sshkeys_test.go new file mode 100644 index 000000000..547954c34 --- /dev/null +++ b/internal/dbmodel/sshkeys_test.go @@ -0,0 +1,35 @@ +// Copyright 2025 Canonical. + +package dbmodel_test + +import ( + "testing" + + qt "github.com/frankban/quicktest" + + "github.com/canonical/jimm/v3/internal/dbmodel" +) + +func TestSSHKeyUniqueConstraint(t *testing.T) { + c := qt.New(t) + db := gormDB(c) + + u, err := dbmodel.NewIdentity("bob@canonical.com") + c.Assert(err, qt.IsNil) + + c.Assert(db.Create(u).Error, qt.IsNil) + + key := dbmodel.SSHKey{ + PublicKey: []byte("test"), + Identity: *u, + KeyComment: "foo", + } + c.Assert(db.Create(&key).Error, qt.IsNil) + + newKey := dbmodel.SSHKey{ + PublicKey: []byte("test"), + Identity: *u, + KeyComment: "bar", + } + c.Assert(db.Create(&newKey).Error, qt.ErrorMatches, ".*duplicate key value violates unique constraint \"unique_identity_ssh_key\".*") +}