/*
   * This action receives the form submission from the page for the final step of the signature process. We'll call
   * REST PKI to complete the signature.
   */
  @RequestMapping(
      value = "/xml-signature-complete",
      method = {RequestMethod.POST})
  public String complete(
      @RequestParam(value = "token", required = true) String token,
      @RequestParam(value = "signature", required = true) String signature,
      Model model)
      throws IOException, RestException {

    // Instantiate the FullXmlSignatureFinisher class, responsible for completing the signature
    // process. For more
    // information, see:
    // https://pki.rest/Content/docs/java-client/index.html?com/lacunasoftware/restpki/XmlSignatureFinisher.html
    XmlSignatureFinisher signatureFinisher = new XmlSignatureFinisher(Util.getRestPkiClient());

    // Set the token for this signature (rendered in a hidden input field, see file
    // templates/xml-full-signature.html)
    signatureFinisher.setToken(token);

    // Set the result of the signature operation
    signatureFinisher.setSignature(signature);

    // Call the finish() method, which finalizes the signature process and returns the signed XML's
    // bytes
    byte[] signedXml;
    try {
      signedXml = signatureFinisher.finish();
    } catch (ValidationException e) {
      // The call above may throw a ValidationException if any validation errors occur (for
      // instance, if the
      // certificate is revoked). If so, we'll render a page showing what went wrong.
      model.addAttribute("title", "Validation of the signature failed");
      // The toString() method of the ValidationResults object can be used to obtain the checks
      // performed, but the
      // string contains tabs and new line characters for formatting. Therefore, we call the method
      // Util.getValidationResultsHtml() to convert these characters to <br>'s and &nbsp;'s.
      model.addAttribute("vrHtml", Util.getValidationResultsHtml(e.getValidationResults()));
      return "validation-failed";
    }

    // Get information about the certificate used by the user to sign the file. This method must
    // only be called after
    // calling the finish() method.
    PKCertificate signerCert = signatureFinisher.getCertificateInfo();

    // At this point, you'd typically store the signed XML on your database. For demonstration
    // purposes, we'll
    // store the XML on a temporary folder and return to the page an identifier that can be used to
    // download it.

    String filename = UUID.randomUUID() + ".xml";
    Files.write(Application.getTempFolderPath().resolve(filename), signedXml);
    model.addAttribute("signerCert", signerCert);
    model.addAttribute("filename", filename);
    return "xml-signature-info";
  }
  /**
   * POST api/authentication?token=xxx
   *
   * <p>This action is called after signing the nonce on the client-side with the user's
   * certificate. We'll once again use the Authentication class to do the actual work.
   */
  @RequestMapping(
      value = "/api/authentication",
      method = {RequestMethod.POST})
  public AuthenticationPostResponse post(
      @RequestParam(value = "token", required = true) String token) throws RestException {

    // Instantiate the Authentication class
    Authentication auth = new Authentication(Util.getRestPkiClient());

    // Call the completeWithWebPki() method, which finalizes the authentication process. It receives
    // as input
    // only the token that was yielded previously (which we sent to the page and the page sent us
    // back on the URL).
    // The call yields a ValidationResults which denotes whether the authentication was successful
    // or not.
    ValidationResults vr = auth.completeWithWebPki(token);

    AuthenticationPostResponse response = new AuthenticationPostResponse();

    // Check the authentication result
    if (!vr.isValid()) {
      // If the authentication failed, inform the page
      response.setSuccess(false);
      response.setMessage("Authentication failed");
      response.setValidationResults(vr.toString());
      return response;
    }

    // At this point, you have assurance that the certificate is valid according to the
    // SecurityContext passed on the first step (see method get()) and that the user is indeed the
    // certificate's
    // subject. Now, you'd typically query your database for a user that matches one of the
    // certificate's fields, such as cert.getEmailAddress() or cert.getPkiBrazil().getCpf() (the
    // actual field
    // to be used as key depends on your application's business logic) and set the user
    // as authenticated with whatever web security framework your application uses.
    // For demonstration purposes, we'll just return a success and put on the message something
    // to show that we have access to the certificate's fields.

    PKCertificate userCert = auth.getPKCertificate();
    StringBuilder message = new StringBuilder();
    message.append("Welcome, " + userCert.getSubjectName().getCommonName() + "!");
    if (!StringUtils.isEmpty(userCert.getEmailAddress())) {
      message.append(" Your email address is " + userCert.getEmailAddress());
    }
    if (!StringUtils.isEmpty(userCert.getPkiBrazil().getCpf())) {
      message.append(" and your CPF is " + userCert.getPkiBrazil().getCpf());
    }

    // Return success to the page
    response.setSuccess(true);
    response.setMessage(message.toString());
    return response;
  }
  /**
   * GET api/authentication
   *
   * <p>This action is called once the user clicks the "Sign In" button.
   */
  @RequestMapping(
      value = "/api/authentication",
      method = {RequestMethod.GET})
  public String get() throws RestException {

    // Instantiate the Authentication class
    Authentication auth = new Authentication(Util.getRestPkiClient());

    // Call the Authentication startWithWebPki() method, which initiates the authentication. This
    // yields the token,
    // a 22-character case-sensitive URL-safe string, which we'll send to the page in order to pass
    // on the
    // signWithRestPki method of the Web PKI component.
    String token = auth.startWithWebPki(Util.getSecurityContext());

    // Note: By changing the SecurityContext above you can accept only certificates from a certain
    // PKI,
    // for instance, ICP-Brasil (SecurityContext.pkiBrazil).

    // Return the token to the page
    return token;
  }
  /*
   * This action receives the encoding of the certificate chosen by the user, uses it to initiate a XML signature
   * using REST PKI and renders the page for the final step of the signature process.
   */
  @RequestMapping(
      value = "/xml-full-signature",
      method = {RequestMethod.POST})
  public String postFull(
      @RequestParam(value = "selectedCertThumb", required = true) String selectedCertThumb,
      @RequestParam(value = "certificate", required = true) String certificate,
      Model model,
      HttpServletResponse response)
      throws IOException, RestException {

    // Instantiate the FullXmlSignatureStarter class, responsible for receiving the signature
    // elements and start the
    // signature process. For more information, see:
    // https://pki.rest/Content/docs/java-client/index.html?com/lacunasoftware/restpki/FullXmlSignatureStarter.html
    FullXmlSignatureStarter signatureStarter = new FullXmlSignatureStarter(Util.getRestPkiClient());

    // Set the XML to be signed, a sample XML Document
    signatureStarter.setXml(Util.getSampleXml());

    // Set the location on which to insert the signature node. If the location is not specified, the
    // signature will appended
    // to the root element (which is most usual with enveloped signatures).
    XmlNamespaceManager nsm = new XmlNamespaceManager();
    nsm.addNamespace("ls", "http://www.lacunasoftware.com/sample");
    signatureStarter.setSignatureElementLocation(
        "//ls:signaturePlaceholder", nsm, XmlInsertionOptions.AppendChild);

    // Set the certificate's encoding in base64 encoding (which is what the Web PKI component
    // yields)
    signatureStarter.setSignerCertificate(certificate);

    // Set the signature policy
    signatureStarter.setSignaturePolicy(SignaturePolicy.XadesBasic);

    // Set a SecurityContext to be used to determine trust in the certificate chain
    signatureStarter.setSecurityContext(SecurityContext.pkiBrazil);
    // Note: By changing the SecurityContext above you can accept only certificates from a certain
    // PKI,
    // for instance, ICP-Brasil (SecurityContext.pkiBrazil).

    // Call the start() method, which initiates the signature on REST PKI. This yields the
    // parameters for the
    // client-side signature, which we'll use to render the page for the final step, where the
    // actual signature will
    // be performed.
    ClientSideSignatureInstructions signatureInstructions;
    try {
      signatureInstructions = signatureStarter.start();
    } catch (ValidationException e) {
      // The call above may throw a ValidationException if the certificate fails the initial
      // validations (for
      // instance, if it is expired). If so, we'll render a page showing what went wrong.
      model.addAttribute("title", "Validation of the certificate failed");
      // The toString() method of the ValidationResults object can be used to obtain the checks
      // performed, but the
      // string contains tabs and new line characters for formatting. Therefore, we call the method
      // Util.getValidationResultsHtml() to convert these characters to <br>'s and &nbsp;'s.
      model.addAttribute("vrHtml", Util.getValidationResultsHtml(e.getValidationResults()));
      String retryUrl = "/xml-full-signature";
      model.addAttribute("retryUrl", retryUrl);
      return "validation-failed";
    }

    // Among the data returned by the start() method is the token, a string which identifies this
    // signature process.
    // This token can only be used for a single signature attempt. In order to retry the signature
    // it is
    // necessary to get a new token. This can be a problem if the user uses the back button of the
    // browser, since the
    // browser might show a cached page that we rendered previously, with a now stale token. To
    // prevent this from
    // happening, we call the method Util.setNoCacheHeaders(), which sets HTTP headers to prevent
    // caching of the page.
    Util.setNoCacheHeaders(response);

    // Render the page for the final step of the signature process, on which the actual signature
    // will be performed
    // (templates/xml-full-signature-step2.html)
    model.addAttribute("selectedCertThumb", selectedCertThumb);
    model.addAttribute("token", signatureInstructions.getToken());
    model.addAttribute("toSignHash", signatureInstructions.getToSignHash());
    model.addAttribute("digestAlg", signatureInstructions.getDigestAlgorithmOid());
    return "xml-full-signature-step2";
  }