
Unit Test Security Authorization
Add Spring Security unit tests for @PreAuthorize and expression-based authorization without missing owner-vs-admin edge cases.
Overview
Unit Test Security Authorization is an agent skill for the Ship phase that generates and explains Spring Security unit tests for expression-based @PreAuthorize authorization.
Install
npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill unit-test-security-authorizationWhat is this skill?
- JUnit patterns for @PreAuthorize expressions (admin, owner, hasPermission)
- @WithMockUser scenarios for allow vs AccessDeniedException deny paths
- Expression-based checks on principal username, user id, and custom permissions
- Copy-paste Java examples for DocumentService-style secured methods
- Focuses on method-level security behavior, not full integration OAuth flows
Adoption & trust: 1.2k installs on skills.sh; 271 GitHub stars; 3/3 security scanners passed (skills.sh audits).
What problem does it solve?
You added @PreAuthorize rules but have no systematic tests proving admins, owners, and strangers get the right access outcomes.
Who is it for?
Indie developers on Spring Boot services who want fast, deterministic authorization coverage before merging secured endpoints.
Skip if: Teams needing end-to-end OAuth login flows, policy-as-code platforms, or non-JVM stacks without Spring Security.
When should I use this skill?
Writing or reviewing Spring @PreAuthorize rules and need matching JUnit allow/deny coverage.
What do I get? / Deliverables
You get concrete JUnit cases that assert allowed calls succeed and forbidden calls throw AccessDeniedException for your expression rules.
- JUnit test class snippets
- Allow and deny assertion patterns per expression type
Recommended Skills
Journey fit
Authorization regressions surface when you ship or refactor secured APIs—unit tests belong on the Ship/testing shelf before release. Subphase testing is where JVM services prove access rules with @WithMockUser and AccessDeniedException assertions alongside other QA work.
How it compares
Use for focused authorization unit tests instead of relying only on manual API smoke checks in Postman.
Common Questions / FAQ
Who is unit-test-security-authorization for?
Solo and small-team builders shipping Java/Spring backends who use method-level security and want agent help writing correct allow/deny unit tests.
When should I use unit-test-security-authorization?
During Ship when you add or change @PreAuthorize expressions, before release reviews, or when refactoring DocumentService-style owner and role rules.
Is unit-test-security-authorization safe to install?
Review the Security Audits panel on this Prism page for the developer-kit package; the skill content is test templates only and does not run shell or network by itself.
SKILL.md
READMESKILL.md - Unit Test Security Authorization
# Advanced Authorization Testing ## Testing Expression-Based Authorization ### Complex Permission Expressions ```java @Service public class DocumentService { @PreAuthorize("hasRole('ADMIN') or authentication.principal.username == #owner") public Document getDocument(String owner, Long docId) { // get document } @PreAuthorize("hasPermission(#docId, 'Document', 'WRITE')") public void updateDocument(Long docId, String content) { // update logic } @PreAuthorize("#userId == authentication.principal.id") public UserProfile getUserProfile(Long userId) { // get profile } } ``` ### Tests ```java class ExpressionBasedSecurityTest { @Test @WithMockUser(username = "alice", roles = "ADMIN") void shouldAllowAdminToAccessAnyDocument() { DocumentService service = new DocumentService(); assertThatCode(() -> service.getDocument("bob", 1L)) .doesNotThrowAnyException(); } @Test @WithMockUser(username = "alice") void shouldAllowOwnerToAccessOwnDocument() { DocumentService service = new DocumentService(); assertThatCode(() -> service.getDocument("alice", 1L)) .doesNotThrowAnyException(); } @Test @WithMockUser(username = "alice") void shouldDenyUserAccessToOtherUserDocument() { DocumentService service = new DocumentService(); assertThatThrownBy(() -> service.getDocument("bob", 1L)) .isInstanceOf(AccessDeniedException.class); } @Test @WithMockUser(username = "alice", id = "1") void shouldAllowUserToAccessOwnProfile() { DocumentService service = new DocumentService(); assertThatCode(() -> service.getUserProfile(1L)) .doesNotThrowAnyException(); } @Test @WithMockUser(username = "alice", id = "1") void shouldDenyUserAccessToOtherProfile() { DocumentService service = new DocumentService(); assertThatThrownBy(() -> service.getUserProfile(999L)) .isInstanceOf(AccessDeniedException.class); } } ``` ## Testing Custom Permission Evaluator ### Custom Permission Evaluator Implementation ```java @Component public class DocumentPermissionEvaluator implements PermissionEvaluator { private final DocumentRepository documentRepository; public DocumentPermissionEvaluator(DocumentRepository documentRepository) { this.documentRepository = documentRepository; } @Override public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) { if (authentication == null) return false; Document document = (Document) targetDomainObject; String userUsername = authentication.getName(); return document.getOwner().getUsername().equals(userUsername) || userHasRole(authentication, "ADMIN"); } @Override public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) { if (authentication == null) return false; if (!"Document".equals(targetType)) return false; Document document = documentRepository.findById((Long) targetId).orElse(null); if (document == null) return false; return hasPermission(authentication, document, permission); } private boolean userHasRole(Authentication authentication, String role) { return authentication.getAuthorities().stream() .anyMatch(auth -> auth.getAuthority().equals("ROLE_" + role)); } } ``` ### Unit Tests for Custom Evaluator ```java class DocumentPermissionEvaluatorTest { private DocumentPermissionEvaluator evaluator; private DocumentRepository documentRepository; private Authentication adminAuth; private Authentication userAuth; private Document document; @BeforeEach void setUp() { documentRepository = mock(DocumentRepository.class); evaluator = new DocumentPermissionEvaluator(documentRepository); document = new Document(1L, "Te