package com.atlassian.stash.internal.user;

import com.atlassian.bitbucket.event.request.RequestEndedEvent;
import com.atlassian.bitbucket.permission.Permission;
import com.atlassian.bitbucket.request.RequestManager;
import com.atlassian.bitbucket.user.ApplicationUser;
import com.atlassian.bitbucket.user.EscalatedSecurityContext;
import com.atlassian.bitbucket.user.SecurityService;
import com.atlassian.event.api.EventListener;
import com.atlassian.plugin.spring.AvailableToPlugins;
import com.atlassian.stash.internal.annotation.NotProfiled;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

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

/**
 * IMPORTANT:
 * The service methods here don't have permission restrictions because its entire purpose is to elevate permissions.
 * It needs to be used with care, such that we don't elevate to SYS_ADMIN by accident
 */
@AvailableToPlugins(SecurityService.class)
@NotProfiled
@Service("securityService")
public class DefaultSecurityService implements SecurityService {

    private final RequestManager requestManager;

    @Autowired
    public DefaultSecurityService(RequestManager requestManager) {
        this.requestManager = requestManager;
    }

    @Nonnull
    @Override
    public EscalatedSecurityContext anonymously(@Nonnull String reason) {
        return contextBuilder(reason).anonymously().build();
    }

    @Nonnull
    @Override
    public EscalatedSecurityContext escalate(@Nonnull String reason) {
        return contextBuilder(reason).build();
    }

    @Nonnull
    @Override
    public EscalatedSecurityContext impersonating(@Nonnull ApplicationUser user, @Nonnull String reason) {
        return contextBuilder(reason).impersonating(user).build();
    }

    /**
     * Ensure the {@code SecurityContextHolder} is cleared when the request ends.
     *
     * @param event ignored
     */
    @EventListener
    public void onRequestEnded(RequestEndedEvent event) {
        SecurityContextHolder.clearContext();
    }

    @Nonnull
    @Override
    public EscalatedSecurityContext withPermission(@Nonnull Permission permission, @Nonnull String reason) {
        return contextBuilder(reason).withPermission(permission).build();
    }

    @Nonnull
    @Override
    public EscalatedSecurityContext withPermission(@Nonnull Permission permission, @Nonnull Object resource, @Nonnull String reason) {
        return contextBuilder(reason).withPermission(resource, permission).build();
    }

    @Nonnull
    @Override
    public EscalatedSecurityContext withPermissions(@Nonnull Set<Permission> permissions, @Nonnull String reason) {
        return contextBuilder(reason).withPermissions(permissions).build();
    }

    private DefaultEscalatedSecurityContext.Builder contextBuilder(String reason) {
        return new DefaultEscalatedSecurityContext.Builder(reason, requestManager);
    }
}