Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions contribs/gnokms/internal/auth/auth.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package auth provides the 'gnokms auth' command and its subcommands for managing mutual authentication keys.
package auth

import (
Expand Down
41 changes: 32 additions & 9 deletions contribs/gnokms/internal/auth/auth_authorized.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package auth

import (
"context"
"flag"
"fmt"
"slices"

Expand Down Expand Up @@ -82,6 +83,12 @@ func newAuthAuthorizedAddCmd(rootCfg *common.AuthFlags, io commands.IO) *command
}

func execAuthAuthorizedAdd(rootCfg *common.AuthFlags, args []string, io commands.IO) error {
// Check that at least one public key is provided.
if len(args) == 0 {
io.ErrPrintln("error: at least one public key must be provided\n")
return flag.ErrHelp
}

// Add the public keys to the authorized keys list.
return manipulatesAuthorizedKeys(rootCfg, args, func(current []string, updates []string) []string {
for _, publicKey := range updates {
Expand Down Expand Up @@ -113,6 +120,12 @@ func newAuthAuthorizedRemoveCmd(rootCfg *common.AuthFlags, io commands.IO) *comm
}

func execAuthAuthorizedRemove(rootCfg *common.AuthFlags, args []string, io commands.IO) error {
// Check that at least one public key is provided.
if len(args) == 0 {
io.ErrPrintln("error: at least one public key must be provided\n")
return flag.ErrHelp
}

// Remove the public keys from the authorized keys list.
return manipulatesAuthorizedKeys(rootCfg, args, func(current []string, updates []string) []string {
for _, publicKey := range updates {
Expand All @@ -129,34 +142,44 @@ func execAuthAuthorizedRemove(rootCfg *common.AuthFlags, args []string, io comma

// newAuthAuthorizedListCmd creates the auth authorized list subcommand.
func newAuthAuthorizedListCmd(rootCfg *common.AuthFlags, io commands.IO) *commands.Command {
cfg := &authRawFlags{
auth: rootCfg,
}

return commands.NewCommand(
commands.Metadata{
Name: "list",
ShortUsage: "auth authorized list [flags]",
ShortHelp: "lists the client authorized keys",
LongHelp: "Lists the public keys of the clients that are authorized to authenticate with gnokms.",
},
commands.NewEmptyConfig(),
cfg,
func(_ context.Context, _ []string) error {
return execAuthAuthorizedList(rootCfg, io)
return execAuthAuthorizedList(cfg, io)
},
)
}

func execAuthAuthorizedList(rootCfg *common.AuthFlags, io commands.IO) error {
func execAuthAuthorizedList(authRawCfg *authRawFlags, io commands.IO) error {
// Load the auth keys file.
authKeysFile, err := loadAuthKeysFile(rootCfg)
authKeysFile, err := loadAuthKeysFile(authRawCfg.auth)
if err != nil {
return err
}

// Print the authorized keys.
if len(authKeysFile.ClientAuthorizedKeys) == 0 {
if len(authKeysFile.ClientAuthorizedKeys) == 0 && !authRawCfg.raw {
io.Printfln("No authorized keys found.")
} else {
io.Printfln("Authorized keys:")
for _, authorizedKey := range authKeysFile.ClientAuthorizedKeys {
io.Printfln("- %s", authorizedKey)
} else if len(authKeysFile.ClientAuthorizedKeys) > 0 {
if authRawCfg.raw {
for _, authorizedKey := range authKeysFile.ClientAuthorizedKeys {
io.Printfln("%s", authorizedKey)
}
} else {
io.Printfln("Authorized keys:")
for _, authorizedKey := range authKeysFile.ClientAuthorizedKeys {
io.Printfln("- %s", authorizedKey)
}
}
}

Expand Down
70 changes: 69 additions & 1 deletion contribs/gnokms/internal/auth/auth_authorized_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ func TestAuthorizedList(t *testing.T) {
buffer, io := createBufferedCmdOutput(t)

// Add authorized keys to the auth key file.
for i := 0; i < 3; i++ {
for range 3 {
authorizedKey := ed25519.GenPrivKey().PubKey().String()
authKeysFile.ClientAuthorizedKeys = append(authKeysFile.ClientAuthorizedKeys, authorizedKey)
}
Expand Down Expand Up @@ -329,6 +329,74 @@ func TestAuthorizedList(t *testing.T) {
require.Contains(t, buffer.String(), authorizedKey)
}
})

t.Run("valid auth key file without authorized keys with raw flag", func(t *testing.T) {
t.Parallel()

// Create the auth key file and a buffered command output.
filePath, _ := createAuthKeysFile(t)
buffer, io := createBufferedCmdOutput(t)

// Create the command flags with the auth key file.
flags := &common.AuthFlags{
AuthKeysFile: filePath,
}

// Create the command.
cmd := newAuthAuthorizedListCmd(flags, io)

// Create a context with a 5s timeout.
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()

// Run the command with the --raw flag.
cmdErr := cmd.ParseAndRun(ctx, []string{"--raw"})
require.NoError(t, cmdErr)

// Check that the command output is empty (no "No authorized keys found." message).
assert.Empty(t, buffer.String())
})

t.Run("valid auth key file with authorized keys with raw flag", func(t *testing.T) {
t.Parallel()

// Create the auth key file and a buffered command output.
filePath, authKeysFile := createAuthKeysFile(t)
buffer, io := createBufferedCmdOutput(t)

// Add authorized keys to the auth key file.
for range 3 {
authorizedKey := ed25519.GenPrivKey().PubKey().String()
authKeysFile.ClientAuthorizedKeys = append(authKeysFile.ClientAuthorizedKeys, authorizedKey)
}

// Save the auth key file.
require.NoError(t, authKeysFile.Save(filePath))

// Create the command flags with the auth key file.
flags := &common.AuthFlags{
AuthKeysFile: filePath,
}

// Create the command.
cmd := newAuthAuthorizedListCmd(flags, io)

// Create a context with a 5s timeout.
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()

// Run the command with the --raw flag.
cmdErr := cmd.ParseAndRun(ctx, []string{"--raw"})
require.NoError(t, cmdErr)

// Check the command output contains raw keys without formatting.
output := buffer.String()
assert.NotContains(t, output, "Authorized keys:")
assert.NotContains(t, output, "-")
for _, authorizedKey := range authKeysFile.ClientAuthorizedKeys {
require.Contains(t, output, authorizedKey)
}
})
}

func TestAuthorizedCmd(t *testing.T) {
Expand Down
10 changes: 5 additions & 5 deletions contribs/gnokms/internal/auth/auth_generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,22 +48,22 @@ func (f *authGenerateFlags) RegisterFlags(fs *flag.FlagSet) {
)
}

func execAuthGenerate(authGenFlags *authGenerateFlags, io commands.IO) error {
func execAuthGenerate(authGenCfg *authGenerateFlags, io commands.IO) error {
// Check if the file already exists.
if osm.FileExists(authGenFlags.auth.AuthKeysFile) && !authGenFlags.overwrite {
if osm.FileExists(authGenCfg.auth.AuthKeysFile) && !authGenCfg.overwrite {
return fmt.Errorf("%s: %s\n%s",
"error: auth keys file already exists at path", authGenFlags.auth.AuthKeysFile,
"error: auth keys file already exists at path", authGenCfg.auth.AuthKeysFile,
"use 'gnokms auth generate -overwrite' to overwrite it",
)
}

// Generate a new auth keys file.
if _, err := common.GeneratePersistedAuthKeysFile(authGenFlags.auth.AuthKeysFile); err != nil {
if _, err := common.GeneratePersistedAuthKeysFile(authGenCfg.auth.AuthKeysFile); err != nil {
return fmt.Errorf("error generating auth keys file: %w", err)
}

// Print the path to the generated file.
io.Printfln("Generated auth keys file at path: %q", authGenFlags.auth.AuthKeysFile)
io.Printfln("Generated auth keys file at path: %q", authGenCfg.auth.AuthKeysFile)

return nil
}
18 changes: 13 additions & 5 deletions contribs/gnokms/internal/auth/auth_identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,37 @@ import (

// newAuthIdentity creates the auth identity subcommand.
func newAuthIdentityCmd(rootCfg *common.AuthFlags, io commands.IO) *commands.Command {
cfg := &authRawFlags{
auth: rootCfg,
}

return commands.NewCommand(
commands.Metadata{
Name: "identity",
ShortUsage: "auth identity [flags]",
ShortHelp: "prints the identity public key of gnokms",
LongHelp: "Prints the identity public key of gnokms. This should be added to the authorized keys list of a client to allow it to authenticate with gnokms.",
},
commands.NewEmptyConfig(),
cfg,
func(_ context.Context, _ []string) error {
return execAuthIdentity(rootCfg, io)
return execAuthIdentity(cfg, io)
},
)
}

func execAuthIdentity(rootCfg *common.AuthFlags, io commands.IO) error {
func execAuthIdentity(authRawCfg *authRawFlags, io commands.IO) error {
// Load the auth keys file.
authKeysFile, err := loadAuthKeysFile(rootCfg)
authKeysFile, err := loadAuthKeysFile(authRawCfg.auth)
if err != nil {
return err
}

// Print the identity public key.
io.Printfln("Server public key: %q", authKeysFile.ServerIdentity.PubKey)
if authRawCfg.raw {
io.Println(authKeysFile.ServerIdentity.PubKey)
} else {
io.Printfln("Server public key: %q", authKeysFile.ServerIdentity.PubKey)
}

return nil
}
29 changes: 29 additions & 0 deletions contribs/gnokms/internal/auth/auth_identity_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,33 @@ func TestIdentity(t *testing.T) {
// Check the command output.
assert.Contains(t, buffer.String(), authKeysFile.ServerIdentity.PubKey)
})

t.Run("valid auth key file with raw flag", func(t *testing.T) {
t.Parallel()

// Create the auth key file and a buffered command output.
filePath, authKeysFile := createAuthKeysFile(t)
buffer, io := createBufferedCmdOutput(t)

// Create the command flags with the auth key file.
flags := &common.AuthFlags{
AuthKeysFile: filePath,
}

// Create the command.
cmd := newAuthIdentityCmd(flags, io)

// Create a context with a 5s timeout.
ctx, cancelFn := context.WithTimeout(context.Background(), 5*time.Second)
defer cancelFn()

// Run the command with the --raw flag.
cmdErr := cmd.ParseAndRun(ctx, []string{"--raw"})
require.NoError(t, cmdErr)

// Check the command output contains only the raw public key without description.
output := buffer.String()
assert.Contains(t, output, authKeysFile.ServerIdentity.PubKey)
assert.NotContains(t, output, "Server public key:")
})
}
25 changes: 25 additions & 0 deletions contribs/gnokms/internal/auth/auth_raw_flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package auth

import (
"flag"

"github.com/gnolang/gno/contribs/gnokms/internal/common"
)

type authRawFlags struct {
auth *common.AuthFlags
raw bool
}

var defaultAuthRawFlags = &authRawFlags{
raw: false,
}

func (f *authRawFlags) RegisterFlags(fs *flag.FlagSet) {
fs.BoolVar(
&f.raw,
"raw",
defaultAuthRawFlags.raw,
"output raw values, without descriptions",
)
}
2 changes: 2 additions & 0 deletions contribs/gnokms/internal/common/flags.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// Package common contains common flag definitions, authentication key file
// management, and utility functions used across gnokms commands.
package common

import (
Expand Down
1 change: 1 addition & 0 deletions contribs/gnokms/internal/gnokey/gnokey.go
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// Package gnokey implements a remote signer server using gnokey as the backend.
package gnokey

import (
Expand Down
Loading
Loading