Skip to content

Instantly share code, notes, and snippets.

@sody
Created January 12, 2012 12:46
Show Gist options
  • Save sody/1600306 to your computer and use it in GitHub Desktop.
Save sody/1600306 to your computer and use it in GitHub Desktop.
Secure pages with tapestry5. Rethinking
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Authority {
/**
* Defines authority needed to access the page or action.
*/
String value();
}
public class AuthorityAnnotationWorker implements ComponentClassTransformWorker2 {
private final SecurityContext securityContext;
public AuthorityAnnotationWorker(final SecurityContext securityContext) {
this.securityContext = securityContext;
}
public void transform(final PlasticClass plasticClass, final TransformationSupport support, final MutableComponentModel model) {
final Authority authority = plasticClass.getAnnotation(Authority.class);
if (authority != null) {
final PlasticMethod dispatchMethod = plasticClass.introduceMethod(DISPATCH_COMPONENT_EVENT_DESCRIPTION);
dispatchMethod.addAdvice(new MethodAdvice() {
public void advise(final MethodInvocation invocation) {
final ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
// need to check only on activate event
if (event.matches(EventConstants.ACTIVATE, "", 0)) {
checkAuthority(authority);
}
invocation.proceed();
}
});
}
}
private void checkAuthority(final Authority authority) {
if (authority != null) {
final Authentication authentication = securityContext.getCurrentUser();
// is anonymous
if (authentication == null) {
throw new AuthenticationException("Not authenticated");
}
// check if current user has defined authority
if (!authentication.hasAuthority(authority.value())) {
throw new AccessDeniedException(String.format("Access denied. Need authority: '%s'", authority.value()));
}
}
}
}
public class AuthorityAnnotationWorker implements ComponentClassTransformWorker2 {
private final SecurityContext securityContext;
public AuthorityAnnotationWorker(final SecurityContext securityContext) {
this.securityContext = securityContext;
}
public void transform(final PlasticClass plasticClass, final TransformationSupport support, final MutableComponentModel model) {
final Authority authority = plasticClass.getAnnotation(Authority.class);
if (model.isPage() && authority != null) {
support.addEventHandler(EventConstants.ACTIVATE, 0, "Page Security", new ComponentEventHandler() {
public void handleEvent(final Component instance, final ComponentEvent event) {
checkAuthority(authority);
}
});
}
}
private void checkAuthority(final Authority authority) {
if (authority != null) {
final Authentication authentication = securityContext.getCurrentUser();
// is anonymous
if (authentication == null) {
throw new AuthenticationException("Not authenticated");
}
// check if current user has defined authority
if (!authentication.hasAuthority(authority.value())) {
throw new AccessDeniedException(String.format("Access denied. Need authority: '%s'", authority.value()));
}
}
}
}
@Contribute(ComponentClassTransformWorker2.class)
public static void contributeComponentClassTransformWorker(final OrderedConfiguration<ComponentClassTransformWorker2> configuration) {
configuration.addInstance("AuthorityAnnotationWorker", AuthorityAnnotationWorker.class, "after:OnEvent");
}
public class AuthorityAnnotationWorker implements ComponentClassTransformWorker2 {
private final SecurityContext securityContext;
public AuthorityAnnotationWorker(final SecurityContext securityContext) {
this.securityContext = securityContext;
}
public void transform(final PlasticClass plasticClass, final TransformationSupport support, final MutableComponentModel model) {
final Authority authority = plasticClass.getAnnotation(Authority.class);
if (authority != null) {
final PlasticMethod dispatchMethod = plasticClass.introduceMethod(DISPATCH_COMPONENT_EVENT_DESCRIPTION);
dispatchMethod.addAdvice(new MethodAdvice() {
public void advise(final MethodInvocation invocation) {
final ComponentEvent event = (ComponentEvent) invocation.getParameter(0);
// need to check only on activate event
if (event.matches(EventConstants.ACTIVATE, "", 0)) {
checkAuthority(authority);
}
invocation.proceed();
}
});
}
// add event handlers security checks
final List<PlasticMethod> methods = plasticClass.getMethodsWithAnnotation(Authority.class);
for (PlasticMethod method : methods) {
final Authority eventAuthority = method.getAnnotation(Authority.class);
method.addAdvice(new MethodAdvice() {
public void advise(final MethodInvocation invocation) {
checkAuthority(eventAuthority);
invocation.proceed();
}
});
}
}
private void checkAuthority(final Authority authority) {
if (authority != null) {
final Authentication authentication = securityContext.getCurrentUser();
// is anonymous
if (authentication == null) {
throw new AuthenticationException("Not authenticated");
}
// check if current user has defined authority
if (!authentication.hasAuthority(authority.value())) {
throw new AccessDeniedException(String.format("Access denied. Need authority: '%s'", authority.value()));
}
}
}
}
public class SecuredPageActivator implements PageActivator {
private final PageActivator activator;
private final SecurityContext securityContext;
public SecuredPageActivator(final PageActivator activator,
final SecurityContext securityContext) {
this.activator = activator;
this.securityContext = securityContext;
}
public boolean activatePage(final ComponentResources pageResources,
final EventContext activationContext,
final ComponentEventResultProcessor resultProcessor) throws IOException {
// get annotation from page class if present
final Authority authority = pageResources.getPage().getClass().getAnnotation(Authority.class);
if (authority != null) {
final Authentication authentication = securityContext.getCurrentUser();
// is anonymous
if (authentication == null) {
throw new AuthenticationException("Not authenticated");
}
// check if current user has defined authority
if (!authentication.hasAuthority(authority.value())) {
throw new AccessDeniedException(String.format("Access denied. Need authority: '%s'", authority.value()));
}
}
return activator.activatePage(pageResources, activationContext, resultProcessor);
}
}
@Decorate(serviceInterface = PageActivator.class)
public PageActivator decoratePageActivator(final PageActivator activator,
final SecurityContext securityContext) {
return new SecuredPageActivator(activator, securityContext);
}
public class SecurityFilter implements ComponentRequestFilter {
private final SecurityContext securityContext;
private final ComponentSource componentSource;
public SecurityFilter(final SecurityContext securityContext, final ComponentSource componentSource) {
this.securityContext = securityContext;
this.componentSource = componentSource;
}
public void handleComponentEvent(final ComponentEventRequestParameters parameters,
final ComponentRequestHandler handler) throws IOException {
checkAccessToPage(parameters.getActivePageName());
handler.handleComponentEvent(parameters);
}
public void handlePageRender(final PageRenderRequestParameters parameters,
final ComponentRequestHandler handler) throws IOException {
checkAccessToPage(parameters.getLogicalPageName());
handler.handlePageRender(parameters);
}
private void checkAccessToPage(final String pageName) {
// get annotation from page class if present
final Authority authority = componentSource.getPage(pageName).getClass().getAnnotation(Authority.class);
if (authority != null) {
final Authentication authentication = securityContext.getCurrentUser();
// is anonymous
if (authentication == null) {
throw new AuthenticationException("Not authenticated");
}
// check if current user has defined authority
if (!authentication.hasAuthority(authority.value())) {
throw new AccessDeniedException(String.format("Access denied. Need authority: '%s'", authority.value()));
}
}
}
}
@Contribute(ComponentRequestHandler.class)
public static void contributeComponentRequestHandler(final OrderedConfiguration<ComponentRequestFilter> configuration) {
configuration.addInstance("SecurityFilter", SecurityFilter.class);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment