package com.atlassian.bitbucket.user;

import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.request.RequestContext;
import com.atlassian.bitbucket.util.Operation;

import javax.annotation.Nonnull;
import java.util.Set;

/**
 * Custom security context that specifies as what user and with which additional permissions an {@link Operation} should be called.
 */
public interface EscalatedSecurityContext {

    /**
     * Executes an Operation as the specified user and added permissions. The current security context is restored after
     * the operation completes.
     * <p>
     * Note: Nesting invocations merges all elevated permissions rather than replacing the previous values.
     *
     * @param operation the operation to be executed
     * @return the return value of the provided operation
     * @throws E when the provided operation throws an exception
     */
    <T, E extends Throwable> T call(@Nonnull Operation<T, E> operation) throws E;

    /**
     * Escalates the security context as the specified user and added permissions for the duration of the current request.
     * If there is no request in scope an exception is thrown.
     *
     * @throws java.lang.IllegalStateException if there is {@link RequestContext#isActive() no request}
     * in scope for the current thread
     */
    void applyToRequest();

    /**
     * Adds permissions to the set of elevated permissions. If a resource permission (e.g. REPO_READ) is provided, the
     * permission is granted to all resources.
     *
     * @param permission the permission to be temporarily granted
     * @return the escalated security context
     */
    @Nonnull
    EscalatedSecurityContext withPermission(@Nonnull Permission permission);

    /**
     * Adds permissions on a particular resource to the set of elevated permissions.
     *
     * @param resource the resource
     * @param permission the permission to be temporarily granted on the provided resource
     * @return the escalated security context
     */
    @Nonnull
    EscalatedSecurityContext withPermission(@Nonnull Object resource, @Nonnull Permission permission);

    /**
     * Adds permissions to the set of elevated permissions. If a resource permission (e.g. REPO_READ) is provided, the
     * permission is granted to all resources.
     *
     * @param permissions the permissions to be temporarily granted
     * @return the escalated security context
     */
    @Nonnull
    EscalatedSecurityContext withPermissions(@Nonnull Set<Permission> permissions);
}
