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

View File

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