Skip navigation

In a recent project, we had the challenge of implementing SSL on certain APIs and restricting access to certain APIs by the state of the identity resource (RESTful description of what a certain consumer has done before like choose a country etc.).  Using custom annotations with pre-processors turned out to be a manageable and elegant solution.

 

The "SSL is required" annotation:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface SSL {


}

 

The "specific identity state is required" annotation:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdentityState {

          boolean anonymous() default false;

          boolean guest() default false;

          boolean registered() default false;
}

 

The SSL pre-processor:

 

SecurityContext context = ResteasyProviderFactory.getContextData(SecurityContext.class);
  if (!context.isSecure()) {
    if (request.getSession() != null) {
      // session would only be created if an identity state has been established and a token issued
      // Kill session as tokens may have been exposed
        request.getSession().invalidate();
        logger.info("SSL was required, but not found, killing session . . . ");
    }
    throw new UnauthorizedException("This API requires SSL");
  }

 

The identity state pre-processor:

 

if (remoteState == null ) { 
  throw new UnauthorizedException(
    "This API requires you to have called another API before"); 
}


if (!remoteState.getSite().equals(country)) {
  throw new UnauthorizedException(
    "The country ID must be the same as the one you used before - for example, 'US' or 'CA' "); 
}

 

Security note

Unexpected results may be produced by annotations at the method and the class level.  Use something like this in your accept() method to harmonize the two:

 

SSL ssl = (SSL) declaring.getAnnotation(SSL.class);
SSL methodssl = (SSL) method.getAnnotation(SSL.class);
if (methodssl != null) {
  ssl = methodssl;
}

Having struggled with this particular combination -- FileUpload and MediaOutput -- in the earlier versions of Richfaces 3.x, it was nice to see that Richfaces 4 has addressed some of the concerns, especially around these two areas:

 

1. Better parameter names and successful uploads of some files, and various minor improvements such as: on a multiple file upload, allows some files through even the total number of files exceeds the upload limit.

 

       <rich:fileUpload fileUploadListener="#{fileUploadBean.listener}" 
                  id="upload" acceptedTypes="txt,png,jpg">
             <a4j:ajax event="uploadcomplete" execute="@none" render="info" />
       </rich:fileUpload>

 

 

2. Content-Disposition header is now present and, in addition to inline presentation, also allows presentation of a download as a link to a Richfaces resource.  In this example, a text file is available for download as a link or for viewing in the browser, with the content having been generated through the 'generate' method on the backing bean.  This tag is embedded in a loop, and the 'row' is being passed to the generate method.

 

       <a4j:mediaOutput element="a" mimeType="text/plain"
              createContent="#{fileUploadBean.generate}" value="#{row}"
              charset="UTF-8" fileName="#{file.outputPageName}" 
              style="width:100px; height:100px;" cacheable="false">
              <f:param value="#{fileUploadBean.timeStamp}" name="time" />
              Download
          </a4j:mediaOutput>

 

However, as far as I have been able to find out, a content disposition of attachment is still not configurable.  This is the case when the user gets prompted for a download even if the browser has the capability to show the content inline, either intrinsically or though a plugin/extension.

A situation may arise in which:

--  a reverse-engineered model is being extended, and re-definition of columns is not allowed

and

-- Hibernate annotations are being used instead of configuration files

 

Here is how the encryption type can be defined through annotation at the package level so that it can be used in all entities, whether auto-generated or extended:

@org.hibernate.annotations.TypeDefs(
    {
@org.hibernate.annotations.TypeDef(
   name="encryptedString",
   typeClass=org.jasypt.hibernate.type.EncryptedStringType.class,
   parameters= {
       @org.hibernate.annotations.Parameter(name="encryptorRegisteredName", value="hibernateStringEncryptor")
   }
)
    }
)
package com.findax.model;

package-info.java:

 

@org.hibernate.annotations.TypeDefs(

    {

@org.hibernate.annotations.TypeDef(

    name="encryptedString",

    typeClass=org.jasypt.hibernate.type.EncryptedStringType.class,

    parameters= {

        @org.hibernate.annotations.Parameter(name="encryptorRegisteredName", value="hibernateStringEncryptor")

    }

)

    }

)

package com.example.model;