Creating Secure Services

by Andrew Taylor <andy@benow.ca>
The BeNOW Service API works in conjunction with the BeNOW Security API to provide secure access to services. Services can be written in such a way as to allow access to services and service methods to specific users. Restrictions can be placed on services and methods which reflects their severity. This tutorial will demonstrate the construction and invocation of secure services. After this tutorial you'll be able to easily create services and methods which may only be accessed by those those you intend to access.
Contents
  1. Creating a Secure Service Interface
  2. Implementing a Secure Service
  3. Calling a Secure Service
Creating a Service Interface
Secure service interfaces are created similarly to simple services, but with a few more annotations. An example secure service interface is declared in src/java/test/org/benow/service/TutorialSecureService.java:
@Publish
@AnonymousAccess
public interface TutorialSecureService extends Service {
  /**
   * @return all known people
   */
  @AnonymousAccess
  public List<Person> getPeople();

  public List<Person> getPeopleOfHeight(
      @ParamName("heightInCMs");
      int heightInCMs);

  @RequiresPermission("delete")
  public boolean deletePerson(
      @ParamName("firstName")
      String firstName, 
      @ParamName("lastName")
      String lastName);
}
This secure service differs from the simple service of the previous tutorial in the extra annotations: @AnonymousAccess and @RequiresPermission(String).
@AnonymousAccess Annotation
The @AnonymousAccess annotation indicates that the associated service or method may be discovered or accessed anonymously (ie by the Anonymous user). The Anonymous user is the user which is associated with invocation if no user has been explicitly specified. The Anonymous user can be granted extra permissions (via the Security API), but typically can't do anything which requires permission.

The @AnonymousAccess annotation on the class indicates that no permissions are required to discover or access the service class. If no @AnonymousAccess annotation is given, then a default permission of [org.some.ServiceName].invoke is required to access the service. In this example, if no @AnonymousAccess annotation were given, test.org.benow.service.TutorialSecureService.invoke would be required to discover or access the service. For discovery, if the user did not have the test.org.benow.service.TutorialSecureService.invoke permission, then the service would not be included in the returned list of available services. As @AnonymousAccess is provided, the anonymous user can discover and access the Service.

The @AnonymousAccess annotation on the getPeople() method indicates that the anonymous user can invoke the method. Again, if that annotation were not present, the test.org.benow.service.TutorialSecureService.invoke permission would be required. Because the @AnonymousAccess annotation is provided on the Service and the getPeople() method, the anonymous user can get and invoke getPeople();.
Default methods
The getPeopleOfHeight(int) method has no annotation, which causes the Service framework to apply the default permission of test.org.benow.service.TutorialSecureService.invoke. Only users with this permission would be able to discover and invoke the method. If the anonymous user were to access the class (as they can due to the @AnonymousAccess on the class), they would not be able to discover or invoke the getPeopleOfHeight(int) method.
@RequiresPermission(String) Annotation
The deletePerson(String,String) method has a @RequiresPermission("delete") annotation, which indicates that the invoking user must have the test.org.benow.service.TutorialSecureService.delete permission. This permission must be explicitly assigned to the user via the security framework. Users without this permission will not be able to discover or access deletePerson(String,String). To assign a non local permission, specify the fully qualified permission name, ie @RequiresPermission("org.my.App.delete"), which would require org.my.App.delete as expected (and not test.org.benow.service.TutorialSecureService.org.my.App.delete).
Implementing a Secure Service
There are no special considerations for implementing a secure service. The service API takes care of all permission assertion during discovery, access and invocation. Simply implement the processing and it will or wont be available according to the user.
Security Notes
There's a few things to note about about the security api. Secured apps must be run within a secure thread. If they are not, the default user is considered to be the Administrator user (admin). The administrator passes all permission assertions... admin can do anything. If you are using the security api, including secure services, the code *must* be run within a SecureThread, either directly, or via a SecureApplication descendant. For more details on the Security API, including SecureThreads and SecureApplications, check out the Using the Security API.
The BeNOW tutorials are a work in progress. If you have any comments or suggestions, please email <andy@benow.ca>.