venerdì 29 luglio 2011

ImageHyperlink



Magari può essere utile sapere che esiste un oggetto Hyperlink fatto apposta per le immagini. Esempio:

ImageHyperlink logsExcelExport = new ImageHyperlink(parent, SWT.CENTER);

Image image = ImageDescriptor.createFromURL(Platform.getBundle(AssessmentBaseErrorCodes.ASSESSMENT_CLIENT_BASE_PLUGIN_ID).getEntry("icons/action-savexls.png")).createImage();
logsExcelExport.setImage(image);
logsExcelExport.setLayoutData(GridDataFactory.swtDefaults().align(SWT.END, SWT.END).hint(17, SWT.FILL).create());
logsExcelExport.setToolTipText("Esporta in Excel");


Come dice il nome, genera un'immagine cliccabile.

lunedì 18 luglio 2011

Testing methods secured with Spring Security

Spring Security allows to secure access to methods, allowing execution only for user with defined roles.

For example, using annotation to configure the roles required:

public interface MyService
{

 @PreAuthorize("hasRole('USER') OR hasRole('ADMIN')")
 void hello();

 @PreAuthorize("hasRole('ADMIN')")
 void bye();

}

In order to call the bye() method the user must have the ADMIN role, for hello() method also the the USER role is enough.

If a secured method is called by a user without the needed roles is raised an exception of type org.springframework.security.access.AccessDeniedException.

Method security is active even during tests, so we have developed an annotation and a listener that can be used to setup easily user roles when testing (we use TestNG, probably something like this can be done also with Junit).

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Authenticate {

 String username() default "";

 String[] roles() default {};
}

public class AuthenticationListener implements IInvokedMethodListener
{

 public static final String DEFAULT_USERNAME = "default";

 private static final String FAKE_PASSWORD = "fakePassword";

 /**
  * {@inheritDoc}
  */
 @Override
 public void beforeInvocation(IInvokedMethod method, ITestResult testResult)
 {
  if (method.isTestMethod())
  {
   ITestNGMethod testMethod = method.getTestMethod();
   Method javaMethod = testMethod.getMethod();
   Authenticate userAnnotation = javaMethod.getAnnotation(Authenticate.class);
   if (userAnnotation != null)
   {
    String username = userAnnotation.username();
    if (username == null || username.isEmpty())
    {
     username = DEFAULT_USERNAME;
    }
    String[] roles = userAnnotation.roles(); // role may be null/empty
    authenticateUser(username, roles);
   }
  }
 }

 private void authenticateUser(String username, String... roles)
 {
  Set<GrantedAuthority> grantedAuthorities = new HashSet<GrantedAuthority>();
  if (roles != null)
  {
   for (String role : roles)
   {
    grantedAuthorities.add(new GrantedAuthorityImpl(role));
   }
  }
  UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, FAKE_PASSWORD,
   grantedAuthorities);
  SecurityContextHolder.getContext().setAuthentication(token);
 }

 /**
  * {@inheritDoc}
  */
 @Override
 public void afterInvocation(IInvokedMethod method, ITestResult testResult)
 {
  if (method.isTestMethod())
  {
   SecurityContextHolder.clearContext();
  }
 }

}

The Authenticate annotation allows to set the username and a list of roles that must be used when executing a test method, the AuthenticationListener take care to configure the authentication token before test execution and to reset the security context after execution.

This is an example of a test method that uses the Authenticate annotation:

@ContextConfiguration("classpath:META-INF/spring/applicationContext*.xml")
@Listeners(AuthenticationListener.class)
public class MyServiceTest extends AbstractTestNGSpringContextTests
{

 @Autowired
 private MyService myService;

 @Test
 @Authenticate(username = "pippo", roles = {"ADMIN" })
 public void testCallHelloAsAdmin()
 {
  myService.hello();
 }

 @Test
 @Authenticate(username = "pippo", roles = {"USER" })
 public void testCallHelloAsUser()
 {
  myService.hello();
 }

 @Test
 @Authenticate(username = "pippo", roles = {"ADMIN" })
 public void testCallByeAsAdmin()
 {
  myService.bye();
 }

 @Test(expectedExceptions = AccessDeniedException.class)
 @Authenticate(username = "pippo", roles = {"USER" })
 public void testCallByeAsUser()
 {
  myService.bye();
 }
}


See also:
The demo project on GitHub
Spring Security
TestNG Listeners
Annotations

martedì 12 luglio 2011

Liquibase migration for Spring Security tables

Spring security default implementation requires to access to a database in order to do its job.

Here is the liquibase migration file for creating these tables.

The changesets 1, 2 and 2a create tables for authentication and authorization.
Changesets 3, 3a, 4, 5, 6 and 6a create tables for domain object security (ACL).
Tha last changeset, 7, insert some data in tables (2 users wioht their roles).



This migration is customized for MySql database, enforces the InnoDB engine when creates tables, but should work also on other databases.

See also:
Spring Security
Security Database Schema
Liquibase