// MergeUpdate copies a subset of information from the input Registration // into the Registration r. It returns true if an update was performed and the base object // was changed, and false if no change was made. func mergeUpdate(r *core.Registration, input core.Registration) bool { var changed bool // Note: we allow input.Contact to overwrite r.Contact even if the former is // empty in order to allow users to remove the contact associated with // a registration. Since the field type is a pointer to slice of pointers we // can perform a nil check to differentiate between an empty value and a nil // (e.g. not provided) value if input.Contact != nil && !contactsEqual(r, input) { r.Contact = input.Contact changed = true } // If there is an agreement in the input and it's not the same as the base, // then we update the base if len(input.Agreement) > 0 && input.Agreement != r.Agreement { r.Agreement = input.Agreement changed = true } if features.Enabled(features.AllowKeyRollover) && input.Key != nil { sameKey, _ := core.PublicKeysEqual(r.Key.Key, input.Key.Key) if !sameKey { r.Key = input.Key changed = true } } return changed }
// Registration is used by a client to submit an update to their registration. func (wfe *WebFrontEndImpl) Registration(response http.ResponseWriter, request *http.Request) { logEvent := wfe.populateRequestEvent(request) defer wfe.logRequestDetails(&logEvent) body, _, currReg, err := wfe.verifyPOST(request, true, core.ResourceRegistration) if err != nil { logEvent.Error = err.Error() respMsg := malformedJWS respCode := http.StatusBadRequest if err == sql.ErrNoRows { respMsg = unknownKey respCode = http.StatusForbidden } wfe.sendError(response, respMsg, err, respCode) return } logEvent.Requester = currReg.ID logEvent.Contacts = currReg.Contact // Requests to this handler should have a path that leads to a known // registration idStr := parseIDFromPath(request.URL.Path) id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Registration ID must be an integer", err, http.StatusBadRequest) return } else if id <= 0 { logEvent.Error = "Registration ID must be a positive non-zero integer" wfe.sendError(response, logEvent.Error, id, http.StatusBadRequest) return } else if id != currReg.ID { logEvent.Error = "Request signing key did not match registration key" wfe.sendError(response, logEvent.Error, "", http.StatusForbidden) return } var update core.Registration err = json.Unmarshal(body, &update) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Error unmarshaling registration", err, http.StatusBadRequest) return } if len(update.Agreement) > 0 && update.Agreement != wfe.SubscriberAgreementURL { logEvent.Error = fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", update.Agreement, wfe.SubscriberAgreementURL) wfe.sendError(response, logEvent.Error, nil, http.StatusBadRequest) return } // Registration objects contain a JWK object, which must be non-nil. We know // the key of the updated registration object is going to be the same as the // key of the current one, so we set it here. This ensures we can cleanly // serialize the update as JSON to send via AMQP to the RA. update.Key = currReg.Key // Ask the RA to update this authorization. updatedReg, err := wfe.RA.UpdateRegistration(currReg, update) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Unable to update registration", err, statusCodeFromError(err)) return } jsonReply, err := json.Marshal(updatedReg) if err != nil { logEvent.Error = err.Error() // StatusInternalServerError because we just generated the reg, it should be OK wfe.sendError(response, "Failed to marshal registration", err, http.StatusInternalServerError) return } response.Header().Set("Content-Type", "application/json") response.Header().Add("Link", link(wfe.NewAuthz, "next")) if len(wfe.SubscriberAgreementURL) > 0 { response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service")) } response.WriteHeader(http.StatusAccepted) response.Write(jsonReply) }
// NewRegistration is used by clients to submit a new registration/account func (wfe *WebFrontEndImpl) NewRegistration(response http.ResponseWriter, request *http.Request) { logEvent := wfe.populateRequestEvent(request) defer wfe.logRequestDetails(&logEvent) body, key, _, err := wfe.verifyPOST(request, false, core.ResourceNewReg) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, malformedJWS, err, http.StatusBadRequest) return } if existingReg, err := wfe.SA.GetRegistrationByKey(*key); err == nil { logEvent.Error = "Registration key is already in use" response.Header().Set("Location", fmt.Sprintf("%s%d", wfe.RegBase, existingReg.ID)) wfe.sendError(response, logEvent.Error, nil, http.StatusConflict) return } var init core.Registration err = json.Unmarshal(body, &init) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Error unmarshaling JSON", err, http.StatusBadRequest) return } if len(init.Agreement) > 0 && init.Agreement != wfe.SubscriberAgreementURL { logEvent.Error = fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL) wfe.sendError(response, logEvent.Error, nil, http.StatusBadRequest) return } init.Key = *key reg, err := wfe.RA.NewRegistration(init) if err != nil { logEvent.Error = err.Error() wfe.sendError(response, "Error creating new registration", err, statusCodeFromError(err)) return } logEvent.Requester = reg.ID logEvent.Contacts = reg.Contact // Use an explicitly typed variable. Otherwise `go vet' incorrectly complains // that reg.ID is a string being passed to %d. var id int64 = reg.ID regURL := fmt.Sprintf("%s%d", wfe.RegBase, id) responseBody, err := json.Marshal(reg) if err != nil { logEvent.Error = err.Error() // StatusInternalServerError because we just created this registration, it should be OK. wfe.sendError(response, "Error marshaling registration", err, http.StatusInternalServerError) return } response.Header().Add("Location", regURL) response.Header().Set("Content-Type", "application/json") response.Header().Add("Link", link(wfe.NewAuthz, "next")) if len(wfe.SubscriberAgreementURL) > 0 { response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service")) } response.WriteHeader(http.StatusCreated) response.Write(responseBody) // incr reg stat wfe.Stats.Inc("Registrations", 1, 1.0) }
// NewRegistration is used by clients to submit a new registration/account func (wfe *WebFrontEndImpl) NewRegistration(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) { body, key, _, err := wfe.verifyPOST(logEvent, request, false, core.ResourceNewReg) if err != nil { // verifyPOST handles its own setting of logEvent.Errors wfe.sendError(response, logEvent, malformedJWS, err, statusCodeFromError(err)) return } if existingReg, err := wfe.SA.GetRegistrationByKey(*key); err == nil { response.Header().Set("Location", fmt.Sprintf("%s%d", wfe.RegBase, existingReg.ID)) wfe.sendError(response, logEvent, "Registration key is already in use", nil, http.StatusConflict) return } var init core.Registration err = json.Unmarshal(body, &init) if err != nil { wfe.sendError(response, logEvent, "Error unmarshaling JSON", err, http.StatusBadRequest) return } if len(init.Agreement) > 0 && init.Agreement != wfe.SubscriberAgreementURL { msg := fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL) wfe.sendError(response, logEvent, msg, nil, http.StatusBadRequest) return } init.Key = *key init.InitialIP = net.ParseIP(request.Header.Get("X-Real-IP")) if init.InitialIP == nil { host, _, err := net.SplitHostPort(request.RemoteAddr) if err == nil { init.InitialIP = net.ParseIP(host) } else { logEvent.AddError("Couldn't parse RemoteAddr: %s", request.RemoteAddr) wfe.sendError(response, logEvent, "couldn't parse the remote (that is, the client's) address", nil, http.StatusInternalServerError) return } } reg, err := wfe.RA.NewRegistration(init) if err != nil { logEvent.AddError("unable to create new registration: %s", err) wfe.sendError(response, logEvent, "Error creating new registration", err, statusCodeFromError(err)) return } logEvent.Requester = reg.ID logEvent.Contacts = reg.Contact // Use an explicitly typed variable. Otherwise `go vet' incorrectly complains // that reg.ID is a string being passed to %d. regURL := fmt.Sprintf("%s%d", wfe.RegBase, reg.ID) responseBody, err := json.Marshal(reg) if err != nil { // StatusInternalServerError because we just created this registration, it should be OK. logEvent.AddError("unable to marshal registration: %s", err) wfe.sendError(response, logEvent, "Error marshaling registration", err, http.StatusInternalServerError) return } response.Header().Add("Location", regURL) response.Header().Set("Content-Type", "application/json") response.Header().Add("Link", link(wfe.NewAuthz, "next")) if len(wfe.SubscriberAgreementURL) > 0 { response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service")) } response.WriteHeader(http.StatusCreated) response.Write(responseBody) }
// Registration is used by a client to submit an update to their registration. func (wfe *WebFrontEndImpl) Registration(logEvent *requestEvent, response http.ResponseWriter, request *http.Request) { body, _, currReg, prob := wfe.verifyPOST(logEvent, request, true, core.ResourceRegistration) if prob != nil { // verifyPOST handles its own setting of logEvent.Errors wfe.sendError(response, logEvent, prob, nil) return } // Requests to this handler should have a path that leads to a known // registration idStr := parseIDFromPath(request.URL.Path) id, err := strconv.ParseInt(idStr, 10, 64) if err != nil { logEvent.AddError("registration ID must be an integer, was %#v", idStr) wfe.sendError(response, logEvent, probs.Malformed("Registration ID must be an integer"), err) return } else if id <= 0 { msg := fmt.Sprintf("Registration ID must be a positive non-zero integer, was %d", id) logEvent.AddError(msg) wfe.sendError(response, logEvent, probs.Malformed(msg), nil) return } else if id != currReg.ID { logEvent.AddError("Request signing key did not match registration key: %d != %d", id, currReg.ID) wfe.sendError(response, logEvent, probs.Unauthorized("Request signing key did not match registration key"), nil) return } var update core.Registration err = json.Unmarshal(body, &update) if err != nil { logEvent.AddError("unable to JSON parse registration: %s", err) wfe.sendError(response, logEvent, probs.Malformed("Error unmarshaling registration"), err) return } if len(update.Agreement) > 0 && update.Agreement != wfe.SubscriberAgreementURL { msg := fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", update.Agreement, wfe.SubscriberAgreementURL) logEvent.AddError(msg) wfe.sendError(response, logEvent, probs.Malformed(msg), nil) return } // Registration objects contain a JWK object, which must be non-nil. We know // the key of the updated registration object is going to be the same as the // key of the current one, so we set it here. This ensures we can cleanly // serialize the update as JSON to send via AMQP to the RA. update.Key = currReg.Key // Ask the RA to update this authorization. updatedReg, err := wfe.RA.UpdateRegistration(currReg, update) if err != nil { logEvent.AddError("unable to update registration: %s", err) wfe.sendError(response, logEvent, core.ProblemDetailsForError(err, "Unable to update registration"), err) return } jsonReply, err := json.Marshal(updatedReg) if err != nil { // ServerInternal because we just generated the reg, it should be OK logEvent.AddError("unable to marshal updated registration: %s", err) wfe.sendError(response, logEvent, probs.ServerInternal("Failed to marshal registration"), err) return } response.Header().Set("Content-Type", "application/json") response.Header().Add("Link", link(wfe.NewAuthz, "next")) if len(wfe.SubscriberAgreementURL) > 0 { response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service")) } response.WriteHeader(http.StatusAccepted) response.Write(jsonReply) }
// NewRegistration is used by clients to submit a new registration/account func (wfe *WebFrontEndImpl) NewRegistration(ctx context.Context, logEvent *requestEvent, response http.ResponseWriter, request *http.Request) { body, key, _, prob := wfe.verifyPOST(ctx, logEvent, request, false, core.ResourceNewReg) addRequesterHeader(response, logEvent.Requester) if prob != nil { // verifyPOST handles its own setting of logEvent.Errors wfe.sendError(response, logEvent, prob, nil) return } if existingReg, err := wfe.SA.GetRegistrationByKey(ctx, *key); err == nil { response.Header().Set("Location", wfe.relativeEndpoint(request, fmt.Sprintf("%s%d", regPath, existingReg.ID))) // TODO(#595): check for missing registration err wfe.sendError(response, logEvent, probs.Conflict("Registration key is already in use"), err) return } var init core.Registration err := json.Unmarshal(body, &init) if err != nil { wfe.sendError(response, logEvent, probs.Malformed("Error unmarshaling JSON"), err) return } if len(init.Agreement) > 0 && init.Agreement != wfe.SubscriberAgreementURL { msg := fmt.Sprintf("Provided agreement URL [%s] does not match current agreement URL [%s]", init.Agreement, wfe.SubscriberAgreementURL) wfe.sendError(response, logEvent, probs.Malformed(msg), nil) return } init.Key = *key init.InitialIP = net.ParseIP(request.Header.Get("X-Real-IP")) if init.InitialIP == nil { host, _, err := net.SplitHostPort(request.RemoteAddr) if err == nil { init.InitialIP = net.ParseIP(host) } else { logEvent.AddError("Couldn't parse RemoteAddr: %s", request.RemoteAddr) wfe.sendError(response, logEvent, probs.ServerInternal("couldn't parse the remote (that is, the client's) address"), nil) return } } reg, err := wfe.RA.NewRegistration(ctx, init) if err != nil { logEvent.AddError("unable to create new registration: %s", err) wfe.sendError(response, logEvent, core.ProblemDetailsForError(err, "Error creating new registration"), err) return } logEvent.Requester = reg.ID addRequesterHeader(response, reg.ID) logEvent.Contacts = reg.Contact // Use an explicitly typed variable. Otherwise `go vet' incorrectly complains // that reg.ID is a string being passed to %d. regURL := wfe.relativeEndpoint(request, fmt.Sprintf("%s%d", regPath, reg.ID)) responseBody, err := marshalIndent(reg) if err != nil { // ServerInternal because we just created this registration, and it // should be OK. logEvent.AddError("unable to marshal registration: %s", err) wfe.sendError(response, logEvent, probs.ServerInternal("Error marshaling registration"), err) return } response.Header().Add("Location", regURL) response.Header().Set("Content-Type", "application/json") response.Header().Add("Link", link(wfe.relativeEndpoint(request, newAuthzPath), "next")) if len(wfe.SubscriberAgreementURL) > 0 { response.Header().Add("Link", link(wfe.SubscriberAgreementURL, "terms-of-service")) } response.WriteHeader(http.StatusCreated) response.Write(responseBody) }
func (ra *MockRegistrationAuthority) NewRegistration(reg core.Registration, jwk jose.JsonWebKey) (core.Registration, error) { reg.Key = jwk return reg, nil }