From 57715d8ce2abe942526eca17668c0db14e399f6c Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Fri, 4 Feb 2022 16:47:34 +0100 Subject: [PATCH] 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. --- doc/soju.1.scd | 33 +++++++++++++--- service.go | 103 +++++++++++++++++++++++++++++++------------------ 2 files changed, 93 insertions(+), 43 deletions(-) diff --git a/doc/soju.1.scd b/doc/soju.1.scd index 93e07fb..bb41df1 100644 --- a/doc/soju.1.scd +++ b/doc/soju.1.scd @@ -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...] +*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* + Select a network. By default, the current network is selected, if any. + *-key-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* Size of RSA key to generate. Ignored for other key types. -*certfp fingerprint* +*certfp fingerprint* [options...] Show SHA-1 and SHA-256 fingerprints for the certificate currently used with the network. -*sasl status* + Options are: + + *-network* + Select a network. By default, the current network is selected, if any. + +*sasl status* [options...] Show current SASL status. -*sasl set-plain* + Options are: + + *-network* + Select a network. By default, the current network is selected, if any. + +*sasl set-plain* [options...] Set SASL PLAIN credentials. -*sasl reset* + Options are: + + *-network* + 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* + Select a network. By default, the current network is selected, if any. + *user create* -username -password [options...] Create a new soju user. Only admin users can create new accounts. The _-username_ and _-password_ flags are mandatory. diff --git a/service.go b/service.go index 855f413..01655fa 100644 --- a/service.go +++ b/service.go @@ -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] ", + 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: "", - 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: "", + usage: "[-network name]", desc: "show SASL status", handle: handleServiceSASLStatus, }, "set-plain": { - usage: " ", + usage: "[-network name] ", desc: "set SASL PLAIN credentials", handle: handleServiceSASLSetPlain, }, "reset": { - usage: "", + 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 = ""