macaroons: add constraint/checker options layer

This commit is contained in:
whythat 2017-09-02 02:30:57 +03:00 committed by Olaoluwa Osuntokun
parent d65f17f1b1
commit 5c3493bd30

86
macaroons/constraints.go Normal file

@ -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
},
}
}