Skip to content

Commit

Permalink
Merge 06ff9b8 into da72f8a
Browse files Browse the repository at this point in the history
  • Loading branch information
guggero committed Jun 1, 2018
2 parents da72f8a + 06ff9b8 commit dbfc932
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 5 deletions.
160 changes: 155 additions & 5 deletions cmd/lncli/commands.go
Expand Up @@ -10,24 +10,28 @@ import (
"io"
"io/ioutil"
"math"
"net"
"os"
"os/exec"
"strconv"
"strings"
"sync"
"syscall"

"gopkg.in/macaroon.v2"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"

"github.com/awalterschulze/gographviz"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/macaroons"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcutil"
"github.com/urfave/cli"
"golang.org/x/crypto/ssh/terminal"
"golang.org/x/net/context"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// TODO(roasbeef): cli logic for supporting both positional and unix style
Expand Down Expand Up @@ -57,7 +61,7 @@ func printRespJSON(resp proto.Message) {

jsonStr, err := jsonMarshaler.MarshalToString(resp)
if err != nil {
fmt.Println("unable to decode response: ", err)
fmt.Println("unable to encode response: ", err)
return
}

Expand Down Expand Up @@ -2903,3 +2907,149 @@ func forwardingHistory(ctx *cli.Context) error {
printRespJSON(resp)
return nil
}

var delegateMacaroonCommand = cli.Command{
Name: "delegatemacaroon",
Category: "Macaroons",
Usage: "Delegates a macaroon by adding restrictions to an " +
"existing one",
ArgsUsage: "[format] [timeout] [ip_address]",
Description: `
Take an existing macaroon (use --macaroonpath) as a template, add
restrictions to it and then serialize it either in JSON (--format=json)
or binary (--format=binary) format.
`,
Flags: []cli.Flag{
cli.StringFlag{
Name: "format",
Usage: "the format to serialize the macaroon in, " +
"must be either 'json' or 'binary'",
Value: "binary",
},
cli.Uint64Flag{
Name: "timeout",
Usage: "the number of seconds the macaroon will be " +
"valid before it times out",
},
cli.StringFlag{
Name: "ip_address",
Usage: "the IP address the maracoon will be bound to",
},
},
Action: actionDecorator(delegateMacaroon),
}

func delegateMacaroon(ctx *cli.Context) error {
var (
format, macPath string
timeout int64
ipAddress net.IP
err error
)
args := ctx.Args()

switch {
case ctx.GlobalIsSet("macaroonpath"):
macPath = cleanAndExpandPath(ctx.GlobalString("macaroonpath"))
default:
macPath = cleanAndExpandPath(defaultMacaroonPath)
}

switch {
case ctx.IsSet("format"):
format = ctx.String("format")
case args.Present():
format = args.First()
if format != "json" && format != "binary" {
return fmt.Errorf("format must be either 'json' or " +
"'binary'")
}
args = args.Tail()
}

switch {
case ctx.IsSet("timeout"):
timeout = ctx.Int64("timeout")
if timeout <= 0 {
return fmt.Errorf("timeout must be greater than 0")
}
case args.Present():
timeout, err = strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode timeout: %v", err)
}
if timeout <= 0 {
return fmt.Errorf("timeout must be greater than 0")
}
args = args.Tail()
}

switch {
case ctx.IsSet("ip_address"):
ipAddress = net.ParseIP(ctx.String("ip_address"))
if ipAddress == nil {
return fmt.Errorf("unable to parse ip_address: %s",
ctx.String("ip_address"))
}
case args.Present():
ipString := args.First()
ipAddress = net.ParseIP(ipString)
if ipAddress == nil {
return fmt.Errorf("unable to parse ip_address: %s",
ipString)
}
args = args.Tail()
}

// Read existing macaroon from file provided by --macaroonpath.
macBytes, err := ioutil.ReadFile(macPath)
if err != nil {
fatal(err)
}
mac := &macaroon.Macaroon{}
if err = mac.UnmarshalBinary(macBytes); err != nil {
fatal(err)
}

// Now apply the desired constraints to the macaroon.
// This will always create a new macaroon object, even if no constraints
// are added.
macConstraints := []macaroons.Constraint{}
if timeout > 0 {
macConstraints = append(macConstraints,
macaroons.TimeoutConstraint(timeout))
}
if ipAddress != nil {
macConstraints = append(macConstraints,
macaroons.IPLockConstraint(ipAddress.String()))
}
constrainedMac, err := macaroons.AddConstraints(mac, macConstraints...)
if err != nil {
fatal(err)
}

// Serialize the macaroon using the requested format and then print
// it to standard output. Both formats produce JSON as an output:
// - 'binary': prints a JSON object with one single string
// property 'macaroon'
// - 'json': directly prints the JSON serialized macaroon
if format == "binary" {
binaryString, err := constrainedMac.MarshalBinary()
if err != nil {
fatal(err)
}
printJSON(map[string]string{
"macaroon": hex.EncodeToString(binaryString),
})
} else {
jsonBytes, err := constrainedMac.MarshalJSON()
if err != nil {
fatal(err)
}
var out bytes.Buffer
json.Indent(&out, jsonBytes, "", "\t")
out.WriteString("\n")
out.WriteTo(os.Stdout)
}
return nil
}
1 change: 1 addition & 0 deletions cmd/lncli/main.go
Expand Up @@ -228,6 +228,7 @@ func main() {
feeReportCommand,
updateChannelPolicyCommand,
forwardingHistoryCommand,
delegateMacaroonCommand,
}

if err := app.Run(os.Args); err != nil {
Expand Down

0 comments on commit dbfc932

Please sign in to comment.