service: switch to -network flag for certfp and sasl commands

Instead of always requiring users to explicitly specify the network
name, guess it from the downstream connection.

Network commands are left as-is because it's not yet clear how to
handle them.
This commit is contained in:
Simon Ser 2022-02-04 16:47:34 +01:00
parent 64ad2164de
commit 57715d8ce2
2 changed files with 93 additions and 43 deletions

View file

@ -325,7 +325,7 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
*default*
Currently same as *message*. This is the default behaviour.
*certfp generate* [options...] <network name>
*certfp generate* [options...]
Generate self-signed certificate and use it for authentication (via SASL
EXTERNAL).
@ -333,6 +333,9 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
Options are:
*-network* <name>
Select a network. By default, the current network is selected, if any.
*-key-type* <type>
Private key algorithm to use. Valid values are: _rsa_, _ecdsa_ and
_ed25519_. _ecdsa_ uses the NIST P-521 curve.
@ -340,19 +343,39 @@ abbreviated form, for instance *network* can be abbreviated as *net* or just
*-bits* <bits>
Size of RSA key to generate. Ignored for other key types.
*certfp fingerprint* <network name>
*certfp fingerprint* [options...]
Show SHA-1 and SHA-256 fingerprints for the certificate
currently used with the network.
*sasl status* <network name>
Options are:
*-network* <name>
Select a network. By default, the current network is selected, if any.
*sasl status* [options...]
Show current SASL status.
*sasl set-plain* <network name> <username> <password>
Options are:
*-network* <name>
Select a network. By default, the current network is selected, if any.
*sasl set-plain* [options...] <username> <password>
Set SASL PLAIN credentials.
*sasl reset* <network name>
Options are:
*-network* <name>
Select a network. By default, the current network is selected, if any.
*sasl reset* [options...]
Disable SASL authentication and remove stored credentials.
Options are:
*-network* <name>
Select a network. By default, the current network is selected, if any.
*user create* -username <username> -password <password> [options...]
Create a new soju user. Only admin users can create new accounts.
The _-username_ and _-password_ flags are mandatory.

View file

@ -6,7 +6,6 @@ import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
"errors"
"flag"
"fmt"
"io/ioutil"
@ -228,13 +227,13 @@ func init() {
"certfp": {
children: serviceCommandSet{
"generate": {
usage: "[-key-type rsa|ecdsa|ed25519] [-bits N] <network name>",
usage: "[-key-type rsa|ecdsa|ed25519] [-bits N] [-network name]",
desc: "generate a new self-signed certificate, defaults to using RSA-3072 key",
handle: handleServiceCertFPGenerate,
},
"fingerprint": {
usage: "<network name>",
desc: "show fingerprints of certificate associated with the network",
usage: "[-network name]",
desc: "show fingerprints of certificate",
handle: handleServiceCertFPFingerprints,
},
},
@ -242,17 +241,17 @@ func init() {
"sasl": {
children: serviceCommandSet{
"status": {
usage: "<network name>",
usage: "[-network name]",
desc: "show SASL status",
handle: handleServiceSASLStatus,
},
"set-plain": {
usage: "<network name> <username> <password>",
usage: "[-network name] <username> <password>",
desc: "set SASL PLAIN credentials",
handle: handleServiceSASLSetPlain,
},
"reset": {
usage: "<network name>",
usage: "[-network name]",
desc: "disable SASL authentication and remove stored credentials",
handle: handleServiceSASLReset,
},
@ -631,8 +630,24 @@ func sendCertfpFingerprints(dc *downstreamConn, cert []byte) {
sendServicePRIVMSG(dc, "SHA-512 fingerprint: "+hex.EncodeToString(sha512Sum[:]))
}
func getNetworkFromFlag(dc *downstreamConn, name string) (*network, error) {
if name == "" {
if dc.network == nil {
return nil, fmt.Errorf("no network selected, -network is required")
}
return dc.network, nil
} else {
net := dc.user.getNetwork(name)
if net == nil {
return nil, fmt.Errorf("unknown network %q", name)
}
return net, nil
}
}
func handleServiceCertFPGenerate(ctx context.Context, dc *downstreamConn, params []string) error {
fs := newFlagSet()
netName := fs.String("network", "", "select a network")
keyType := fs.String("key-type", "rsa", "key type to generate (rsa, ecdsa, ed25519)")
bits := fs.Int("bits", 3072, "size of key to generate, meaningful only for RSA")
@ -640,19 +655,15 @@ func handleServiceCertFPGenerate(ctx context.Context, dc *downstreamConn, params
return err
}
if len(fs.Args()) != 1 {
return errors.New("exactly one argument is required")
}
net := dc.user.getNetwork(fs.Arg(0))
if net == nil {
return fmt.Errorf("unknown network %q", fs.Arg(0))
}
if *bits <= 0 || *bits > maxRSABits {
return fmt.Errorf("invalid value for -bits")
}
net, err := getNetworkFromFlag(dc, *netName)
if err != nil {
return err
}
privKey, cert, err := generateCertFP(*keyType, *bits)
if err != nil {
return err
@ -672,13 +683,16 @@ func handleServiceCertFPGenerate(ctx context.Context, dc *downstreamConn, params
}
func handleServiceCertFPFingerprints(ctx context.Context, dc *downstreamConn, params []string) error {
if len(params) != 1 {
return fmt.Errorf("expected exactly one argument")
fs := newFlagSet()
netName := fs.String("network", "", "select a network")
if err := fs.Parse(params); err != nil {
return err
}
net := dc.user.getNetwork(params[0])
if net == nil {
return fmt.Errorf("unknown network %q", params[0])
net, err := getNetworkFromFlag(dc, *netName)
if err != nil {
return err
}
if net.SASL.Mechanism != "EXTERNAL" {
@ -690,13 +704,16 @@ func handleServiceCertFPFingerprints(ctx context.Context, dc *downstreamConn, pa
}
func handleServiceSASLStatus(ctx context.Context, dc *downstreamConn, params []string) error {
if len(params) != 1 {
return fmt.Errorf("expected exactly one argument")
fs := newFlagSet()
netName := fs.String("network", "", "select a network")
if err := fs.Parse(params); err != nil {
return err
}
net := dc.user.getNetwork(params[0])
if net == nil {
return fmt.Errorf("unknown network %q", params[0])
net, err := getNetworkFromFlag(dc, *netName)
if err != nil {
return err
}
switch net.SASL.Mechanism {
@ -722,17 +739,24 @@ func handleServiceSASLStatus(ctx context.Context, dc *downstreamConn, params []s
}
func handleServiceSASLSetPlain(ctx context.Context, dc *downstreamConn, params []string) error {
if len(params) != 3 {
return fmt.Errorf("expected exactly 3 arguments")
fs := newFlagSet()
netName := fs.String("network", "", "select a network")
if err := fs.Parse(params); err != nil {
return err
}
net := dc.user.getNetwork(params[0])
if net == nil {
return fmt.Errorf("unknown network %q", params[0])
if len(fs.Args()) != 2 {
return fmt.Errorf("expected exactly 2 arguments")
}
net.SASL.Plain.Username = params[1]
net.SASL.Plain.Password = params[2]
net, err := getNetworkFromFlag(dc, *netName)
if err != nil {
return err
}
net.SASL.Plain.Username = fs.Arg(0)
net.SASL.Plain.Password = fs.Arg(1)
net.SASL.Mechanism = "PLAIN"
if err := dc.srv.db.StoreNetwork(ctx, dc.user.ID, &net.Network); err != nil {
@ -744,13 +768,16 @@ func handleServiceSASLSetPlain(ctx context.Context, dc *downstreamConn, params [
}
func handleServiceSASLReset(ctx context.Context, dc *downstreamConn, params []string) error {
if len(params) != 1 {
return fmt.Errorf("expected exactly one argument")
fs := newFlagSet()
netName := fs.String("network", "", "select a network")
if err := fs.Parse(params); err != nil {
return err
}
net := dc.user.getNetwork(params[0])
if net == nil {
return fmt.Errorf("unknown network %q", params[0])
net, err := getNetworkFromFlag(dc, *netName)
if err != nil {
return err
}
net.SASL.Plain.Username = ""