// userHandler handles requests to add new users, change user details, etc. // It is only accessible to users that are members of the admin group. func (h *handler) userHandler(_ http.Header, req *http.Request) (interface{}, error) { ctxt := h.newContext(req, "change-user") breq := h.svc.NewRequest(req, ctxt) err := breq.Check() if err != nil { // We issue a macaroon with a third-party caveat targetting // the id service itself. This means that the flow for self-created // macaroons is just the same as for any other service. // Theoretically, we could just redirect the user to the // login page, but that would require a different flow // and it's not clear that it would be an advantage. m, err := h.svc.NewMacaroon("", nil, []bakery.Caveat{ checkers.ThirdParty(h.svc.Location(), "member-of-group admin"), checkers.FirstParty("operation change-user"), }) if err != nil { return nil, errgo.Notef(err, "cannot mint new macaroon") } return nil, &httpbakery.Error{ Message: err.Error(), Code: httpbakery.ErrDischargeRequired, Info: &httpbakery.ErrorInfo{ Macaroon: m, }, } } // PUT /user/$user - create new user // PUT /user/$user/group-membership - change group membership of user return nil, errgo.New("not implemented yet") }
// writeError writes an error to w. If the error was generated because // of a required macaroon that the client does not have, we mint a // macaroon that, when discharged, will grant the client the // right to execute the given operation. // // The logic in this function is crucial to the security of the service // - it must determine for a given operation what caveats to attach. func (srv *targetServiceHandler) writeError(w http.ResponseWriter, operation string, verr error) { fail := func(code int, msg string, args ...interface{}) { if code == http.StatusInternalServerError { msg = "internal error: " + msg } http.Error(w, fmt.Sprintf(msg, args...), code) } if _, ok := verr.(*bakery.VerificationError); !ok { fail(http.StatusForbidden, "%v", verr) return } // Work out what caveats we need to apply for the given operation. // Could special-case the operation here if desired. caveats := []bakery.Caveat{ checkers.TimeBefore(time.Now().Add(5 * time.Minute)), checkers.ThirdParty(srv.authEndpoint, "access-allowed"), checkers.FirstParty("operation " + operation), } // Mint an appropriate macaroon and send it back to the client. m, err := srv.svc.NewMacaroon("", nil, caveats) if err != nil { fail(http.StatusInternalServerError, "cannot mint macaroon: %v", err) return } httpbakery.WriteDischargeRequiredError(w, m, verr) }