func (s *macaroonStore) MacaroonIdInfo(ctxt context.Context, id []byte) (rootKey []byte, ops []auth.Op, err error) { var mid macaroonId if err := json.Unmarshal(id, &mid); err != nil { return nil, nil, errgo.Notef(err, "bad macaroon id") } rootKey, err = s.store.Get(mid.Id) if err != nil { return nil, nil, errgo.Notef(err, "cannot find root key") } return rootKey, mid.Ops, nil }
func (s *macaroonStore) NewMacaroon(ops []auth.Op, caveats []checkers.Caveat) (*macaroon.Macaroon, error) { rootKey, id, err := s.store.RootKey() if err != nil { return nil, errgo.Mask(err) } mid := macaroonId{ Id: id, Ops: ops, } data, _ := json.Marshal(mid) m, err := macaroon.New(rootKey, data, "", macaroon.LatestVersion) if err != nil { return nil, errgo.Mask(err) } for _, cav := range caveats { if err := bakery.AddCaveat(s.key, s.locator, m, cav); err != nil { return nil, errgo.Notef(err, "cannot add caveat") } } return m, nil }
// allowAny is the internal version of AllowAny. Instead of returning an // authInfo struct, it returns a slice describing which operations have // been successfully authorized and a slice describing which macaroons // have been used in the authorization. func (a *Authorizer) allowAny(ctxt context.Context, ops []Op) (authed, used []bool, err error) { if err := a.init(ctxt); err != nil { return nil, nil, errgo.Mask(err) } logger.Infof("after authorizer init, identity %#v", a.identity) used = make([]bool, len(a.macaroons)) authed = make([]bool, len(ops)) numAuthed := 0 for i, op := range ops { if op == LoginOp && len(ops) > 1 { // LoginOp cannot be combined with other operations in the // same macaroon, so ignore it if it is. continue } for _, mindex := range a.authIndexes[op] { _, err := a.checkConditions(ctxt, op, a.conditions[mindex]) if err != nil { logger.Infof("caveat check failed: %v", err) // log error? continue } authed[i] = true numAuthed++ used[mindex] = true break } } if a.identity != nil { // We've authenticated as a user, so even if the operations didn't // specifically require it, we add the authn macaroon and its // conditions to the macaroons used and its con indexes := a.authIndexes[LoginOp] if len(indexes) == 0 { // Should never happen because init ensures it's there. panic("no macaroon info found for login op") } // Note: because we never issue a macaroon which combines LoginOp // with other operations, if the login op macaroon is used, we // know that it's already checked out successfully with LoginOp, // so no need to check again. used[indexes[0]] = true } if numAuthed == len(ops) { // All operations allowed. return nil, used, nil } // There are some unauthorized operations. need := make([]Op, 0, len(ops)-numAuthed) needIndex := make([]int, cap(need)) for i, ok := range authed { if !ok { needIndex[len(need)] = i need = append(need, ops[i]) } } logger.Infof("operations needed after authz macaroons: %#v", need) // Try to authorize the operations even even if we haven't got an authenticated user. oks, caveats, err := a.service.p.UserChecker.Allow(ctxt, a.identity, need) if err != nil { return authed, used, errgo.Notef(err, "cannot check permissions") } if len(oks) != len(need) { return authed, used, errgo.Newf("unexpected slice length returned from Allow (got %d; want %d)", len(oks), len(need)) } stillNeed := make([]Op, 0, len(need)) for i, ok := range oks { if ok { authed[needIndex[i]] = true } else { stillNeed = append(stillNeed, ops[needIndex[i]]) } } if len(stillNeed) == 0 && len(caveats) == 0 { // No more ops need to be authenticated and no caveats to be discharged. return authed, used, nil } logger.Infof("operations still needed after auth check: %#v", stillNeed) if a.identity == nil { // User hasn't authenticated - ask them to do so. return authed, used, &DischargeRequiredError{ Message: "authentication required", Ops: []Op{LoginOp}, Caveats: a.service.p.IdentityClient.IdentityCaveats(), } } if len(caveats) == 0 { return authed, used, ErrPermissionDenied } return authed, used, &DischargeRequiredError{ Message: "some operations have extra caveats", Ops: ops, Caveats: caveats, } }