From 5c3493bd30299e4d512875c4725d6430d6b83ef2 Mon Sep 17 00:00:00 2001 From: whythat Date: Sat, 2 Sep 2017 02:30:57 +0300 Subject: [PATCH] macaroons: add constraint/checker options layer --- macaroons/constraints.go | 86 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 macaroons/constraints.go diff --git a/macaroons/constraints.go b/macaroons/constraints.go new file mode 100644 index 00000000..749f4a45 --- /dev/null +++ b/macaroons/constraints.go @@ -0,0 +1,86 @@ +package macaroons + +import ( + "fmt" + "net" + "time" + + "gopkg.in/macaroon-bakery.v1/bakery/checkers" + macaroon "gopkg.in/macaroon.v1" +) + +// Constraint type adds a layer of indirection over macaroon caveats and +// checkers. +type Constraint func(*macaroon.Macaroon) + +// AddConstraints returns new derived macaroon by applying every passed +// constraint and tightening its restrictions. +func AddConstraints(mac *macaroon.Macaroon, cs ...Constraint) *macaroon.Macaroon { + newMac := mac.Clone() + for _, constraint := range cs { + constraint(newMac) + } + return newMac +} + +// Each *Constraint function is a functional option, which takes a pointer +// to the macaroon and adds another restriction to it. For each *Constraint, +// the corresponding *Checker is provided. + +// PermissionsConstraint restricts allowed operations set to the ones +// passed to it. +func PermissionsConstraint(ops ...string) func(*macaroon.Macaroon) { + return func(mac *macaroon.Macaroon) { + caveat := checkers.AllowCaveat(ops...) + mac.AddFirstPartyCaveat(caveat.Condition) + } +} + +// PermissionsChecker wraps default checkers.OperationChecker. +func PermissionsChecker(method string) checkers.Checker { + return checkers.OperationChecker(method) +} + +// TimeoutConstraint restricts the lifetime of the macaroon +// to the amount of seconds given. +func TimeoutConstraint(seconds int64) func(*macaroon.Macaroon) { + return func(mac *macaroon.Macaroon) { + macaroonTimeout := time.Duration(seconds) + requestTimeout := time.Now().Add(time.Second * macaroonTimeout) + caveat := checkers.TimeBeforeCaveat(requestTimeout) + mac.AddFirstPartyCaveat(caveat.Condition) + } +} + +// TimeoutChecker wraps default checkers.TimeBefore checker. +func TimeoutChecker() checkers.Checker { + return checkers.TimeBefore +} + +// IPLockConstraint locks macaroon to a specific IP address. +// If address is an empty string, this constraint does nothing to +// accommodate default value's desired behavior. +func IPLockConstraint(ipAddr string) func(*macaroon.Macaroon) { + return func(mac *macaroon.Macaroon) { + if ipAddr != "" { + macaroonIPAddr := net.ParseIP(ipAddr) + caveat := checkers.ClientIPAddrCaveat(macaroonIPAddr) + mac.AddFirstPartyCaveat(caveat.Condition) + } + } +} + +// IPLockChecker accepts client IP from the validation context and compares it +// with IP locked in the macaroon. +func IPLockChecker(clientIP string) checkers.Checker { + return checkers.CheckerFunc{ + Condition_: checkers.CondClientIPAddr, + Check_: func(_, cav string) error { + if !net.ParseIP(cav).Equal(net.ParseIP(clientIP)) { + msg := "macaroon locked to different IP address" + return fmt.Errorf(msg) + } + return nil + }, + } +}