Compare commits

...

4 Commits

16 changed files with 300 additions and 27 deletions

View File

@ -0,0 +1,30 @@
package com.inteligr8.alfresco.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation tells the framework to only execute the annotated method if
* the ACS authenticated user is authorized.
*
* @see com.inteligr8.alfresco.annotations.Authorizable
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(IfAuthorizeds.class)
public @interface IfAuthorized {
/**
* The authorities (users and/or user groups) to constrain for the
* authorization context. Only one authority needs to match. To
* require multiple authorities, use multiple @IfAuthorized
* annotations.
*
* @return An array of ACS authorities; empty means any authenticated user.
*/
String[] value() default "";
}

View File

@ -0,0 +1,14 @@
package com.inteligr8.alfresco.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface IfAuthorizeds {
IfAuthorized[] value();
}

View File

@ -9,9 +9,6 @@ import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.service.transaction.TransactionService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.DeclarePrecedence;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -41,7 +38,6 @@ import com.inteligr8.alfresco.annotations.util.TransactionalAnnotationAdapter;
* @see org.springframework.transaction.annotation.Transactional
* @see com.inteligr8.alfresco.annotations.TransactionalRetryable
*/
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
public abstract class AbstractRetryingTransactionAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@ -54,17 +50,24 @@ public abstract class AbstractRetryingTransactionAspect {
public abstract String getJtaInterfaceName();
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional) && execution(* *(..))")
public void isTransactionalAnnotated() {
}
/**
* A @Pointcunt annotation is not recognized in super classes.
*/
//@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional) && execution(* *(..))")
public abstract void isTransactionalAnnotated();
public abstract void isJtaTransactionalAnnotated();
@Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))")
public void isTransactionalRetryableAnnotated() {
}
@Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()")
/**
* A @Pointcunt annotation is not recognized in super classes.
*/
//@Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))")
public abstract void isTransactionalRetryableAnnotated();
/**
* An @Around annotation is not recognized in super classes.
*/
//@Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()")
public Object retryingTransactional(ProceedingJoinPoint joinPoint) throws Throwable {
this.logger.trace("retryingTransactional({})", joinPoint);

View File

@ -0,0 +1,92 @@
package com.inteligr8.alfresco.annotations.aspect;
import java.lang.reflect.Method;
import java.util.Set;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.util.collections.CollectionUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.extensions.webscripts.Description.RequiredAuthentication;
import org.springframework.extensions.webscripts.WebScriptException;
import org.springframework.http.HttpStatus;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.server.ResponseStatusException;
import com.inteligr8.alfresco.annotations.IfAuthorized;
import com.inteligr8.alfresco.annotations.context.WebScriptContext;
import net.sf.acegisecurity.GrantedAuthority;
/**
* This aspect implements the IfAuthorized annotation.
*
* @see com.inteligr8.alfresco.annotations.IfAuthorized
*/
@Aspect
public class IfAuthorizedAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired(required = false)
private WebScriptContext wscontext;
@Autowired(required = false)
private WebApplicationContext wacontext;
@Pointcut("@annotation(com.inteligr8.alfresco.annotations.IfAuthorized) && execution(* *(..))")
public void isIfAuthorizedAnnotated() {
}
@Around("isIfAuthorizedAnnotated()")
public Object ifAuthorized(ProceedingJoinPoint joinPoint) throws Throwable {
this.logger.trace("ifAuthorized({})", joinPoint);
if (this.wscontext != null && !RequiredAuthentication.user.equals(this.wscontext.getWebscript().getDescription().getRequiredAuthentication()))
return joinPoint.proceed();
if (!(joinPoint.getSignature() instanceof MethodSignature))
throw new IllegalStateException("The @IfAuthorized annotation must be on methods and methods have signatures");
MethodSignature methodSig = (MethodSignature) joinPoint.getSignature();
Method method = methodSig.getMethod();
IfAuthorized[] ifauths = method.getAnnotationsByType(IfAuthorized.class);
for (IfAuthorized ifauth : ifauths) {
if (ifauth.value() != null && ifauth.value().length > 0) {
if (!this.isAuthorized(ifauth.value()))
return this.unauthorized();
}
}
return joinPoint.proceed();
}
protected boolean isAuthorized(String[] permittedAuthorities) {
Set<String> permittedAuthoritiesSet = CollectionUtils.asSet(permittedAuthorities);
GrantedAuthority[] authenticatedAuthorities = AuthenticationUtil.getFullAuthentication().getAuthorities();
for (GrantedAuthority auth : authenticatedAuthorities) {
if (permittedAuthoritiesSet.contains(auth.getAuthority()))
return true;
}
return false;
}
protected Object unauthorized() {
if (this.wscontext != null) {
throw new WebScriptException(HttpStatus.FORBIDDEN.value(), "The authenticated user is not authorized to use this resource");
} else if (this.wacontext != null) {
throw new ResponseStatusException(HttpStatus.FORBIDDEN, "The authenticated user is not authorized to use this resource");
} else {
return null;
}
}
}

View File

@ -0,0 +1,50 @@
package com.inteligr8.alfresco.annotations.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;
import org.springframework.extensions.webscripts.WebScript;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
import org.springframework.web.context.WebApplicationContext;
import com.inteligr8.alfresco.annotations.context.WebScriptContext;
/**
* This aspect captures the WebScript execution context.
*/
@Aspect
public class WebScriptAspect {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private ThreadLocal<WebScriptContext> context = new ThreadLocal<>();
@Pointcut("execution(public void org.springframework.extensions.webscripts.WebScript.execute(..))")
public void isWebScriptExecute() {
}
@Around("isWebScriptExecute()")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
this.logger.trace("execute({})", joinPoint);
WebScript ws = (WebScript) joinPoint.getTarget();
WebScriptRequest req = (WebScriptRequest) joinPoint.getArgs()[0];
WebScriptResponse res = (WebScriptResponse) joinPoint.getArgs()[1];
this.context.set(new WebScriptContext(ws, req, res));
return joinPoint.proceed();
}
@Bean
@Scope(WebApplicationContext.SCOPE_REQUEST)
public WebScriptContext getContext() {
return this.context.get();
}
}

View File

@ -0,0 +1,31 @@
package com.inteligr8.alfresco.annotations.context;
import org.springframework.extensions.webscripts.WebScript;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;
public class WebScriptContext {
private final WebScript webscript;
private final WebScriptRequest request;
private final WebScriptResponse response;
public WebScriptContext(WebScript webscript, WebScriptRequest request, WebScriptResponse response) {
this.webscript = webscript;
this.request = request;
this.response = response;
}
public WebScript getWebscript() {
return webscript;
}
public WebScriptRequest getRequest() {
return request;
}
public WebScriptResponse getResponse() {
return response;
}
}

View File

@ -6,13 +6,14 @@
<aspect name="com.inteligr8.alfresco.annotations.aspect.ThreadedAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.AsyncAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.IfAuthorizedAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.JobLockAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.OperableNodeAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.NodeTypeAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.NodeAspectAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.ChildIsPrimaryAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect" />
<aspect name="com.inteligr8.alfresco.annotations.aspect.WebScriptAspect" />
</aspects>
</aspectj>

View File

@ -16,6 +16,7 @@
<properties>
<alfresco.platform.version>23.2.1</alfresco.platform.version>
<alfresco.platform.war.version>23.2.0.60</alfresco.platform.war.version>
<tomcat-rad.version>10-2.1</tomcat-rad.version>
</properties>
<dependencies>
@ -29,6 +30,11 @@
<artifactId>jakarta.transaction-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -1,6 +1,9 @@
package com.inteligr8.alfresco.annotations.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import org.aspectj.lang.annotation.Pointcut;
import jakarta.transaction.Transactional;
@ -9,6 +12,7 @@ import jakarta.transaction.Transactional;
* @see jakarta.transaction.Transactional
*/
@Aspect
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
public class RetryingTransactionAspect extends AbstractRetryingTransactionAspect {
@Override
@ -16,8 +20,21 @@ public class RetryingTransactionAspect extends AbstractRetryingTransactionAspect
return Transactional.class.getName();
}
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional) && execution(* *(..))")
public void isTransactionalAnnotated() {
}
@Pointcut("@annotation(jakarta.transaction.Transactional) && execution(* *(..))")
public void isJtaTransactionalAnnotated() {
}
@Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))")
public void isTransactionalRetryableAnnotated() {
}
@Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()")
public Object retryingTransactional(ProceedingJoinPoint joinPoint) throws Throwable {
return super.retryingTransactional(joinPoint);
}
}

View File

@ -1,21 +1,21 @@
package com.inteligr8.alfresco.annotations.util;
import jakarta.transaction.Transactional;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
public class JtaTransactionalAnnotationAdapter implements TransactionalAnnotationAdapter {
import jakarta.transaction.Transactional;
public class JakartaTransactionalAnnotationAdapter implements JtaTransactionalAnnotationAdapter {
public static final String JTA_INTERFACE_NAME = "jakarta.transaction.Transactional";
private final Transactional txl;
public static JtaTransactionalAnnotationAdapter cast(Object obj) {
return new JtaTransactionalAnnotationAdapter((Transactional) obj);
public static JakartaTransactionalAnnotationAdapter cast(Object obj) {
return new JakartaTransactionalAnnotationAdapter((Transactional) obj);
}
public JtaTransactionalAnnotationAdapter(Transactional txl) {
public JakartaTransactionalAnnotationAdapter(Transactional txl) {
this.txl = txl;
}

View File

@ -0,0 +1,5 @@
<aspectj>
<aspects>
<aspect name="com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect" />
</aspects>
</aspectj>

View File

@ -1,7 +1,8 @@
module.id=${project.groupId}.${project.artifactId}
module.alias=${project.groupId}.annotations-platform-module
module.title=${project.name}
module.description=${project.description}
module.version=${project.version}
module.version=${module.version}
module.repo.version.min=6.0
#module.repo.version.max=

View File

@ -1,12 +1,16 @@
package com.inteligr8.alfresco.annotations.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import org.aspectj.lang.annotation.Pointcut;
/**
* @see javax.transaction.Transactional
*/
@Aspect
@DeclarePrecedence("com.inteligr8.alfresco.annotations.aspect.AuthorizedAspect, com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect")
public class RetryingTransactionAspect extends AbstractRetryingTransactionAspect {
@Override
@ -14,8 +18,21 @@ public class RetryingTransactionAspect extends AbstractRetryingTransactionAspect
return javax.transaction.Transactional.class.getName();
}
@Pointcut("@annotation(org.springframework.transaction.annotation.Transactional) && execution(* *(..))")
public void isTransactionalAnnotated() {
}
@Pointcut("@annotation(javax.transaction.Transactional) && execution(* *(..))")
public void isJtaTransactionalAnnotated() {
}
@Pointcut("@annotation(com.inteligr8.alfresco.annotations.TransactionalRetryable) && execution(* *(..))")
public void isTransactionalRetryableAnnotated() {
}
@Around("isTransactionalAnnotated() || isJtaTransactionalAnnotated() || isTransactionalRetryableAnnotated()")
public Object retryingTransactional(ProceedingJoinPoint joinPoint) throws Throwable {
return super.retryingTransactional(joinPoint);
}
}

View File

@ -5,17 +5,17 @@ import javax.transaction.Transactional;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
public class JtaTransactionalAnnotationAdapter implements TransactionalAnnotationAdapter {
public class JavaxTransactionalAnnotationAdapter implements JtaTransactionalAnnotationAdapter {
public static final String JTA_INTERFACE_NAME = "javax.transaction.Transactional";
private final Transactional txl;
public static JtaTransactionalAnnotationAdapter cast(Object obj) {
return new JtaTransactionalAnnotationAdapter((Transactional) obj);
public static JavaxTransactionalAnnotationAdapter cast(Object obj) {
return new JavaxTransactionalAnnotationAdapter((Transactional) obj);
}
public JtaTransactionalAnnotationAdapter(Transactional txl) {
public JavaxTransactionalAnnotationAdapter(Transactional txl) {
this.txl = txl;
}

View File

@ -0,0 +1,5 @@
<aspectj>
<aspects>
<aspect name="com.inteligr8.alfresco.annotations.aspect.RetryingTransactionAspect" />
</aspects>
</aspectj>

View File

@ -1,7 +1,8 @@
module.id=${project.groupId}.${project.artifactId}
module.alias=${project.groupId}.annotations-platform-module
module.title=${project.name}
module.description=${project.description}
module.version=${project.version}
module.version=${module.version}
module.repo.version.min=6.0
#module.repo.version.max=