All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.almworks.jira.structure.api.auth.StructureAuth Maven / Gradle / Ivy
package com.almworks.jira.structure.api.auth;
import com.almworks.jira.structure.api.structure.Structure;
import com.almworks.jira.structure.api.util.*;
import com.almworks.jira.structure.api.view.StructureView;
import io.atlassian.fugue.Option;
import io.atlassian.fugue.Pair;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.jira.user.ApplicationUser;
import com.atlassian.jira.util.log.Log4jKit;
import org.apache.log4j.MDC;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
import static io.atlassian.fugue.Option.option;
import static io.atlassian.fugue.Pair.pair;
/**
*
* This class manages the current authentication context assumed when accessing, managing
* and updating Structure entities such as {@link Structure} or {@link StructureView}.
*
* The context is local to the current thread.
*
* By default, the context equals to {@link JiraAuthenticationContext JIRA's authentication context}.
* It is possible to specify another user or disable security checks; see {@link #sudo(ApplicationUser, boolean, CallableE)})
* and {@link #sudo(CallableE)}.
* */
public class StructureAuth {
private static final Logger log = LoggerFactory.getLogger(StructureAuth.class);
/**
* Not using custom class to avoid leaking our classes through ThreadLocal.
* Second component is "override security"
* */
private static final ThreadLocal, Boolean>> sudoContext = new ThreadLocal<>();
private static final AtomicBoolean reportedNoAuthContext = new AtomicBoolean();
/**
* Returns the current user. If the user is not authenticated, i.e. the context is anonymous,
* returns {@code null}. "Override security" setting does not influence this method.
*
* @return the current user
* */
@Nullable
public static ApplicationUser getUser() {
Pair, Boolean> context = sudoContext.get();
return context == null ? getJiraUser() : context.left().getOrNull();
}
/**
* Returns {@link ApplicationUser#getKey() user key} of the current user. If the user is not authenticated, i.e.
* the context is anonymous, returns {@code null}. "Override security" setting does not influence this method.
*
* @return key of the current user
* */
@Nullable
public static String getUserKey() {
return JiraUsers.getKeyFor(getUser());
}
@Nullable
private static ApplicationUser getJiraUser() {
JiraAuthenticationContext jiraContext = JiraComponents.getJiraAuthenticationContext();
if (jiraContext == null) {
if (reportedNoAuthContext.compareAndSet(false, true)) {
log.error("JIRA's ComponentAccessor does not provide authentication context. " +
"Structure will behave as if everything is executed under anonymous user.");
}
return null;
}
return jiraContext.getLoggedInUser();
}
/**
* Returns {@code true} if permission checks shouldn't be carried out in this context.
* */
public static boolean isSecurityOverridden() {
Pair , Boolean> context = sudoContext.get();
return context != null && context.right();
}
/**
* Execute actions with Structure under a different authentication context - as another user,
* and/or with all security checks disabled.
* If you only need to override security while keeping user context intact, you can use
* {@link #sudo(CallableE) a shorthand override}
*
* @param user the user, can be {@code null} to represent anonymous
* @param overrideSecurity if true, user access level will not be checked for {@code f}
* @param f the code to execute, this method will return the computed value.
* Can throw {@link E}, it will be propagated.
* */
public static R sudo(@Nullable ApplicationUser user, boolean overrideSecurity,
CallableE f) throws E
{
JiraAuthenticationContext jiraContext = JiraComponents.getJiraAuthenticationContext();
ApplicationUser oldUser = jiraContext.getLoggedInUser();
boolean sameUser = Objects.equals(user, oldUser);
Pair, Boolean> oldContext = sudoContext.get();
Boolean securityOverridden = oldContext != null && oldContext.right();
if (sameUser && Objects.equals(overrideSecurity, securityOverridden)) {
// no need to change the context
return f.call();
}
sudoContext.set(pair(option(user), overrideSecurity));
jiraContext.setLoggedInUser(user);
String oldLoggingUsername = getOldLoggingUsername(oldUser);
Log4jKit.putUserToMDC(getNewLoggingUsername(user, oldLoggingUsername, sameUser));
try {
return f.call();
} finally {
if (oldContext != null) {
sudoContext.set(oldContext);
} else {
sudoContext.remove();
}
jiraContext.setLoggedInUser(oldUser);
Log4jKit.putUserToMDC(oldLoggingUsername);
}
}
private static String getNewLoggingUsername(@Nullable ApplicationUser user, @NotNull String oldLoggingUsername,
boolean sameUser)
{
return sameUser ? oldLoggingUsername : getDisplayedUserName(user) + "<-" + oldLoggingUsername;
}
@NotNull
private static String getOldLoggingUsername(ApplicationUser oldUser) {
Object obj = MDC.get(Log4jKit.MDC_JIRA_USERNAME);
if (obj != null) return obj.toString();
return getDisplayedUserName(oldUser);
}
private static String getDisplayedUserName(@Nullable ApplicationUser user) {
// start using ApplicationUser.getKey() when Atlassian starts using it
return user == null ? "anonymous" : user.getName();
}
/**
* Execute actions with Structure under the current JIRA user with all security checks disabled.
* @param f the code to execute, this method will return the computed value.
* Can throw {@link E}, it will be propagated.
* */
public static R sudo(CallableE f) throws E {
return sudo(getUser(), true, f);
}
public static AuthContext currentContext() {
Pair, Boolean> context = sudoContext.get();
return context == null
? new AuthContext.Custom(getJiraUser(), false)
: new AuthContext.Custom(context.left().getOrNull(), context.right());
}
}