Hello
i’m new in the atlassian developing and actually stuck with my web-item, x-work…
How can i restrict the access to my web-item, that it’s only visible when you’re logged in as admin?
Thank you rmassaioli
I just wrote my own condition! But is there a way to build the condition into the x-work module?
It works fine with displaying the web-item but when a user knows direct web-item link he can trigger it anyways…
You have to track all possible access to the code. Indeed, if a user knows the url, he has access unless code otherwise. You have to write your own checks in all access methods. I usually redirect to the admin login (if an admin page), the regular login or the dashboard.
EDIT : Found some old code for you
// Inject these two components
@ComponentImport
private LoginUriProvider loginUriProvider;
@ComponentImport
private PermissionManager permissionManager;
// then use them in access methods. Here's a simple example
public String doDefault() throws Exception {
ConfluenceUser user = this.getAuthenticatedUser();
if (this.permissionManager.isConfluenceAdministrator(user)) {
return INPUT;
} else {
URI uri = new URI("/yourAction.action");
return this.loginUriProvider.getLoginUriForRole(uri, UserRole.ADMIN).toString();
}
}
Writing access restriction on all methods under x-work results in a lot of boilerplate code. What if you want to change functionality after and you got 20 actions?
Interceptor comes handy in the above scenario.
I for one would like your sample code ! I’m always looking for ways to improve my code Do you mean like a servlet filter that checks privileges level for all you urls ?
EDIT : think I found what you aare referring to : example from Bamboo
Exactly that, and the same (some other way of course) should be applied for REST urls.
This is from production code, removed package names
atlassian-plugin.xml:
<xwork name="SomeName" key="SomeUniqueKey">
<description>Some description</description>
<package name="downloadActions" extends="default" namespace="/MY_NAME_SPACE">
<!-- declaration of interceptor -->
<interceptor name="validContractInterceptor"
class="my.full.package.ValidContractInterceptor"/>
<!-- end of declaration -->
<!-- declaration of stack, it uses the defaultStack and our inteceptor -->
<interceptor-stack name="validContractStack">
<interceptor-ref name="defaultStack"/>
<interceptor-ref name="validContractInterceptor"/>
</interceptor-stack>
<!-- end of declaration -->
<!-- use the stack we just created -->
<default-interceptor-ref name="validContractStack"/>
<!-- results coming from the interceptor action-->
<global-results>
<result name="NOTPERMITTED" type="dispatcher">/error/notpermitted.action</result>
<result name="HAS_UPDATES_WITHOUT_CONTRACT" type="velocity">/layouts/downloads/no_contract.vm</result>
<result name="SHOULD_UPDATE" type="velocity">/layouts/downloads/shouldUpdate.vm</result>
</global-results>
<!-- end of global results-->
<!-- action(s) protected by the interceptor -->
<action name="listUpdates" class="${project.groupId}.products.actions.ListUpgradesAction">
<result name="success" type="velocity">/layouts/downloads/valid_contract.vm</result>
</action>
</package>
</xwork>
The interceptor class looks like:
import com.atlassian.confluence.user.AuthenticatedUserThreadLocal;
import com.atlassian.confluence.user.ConfluenceUser;
import com.atlassian.confluence.user.UserAccessor;
import com.atlassian.confluence.user.UserDetailsManager;
import com.opensymphony.xwork.ActionInvocation;
import com.opensymphony.xwork.interceptor.Interceptor;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.Map;
import java.util.Optional;
public class ValidContractInterceptor implements Interceptor {
@Autowired
UserDetailsManager userDetailsManager;
@Autowired
UserAccessor userAccessor;
@Override
public void init() {
AutoWirer.autowire(this); //This is needed, at least in my version 5.8.8.
}
@Override
public void destroy() {
}
@Override
public String intercept(ActionInvocation invocation) throws Exception {
//You can get action params such as:
Map<String, String[]> params = invocation.getInvocationContext().getParameters();
//do work here
//....
//....
//Return some global result as declared in the xml such as:
return "NOTPERMITTED";
}
}
The AutoWirer class is a helper that helps autowiring via static methods
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
public class AutoWirer
{
private volatile static ApplicationContext applicationContext;
private AutoWirer()
{
}
public static Object autowire(Object bean)
{
applicationContext.getAutowireCapableBeanFactory().autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE, true);
return bean;
}
@Component
public static class Helper
{
@Autowired
public Helper(ApplicationContext applicationContext)
{
AutoWirer.applicationContext = applicationContext;
}
@PreDestroy
public void preDestroy()
{
AutoWirer.applicationContext = null;
}
}
}
In the case of REST, you need a provider such as:
@Provider
@Scanned
public class AdminOnlyResourceFilter implements ResourceFilter, ContainerRequestFilter {
private final AuthService authService;
@Inject
public AdminOnlyResourceFilter(AuthService authService) {
this.authService = authService;
}
public ContainerRequestFilter getRequestFilter() {
return this;
}
public ContainerResponseFilter getResponseFilter() {
return null;
}
public ContainerRequest filter(final ContainerRequest containerRequest) {
ConfluenceUser user = AuthenticatedUserThreadLocal.get();
if (authService.canAdmin(user)||authService.canOnlyyView(user))
return containerRequest;
throw new AuthenticationRequiredException();
}
}
As you might have guessed, the ResourceFilter ties to to path within the element in the xml file, not sure if/how you can uncouple it