PluginBench
Skill
Review
Audit score 70

unit-test-security-authorization

giuseppe-trisciuoglio/developer-kit

Unit test Spring Security authorization with @PreAuthorize, @Secured, and role-based access control patterns.

What is unit-test-security-authorization?

Provides patterns for testing Spring Security method-level authorization annotations and role-based access control (RBAC). Use this skill when validating @PreAuthorize, @Secured, @RolesAllowed decorators and custom permission evaluators in unit tests.

  • Test @PreAuthorize and @Secured method-level security annotations
  • Validate role-based access control (RBAC) with @WithMockUser
  • Test custom permission evaluators and expression-based authorization
  • Verify access denied scenarios for unauthorized principals
  • Test @PostAuthorize and @RolesAllowed (JSR-250) annotations
  • Confirm security context is active and authorization is enforced

How to install unit-test-security-authorization

npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-security-authorization
Prerequisites
  • spring-security-test dependency in test scope
  • @EnableMethodSecurity or @EnableGlobalMethodSecurity in test configuration
  • JUnit 5 or compatible test framework
  • Basic understanding of Spring Security annotations
Claude Code
Cursor
Windsurf
Cline

How to use unit-test-security-authorization

  1. 1.Add spring-security-test to test dependencies
  2. 2.Enable method security in test configuration with @EnableMethodSecurity
  3. 3.Use @WithMockUser(roles="ROLE_NAME") to set authenticated user context
  4. 4.Write test cases for both allow and deny scenarios
  5. 5.Use @WithUserDetails for expression-based security with custom principals
  6. 6.Assert AccessDeniedException is thrown for unauthorized access
  7. 7.Verify security is active by testing unauthenticated access

Use cases

Good for
  • Testing admin-only operations like deleteUser() with different roles
  • Validating expression-based security rules that check user ownership
  • Testing custom permission evaluators for document or resource access
  • Verifying that unauthorized users receive AccessDeniedException
  • Testing role-based workflows with multiple role combinations
Who it's for
  • Spring developers writing unit tests for secured services
  • QA engineers validating authorization logic
  • Security-focused teams implementing role-based access control
  • Developers migrating from manual to declarative security

unit-test-security-authorization FAQ

How do I test @PreAuthorize with different roles?

Use @WithMockUser(roles="ADMIN") on test methods to set the authenticated user's roles. Write separate test methods for each role to verify both allow and deny cases.

Why does my @PreAuthorize test pass when it should fail?

Verify that @EnableMethodSecurity is active on your test configuration. Without it, @PreAuthorize checks are silently bypassed. Also ensure you're calling the method through a proxy, not directly.

How do I test custom permission evaluators?

Create an Authentication object with UsernamePasswordAuthenticationToken, instantiate your permission evaluator, and call hasPermission(auth, resource, permission) directly in the test.

Can I test expression-based security like #userId == authentication.principal.id?

Yes, use @WithUserDetails with a custom UserDetailsService to provide a principal with the properties your SpEL expression needs.

What's the difference between @Secured and @PreAuthorize?

@Secured uses simple role names (ROLE_ADMIN), while @PreAuthorize supports complex SpEL expressions. @PreAuthorize is more flexible and recommended for new code.

Full instructions (SKILL.md)

Source of truth, from giuseppe-trisciuoglio/developer-kit.


name: unit-test-security-authorization description: Provides patterns for unit testing Spring Security with @PreAuthorize, @Secured, @RolesAllowed. Validates role-based access control and authorization policies. Use when testing security configurations and access control logic. allowed-tools: Read, Write, Bash, Glob, Grep

Unit Testing Security and Authorization

Overview

This skill provides patterns for unit testing Spring Security authorization logic using @PreAuthorize, @Secured, @RolesAllowed, and custom permission evaluators. It covers testing role-based access control (RBAC), expression-based authorization, custom permission evaluators, and verifying access denied scenarios without full Spring Security context.

When to Use

Use this skill when:

  • Testing @PreAuthorize and @Secured method-level security
  • Testing role-based access control (RBAC)
  • Testing custom permission evaluators
  • Verifying access denied scenarios
  • Testing authorization with authenticated principals
  • Want fast authorization tests without full Spring Security context

Instructions

Follow these steps to test Spring Security authorization:

1. Set Up Security Testing Dependencies

Add spring-security-test to your test dependencies:

<dependency>
  <groupId>org.springframework.security</groupId>
  <artifactId>spring-security-test</artifactId>
  <scope>test</scope>
</dependency>

2. Enable Method Security in Test Configuration

@Configuration
@EnableMethodSecurity
class TestSecurityConfig { }

3. Test with @WithMockUser

@Test
@WithMockUser(roles = "ADMIN")
void shouldAllowAdminAccess() {
  assertThatCode(() -> service.deleteUser(1L))
    .doesNotThrowAnyException();
}

@Test
@WithMockUser(roles = "USER")
void shouldDenyUserAccess() {
  assertThatThrownBy(() -> service.deleteUser(1L))
    .isInstanceOf(AccessDeniedException.class);
}

4. Test Custom Permission Evaluators

@Test
void shouldGrantPermissionToOwner() {
  Authentication auth = new UsernamePasswordAuthenticationToken(
    "alice", null, List.of(new SimpleGrantedAuthority("ROLE_USER"))
  );
  Document doc = new Document(1L, "Test", new User("alice"));

  boolean result = evaluator.hasPermission(auth, doc, "WRITE");
  assertThat(result).isTrue();
}

5. Validate Security is Active

If tests pass unexpectedly, add this assertion to verify security is enforced:

@Test
void shouldRejectUnauthorizedWhenSecurityEnabled() {
  assertThatThrownBy(() -> service.deleteUser(1L))
    .isInstanceOf(AccessDeniedException.class);
}

Quick Reference

AnnotationDescriptionExample
@PreAuthorizePre-invocation authorization@PreAuthorize("hasRole('ADMIN')")
@PostAuthorizePost-invocation authorization@PostAuthorize("returnObject.owner == authentication.name")
@SecuredSimple role-based security@Secured("ROLE_ADMIN")
@RolesAllowedJSR-250 standard@RolesAllowed({"ADMIN", "MANAGER"})
@WithMockUserTest annotation@WithMockUser(roles = "ADMIN")

Examples

Basic @PreAuthorize Test

@Service
public class UserService {
  @PreAuthorize("hasRole('ADMIN')")
  public void deleteUser(Long userId) {
    // delete logic
  }
}

// Test
@Test
@WithMockUser(roles = "ADMIN")
void shouldAllowAdminToDeleteUser() {
  assertThatCode(() -> service.deleteUser(1L))
    .doesNotThrowAnyException();
}

@Test
@WithMockUser(roles = "USER")
void shouldDenyUserFromDeletingUser() {
  assertThatThrownBy(() -> service.deleteUser(1L))
    .isInstanceOf(AccessDeniedException.class);
}

Expression-Based Security Test

@PreAuthorize("#userId == authentication.principal.id")
public UserProfile getUserProfile(Long userId) {
  // get profile
}

// For custom principal properties, use @WithUserDetails with a custom UserDetailsService
@Test
@WithUserDetails("alice")
void shouldAllowUserToAccessOwnProfile() {
  assertThatCode(() -> service.getUserProfile(1L))
    .doesNotThrowAnyException();
}

Validation tip: If a security test passes unexpectedly, verify that @EnableMethodSecurity is active on the test configuration — a missing annotation causes all @PreAuthorize checks to be bypassed silently.

See references/basic-testing.md for more basic patterns and references/advanced-authorization.md for complex expressions and custom evaluators.

Best Practices

  1. Use @WithMockUser for setting authenticated user context
  2. Test both allow and deny cases for each security rule
  3. Test with different roles to verify role-based decisions
  4. Test expression-based security comprehensively
  5. Mock external dependencies (permission evaluators, etc.)
  6. Test anonymous access separately from authenticated access
  7. Use @EnableGlobalMethodSecurity in configuration for method-level security

Common Pitfalls

  • Forgetting to enable method security in test configuration
  • Not testing both allow and deny scenarios
  • Testing framework code instead of authorization logic
  • Not handling null authentication in tests
  • Mixing authentication and authorization tests unnecessarily

Constraints and Warnings

  • Method security requires proxy: @PreAuthorize works via proxies; direct method calls bypass security
  • @EnableGlobalMethodSecurity: Must be enabled for @PreAuthorize, @Secured to work
  • Role prefix: Spring adds "ROLE_" prefix automatically; use hasRole('ADMIN') not hasRole('ROLE_ADMIN')
  • Authentication context: Security context is thread-local; be careful with async tests
  • @WithMockUser limitations: Creates a simple Authentication; complex auth scenarios need custom setup
  • SpEL expressions: Complex SpEL in @PreAuthorize can be difficult to debug; test thoroughly
  • Performance impact: Method security adds overhead; consider security at layer boundaries

References

Setup and Configuration

Testing Patterns

Advanced Topics

Complete Examples