cancel
Showing results for 
Search instead for 
Did you mean: 

File Owner?

nm_santos
Champ in-the-making
Champ in-the-making
Good afternoon,

I'm using a web service when accessing alfresco API to add/remove files, etc.

So, I have a web service user created for this, when using AuthenticationUtils.start() and end().

What I need to solve is, after a file is uploaded or edited, instead of appearing modified by web_service_user I want the username of the user to appear.

Can I do that? I read that there is an aspect called SET_OWNER, will that solve my problem?

The user passes it's username to my web service, so there is no problem there.

Regards,
Nuno.
9 REPLIES 9

jpotts
World-Class Innovator
World-Class Innovator
I don't think you'll be able to do this without authenticating as the user.

An exception would be if you called a custom web service (Java that is deployed to the Alfresco WAR) that used "runAs" to run as the user specified (specifically, org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork). That would do it. But letting arbitrary web service *clients* set the modified by property would be a security problem.

The ownable aspect has a property called cmSmiley Surprisedwner, which is used to set the owner of a document, but that has nothing to do with the "modified by" and "created by" properties.

Jeff

nm_santos
Champ in-the-making
Champ in-the-making
I don't think you'll be able to do this without authenticating as the user.

An exception would be if you called a custom web service (Java that is deployed to the Alfresco WAR) that used "runAs" to run as the user specified (specifically, org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork). That would do it. But letting arbitrary web service *clients* set the modified by property would be a security problem.

The ownable aspect has a property called cmSmiley Surprisedwner, which is used to set the owner of a document, but that has nothing to do with the "modified by" and "created by" properties.

Jeff

Hi Jeff,

Can you elaborate on your solution?

I couldn't find that class in the Alfresco Javadoc.

In creating my web service, what are the steps necessary?

I'm not sure how to start coding it. (just the part to run as user)

Regards,
Nuno.

jpotts
World-Class Innovator
World-Class Innovator
Look at this code. It is the source for com.someco.module.scripts.PostRating.java, which is an example Web Script controller from the Alfresco Developer Guide:
package com.someco.scripts;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.namespace.QName;
import org.springframework.extensions.webscripts.Status;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.apache.log4j.Logger;

import com.someco.model.SomeCoModel;
import com.someco.service.RatingService;

/**
* This is the controller for the rating.post web script.
*
* @author jpotts
*
*/
public class PostRating extends org.springframework.extensions.webscripts.DeclarativeWebScript {

   Logger logger = Logger.getLogger(PostRating.class);
         
   private NodeService nodeService;
   private RatingService ratingService;

   @Override
   protected Map<String, Object> executeImpl(WebScriptRequest req, Status status) {
      int ratingValue = -1;
      String id =   req.getParameter("id");
      String rating = req.getParameter("rating");
      String user = req.getParameter("user");

      try {
         ratingValue = Integer.parseInt(rating);
      } catch (NumberFormatException nfe) {
      }
      
      if (id == null || rating == null ||   user == null) {
         logger.debug("ID, rating, or user not set");
         status.setCode(Status.STATUS_BAD_REQUEST, "Required data has not been provided");
      } else if ((ratingValue < 1) || (ratingValue > 5)) {
         logger.debug("Rating out of range");
         status.setCode(Status.STATUS_BAD_REQUEST, "Rating value must be between 1 and 5 inclusive");
      } else {
         logger.debug("Getting current node");
         NodeRef curNode = new NodeRef("workspace://SpacesStore/" + id);   
         if (!nodeService.exists(curNode)) {
            logger.debug("Node not found");
            status.setCode(Status.STATUS_NOT_FOUND, "No node found for id:" + id);
         } else {
            // Refactored to use the Rating Service
            //create(curNode, Integer.parseInt(rating), user);
            ratingService.rate(curNode, Integer.parseInt(rating), user);
         }
      
      }

      logger.debug("Setting model");
      Map<String, Object> model = new HashMap<String, Object>();
      model.put("node", id);
      model.put("rating", rating);
      model.put("user", user);
      
      return model;
   }
   
   /**
    *
    * @param nodeRef
    * @param rating
    * @param user
    * @deprecated Use the Rating Service instead
    */
   protected void create(final NodeRef nodeRef, final int rating, final String user) {

      AuthenticationUtil.runAs(new RunAsWork<String>() {
         @SuppressWarnings("synthetic-access")
         public String doWork() throws Exception {
               // add the aspect to this document if it needs it      
               if (nodeService.hasAspect(nodeRef, SomeCoModel.ASPECT_SC_RATEABLE)) {
                  logger.debug("Document already has aspect");
               } else {
                  logger.debug("Adding rateable aspect");
                  nodeService.addAspect(nodeRef, SomeCoModel.ASPECT_SC_RATEABLE, null);
               }
               
               Map<QName, Serializable> props = new HashMap<QName, Serializable>();
               props.put(SomeCoModel.PROP_RATING, rating);
               props.put(SomeCoModel.PROP_RATER, user);
         
               nodeService.createNode(
                     nodeRef,
                     SomeCoModel.ASSN_SC_RATINGS,
                     QName.createQName(SomeCoModel.NAMESPACE_SOMECO_CONTENT_MODEL, SomeCoModel.PROP_RATING.getLocalName() + new Date().getTime()),
                     SomeCoModel.TYPE_SC_RATING,
                     props);
         
               logger.debug("Created node");
               return "";
         }
      },
      "admin");               
   }
   
   public void setNodeService(NodeService nodeService) {
      this.nodeService = nodeService;
   }

   public void setRatingService(RatingService ratingService) {
      this.ratingService = ratingService;
   }

}
The create method needs to be able to create new objects even when the person calling this web script might only have read privileges. So the method wraps its work in a "runAs" and tells Alfresco to run the code as "admin". But you could put any user there.

The full example lives at this Google Code project. You'll want the code from Chapter 6 that lives in the tag that matches the Alfresco version you are using. If you need a version that works with Alfresco 4, you'll have to start with the 3.4 version and make adjustments if needed.

Jeff

nm_santos
Champ in-the-making
Champ in-the-making
Hi Jeff,

I comprehend the logic behind runAs.

So, let me explain my strategy:

1. My client application will interact with my web service and uploads it's username and files to the web service.

2. The web service makes changes to the data with runAs.

Does my web service needs to implement any particular interface? If I can't use SOAP, I can try with Java RMI.

I'm not sure how I can make it run on Alfresco.

Regards,
Nuno.

jpotts
World-Class Innovator
World-Class Innovator
Alfresco ships with Apache Axis, which is a Web Service container. So if you want to write your own Web Service that runs within the Alfresco process and responds to clients via SOAP, I assume you should be able to do that, although I have never tried it.

The more common way people talk to Alfresco remotely is via RESTful web scripts. The Surf Web Script framework enables you to bind URLs with specific HTTP methods (GET, POST, PUT, DELETE) to controllers (written in your choice of Java or JavaScript) which then forward to views (written in FreeMarker) that render a response in a format specified by the caller (XML, JSON, HTML, RSS, etc.).

The calling app would not use SOAP or RMI but instead would use HttpClient (or something similar) to make the calls to the server.

The code I showed you earlier is an example of a Java-based controller for a web script. So you can go to the source code I referenced and see the other parts that make up a web script (descriptor, views).

Jeff

nm_santos
Champ in-the-making
Champ in-the-making
Hi Jeff,

Thanks for the help.

I've already taken a look at the Java Web Scripts Samples Smiley Happy

My only problem now is, how will I upload the files? All the examples I saw take data from the urls and what I need in my method are bytes.

The content of my files will be XML.

Is there a way with HTTP POST maybe?

Regards,
Nuno.

jpotts
World-Class Innovator
World-Class Innovator
The source code that accompanies the Alfresco Developer Guide includes a simple web script example that shows how to post a file from a basic web form. You can do the same from code using a post.

If you need to see the web script code, use svn to checkout this code:
svn co http://alfresco-developer-guide.googlecode.com/svn/tags/ch7_3.4_enterprise/client-extensions/ .

Then, navigate to config/alfresco/extension/templates/webscripts/com/someco and look at the helloworldform web script.

That code may need to be modified slightly to run on Alfresco 4.

If you need to know how to use HttpClient to post a multi-part form with a file attachment, try Google. Here is one resource, but there are many:
http://www.theserverside.com/news/1365153/HttpClient-and-FileUpload

Jeff

nm_santos
Champ in-the-making
Champ in-the-making
Hi Jeff,

Thanks for your help

I've already seen the HTTP Client documentation.

On my webscript, how can I catch a POST request?

I've already seen the strategy to encapsulate XML text in a HTTP Message Body, which is exactly what I need.

In my case I would use something like this:


private static String url =
         "http://localhost:8080/alfresco/service/fileUpload";
          HttpClient client = new HttpClient();
        PostMethod postMethod = new PostMethod(url);

       //upload file…


And then on the script?

What strategy can I use?

I've already read the javascript upload file, but I'm still lost 😕

Regards,
Nuno.

jpotts
World-Class Innovator
World-Class Innovator
The source of helloworldform.post.js is as follows:
for each (field in formdata.fields) {
        if (field.name == "name") {
                model.name = field.value;
        }
        if (field.name == "file" && field.isFile) {
                filename = field.filename;
                content = field.content;
                mimetype = field.mimetype;
        }
}
var results = search.luceneSearch("+PATH:\"app:company_home/*\" +TYPE:\"cm:folder\" +@cm\\:name:\"Someco\"");
var targetFolder = results[0];
var newDoc = targetFolder.createFile(filename);
newDoc.properties.content.write(content);
newDoc.properties.content.mimetype = mimetype;
newDoc.save();
The code iterates over the fields in the posted form and looks for the "name" and "file" fields. When it finds them, it saves them into variables. The search is used to find a folder to store the file in. The folder's "createFile" method is called to create a new document in Alfresco with the same name as the file that was uploaded. The next line writes the content, which stores the file's bytes in the document's "content" property. Then the mimetype is set and the document is saved.

Because the web script is named helloworld.post.js it will respond to POSTs. The other files that make up the web script are the descriptor (helloworldform.post.desc.xml) which tells the framework which URL to match for this web script as well as a few other settings and the view (helloworldform.post.html.ftl) which, in this case, is returning HTML after the POST happens.

Jeff