1
votes

I'm trying to expose a Jackrabbit Oak repository through a servlet. While I'm able to successfully retrieve and edit a .docx file with LibreOffice Writer, Microsoft Word does not work.

I'm making use of:

  • Jackrabbit Oak version 1.8.4
  • Jackrabbit version 2.17.5
  • LibreOffice Writer version 6.1.3.2

The version of Word not working is:

  • Version 1708
  • Microsoft Office 365 ProPlus.

I access the documents similarly in both editors by using the open document dialogue and putting in http://localhost:8080/helloworld-singleton/repository/default/test.docx

I've setup a simple servlet using the OpenSecurityProvider that exposes a repository that copies a local copy of a simple .docx document:

public class SimpleWebdavServlet extends SimpleWebdavServlet {
  private static Repository repository;

  public Repository getRepository() {
    return getRepositoryInternal();
  }

  static Repository getRepositoryInternal() {
    try {
      if (repository == null) {
        Jcr jcr = new Jcr().with(new OpenSecurityProvider());
        repository = jcr.createRepository();
        Session session = repository.login(new SimpleCredentials("admin", "admin".toCharArray()));

        Node rootNode = session.getRootNode();
        if (!rootNode.hasNode("test.docx")) {
          importFile(session, rootNode);
        }
        session.save();
      }
    } catch (FileNotFoundException | RepositoryException e) {
      e.printStackTrace();
    }
    return repository;
  }

  private static void importFile(Session session, Node rootNode) throws FileNotFoundException, RepositoryException {
    FileInputStream is = new FileInputStream(new File("C:\\test.docx"));
    ValueFactory valueFactory = session.getValueFactory();
    Binary contentValue = valueFactory.createBinary(is);
    Node fileNode = rootNode.addNode("test.docx", "nt:file");
    fileNode.addMixin("mix:referenceable");
    Node resNode = fileNode.addNode("jcr:content", "nt:resource");
    resNode.setProperty("jcr:mimeType", "application/octet-stream");
    resNode.setProperty("jcr:data", contentValue);
    Calendar lastModified = Calendar.getInstance();
    lastModified.setTimeInMillis(lastModified.getTimeInMillis());
    resNode.setProperty("jcr:lastModified", lastModified);
    session.save();
  }
}

This servlet is configured through the following web.xml:

<web-app>
  <display-name>Jackrabbit Webdav</display-name>
  <servlet>
    <servlet-name>Webdav</servlet-name>
    <servlet-class>org.jboss.as.quickstarts.singleton.SimpleWebdavServlet</servlet-class>
    <init-param>
      <param-name>resource-path-prefix</param-name>
      <param-value>/repository</param-value>
    </init-param>
    <init-param>
      <param-name>missing-auth-mapping</param-name>
      <param-value>admin:admin</param-value>
    </init-param>
    <init-param>
      <param-name>resource-config</param-name>
      <param-value>/WEB-INF/config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>Webdav</servlet-name>
    <url-pattern>/repository/*</url-pattern>
  </servlet-mapping>
</web-app>

With this code, I would have expected that I would be able to open the document in Word, edit it, and save the changes to the server - Like I'm able to in LibreOffice Writer.

Instead with Word, the document is opened with protected view, attempt to save changes, you're only prompted to save a local copy of it.

I've tried to make a similar servlet for another JCR Implementation ModeShape, and experienced no problems with the persisting documents through Word there.

As a possible explanation while debugging internally within Jackrabbit, a noticeable difference between the editors is that LibreOffice Writer sends over null in the Authorization header, while Word sends Bearer in the Authorization header for several of it's requests. All of these requests are then ignored due to the exception of org.apache.jackrabbit.webdav.DavException: Unable to decode authorization.

Does anybody know why Word does not work as expected in this case?

1
FWIW, but not related to your issue: don't use unstable Jackrabbit versions (2.17 == odd == unstable) in production code.Julian Reschke

1 Answers

0
votes

I confirm that "Authorization: Bearer" is not supported in jackrabbit-jcr-server. The BasicCredentialsProvider class only supports "login:password" base64 encoded basic authentication.

For testing purposes, I modified the getCredentials() method to force basic authentication with "admin/admin" when bearer authorization is received:

...
if (authStr.length >= 2 && authStr[0].equalsIgnoreCase(HttpServletRequest.BASIC_AUTH)) {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    Base64.decode(authStr[1].toCharArray(), out);
    String decAuthStr = out.toString("ISO-8859-1");
    int pos = decAuthStr.indexOf(':');
    String userid = decAuthStr.substring(0, pos);
    String passwd = decAuthStr.substring(pos + 1);
    return new SimpleCredentials(userid, passwd.toCharArray());
} else if (authStr.length >= 1 && authStr[0].equalsIgnoreCase("Bearer")) {
    return new SimpleCredentials("admin", "admin".toCharArray());
}
throw new ServletException("Unable to decode authorization.");
...

After that I recompiled the jackrabbit-jcr-server (using maven) and redeployed it, and then the direct edition with MS Word worked.

Now a better implementation should be done...