You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.8 KiB
89 lines
2.8 KiB
package macaroons |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"net" |
|
"time" |
|
|
|
"google.golang.org/grpc/peer" |
|
|
|
"gopkg.in/macaroon-bakery.v2/bakery/checkers" |
|
macaroon "gopkg.in/macaroon.v2" |
|
) |
|
|
|
// Constraint type adds a layer of indirection over macaroon caveats. |
|
type Constraint func(*macaroon.Macaroon) error |
|
|
|
// Checker type adds a layer of indirection over macaroon checkers. A Checker |
|
// returns the name of the checker and the checker function; these are used to |
|
// register the function with the bakery service's compound checker. |
|
type Checker func() (string, checkers.Func) |
|
|
|
// AddConstraints returns new derived macaroon by applying every passed |
|
// constraint and tightening its restrictions. |
|
func AddConstraints(mac *macaroon.Macaroon, cs ...Constraint) (*macaroon.Macaroon, error) { |
|
newMac := mac.Clone() |
|
for _, constraint := range cs { |
|
if err := constraint(newMac); err != nil { |
|
return nil, err |
|
} |
|
} |
|
return newMac, nil |
|
} |
|
|
|
// 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 if not provided by default. |
|
|
|
// TimeoutConstraint restricts the lifetime of the macaroon |
|
// to the amount of seconds given. |
|
func TimeoutConstraint(seconds int64) func(*macaroon.Macaroon) error { |
|
return func(mac *macaroon.Macaroon) error { |
|
macaroonTimeout := time.Duration(seconds) |
|
requestTimeout := time.Now().Add(time.Second * macaroonTimeout) |
|
caveat := checkers.TimeBeforeCaveat(requestTimeout) |
|
return mac.AddFirstPartyCaveat([]byte(caveat.Condition)) |
|
} |
|
} |
|
|
|
// 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) error { |
|
return func(mac *macaroon.Macaroon) error { |
|
if ipAddr != "" { |
|
macaroonIPAddr := net.ParseIP(ipAddr) |
|
if macaroonIPAddr == nil { |
|
return fmt.Errorf("incorrect macaroon IP-lock address") |
|
} |
|
caveat := checkers.Condition("ipaddr", |
|
macaroonIPAddr.String()) |
|
return mac.AddFirstPartyCaveat([]byte(caveat)) |
|
} |
|
return nil |
|
} |
|
} |
|
|
|
// IPLockChecker accepts client IP from the validation context and compares it |
|
// with IP locked in the macaroon. It is of the `Checker` type. |
|
func IPLockChecker() (string, checkers.Func) { |
|
return "ipaddr", func(ctx context.Context, cond, arg string) error { |
|
// Get peer info and extract IP address from it for macaroon |
|
// check. |
|
pr, ok := peer.FromContext(ctx) |
|
if !ok { |
|
return fmt.Errorf("unable to get peer info from context") |
|
} |
|
peerAddr, _, err := net.SplitHostPort(pr.Addr.String()) |
|
if err != nil { |
|
return fmt.Errorf("unable to parse peer address") |
|
} |
|
|
|
if !net.ParseIP(arg).Equal(net.ParseIP(peerAddr)) { |
|
msg := "macaroon locked to different IP address" |
|
return fmt.Errorf(msg) |
|
} |
|
return nil |
|
} |
|
}
|
|
|