Skip to content

/unit-test-security-authorization

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.

shell
$ npx -y skills add giuseppe-trisciuoglio/developer-kit --skill unit-test-security-authorization --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.
  • You can call itInvoke it directly when you want it.
  • Slash command/unit-test-security-authorization
How auto-invocation works

Context preview

The summary Claude sees to decide when to auto-load this skill.

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.

SKILL.md

unit-test-security-authorization.SKILL.md
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

| Annotation | Description | Example | |------------|-------------|---------| | `@PreAuthorize` | Pre-invocation authorization | `@PreAuthorize("hasRole('ADMIN')")` | | `@PostAuthorize` | Post-invocation authorization | `@PostAuthorize("returnObject.owner == authentication.name")` | | `@Secured` | Simple role-based security | `@Secured("ROLE_ADMIN")` | | `@RolesAllowed` | JSR-250 standard | `@RolesAllowed({"ADMIN", "MANAGER"})` | | `@WithMockUser` | Test 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](references/basic-testing.md) for more basic patterns and [references/advanced-authorization.md](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;
Read more
Read it on GitHub ↗

Showing the first part of this file.

Ships withdeveloper-kit

Modular plugin marketplace for Claude Code and agentic CLIs, with validated, spec-driven skills, agents, commands, and workflows for Java, TypeScript, Python, PHP, AWS, and AI.

Get the whole plugin, auto-invoked
Stats
315
Stars
0
Views
37
Forks
Maintained
Maintenance
Python
Language
MIT
License
1mo ago
Last commit
9mo ago
Created

Repo: giuseppe-trisciuoglio/developer-kit

Other skills on developer-kit.