@@ -5,11 +5,12 @@ import org.assertj.core.api.Assertions.assertThat
55import org.junit.jupiter.api.Test
66import org.migor.feedless.capability.SecurityContextCapabilityService
77import org.migor.feedless.session.LazyGrantedAuthority
8+ import org.mockito.Mockito
89import org.mockito.Mockito.mock
910import org.mockito.Mockito.`when`
1011import org.springframework.security.core.Authentication
11- import org.springframework.security.core.GrantedAuthority
12- import org.springframework.security.core.authority.SimpleGrantedAuthority
12+ import org.springframework.security.core.context.SecurityContext
13+ import org.springframework.security.core.context.SecurityContextHolder
1314
1415class CustomSecurityExpressionRootTest {
1516
@@ -23,11 +24,13 @@ class CustomSecurityExpressionRootTest {
2324 )
2425 `when `(authentication.authorities).thenReturn(authorities)
2526
26- val expressionRoot = SecurityContextCapabilityService (authentication)
27+ mockAuthentication(authentication) {
28+ val expressionRoot = SecurityContextCapabilityService ()
2729
28- // when & then
29- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
30- assertThat(expressionRoot.hasCapability(" groups" )).isTrue()
30+ // when & then
31+ assertThat(expressionRoot.hasCapability(" user" )).isTrue()
32+ assertThat(expressionRoot.hasCapability(" groups" )).isTrue()
33+ }
3134 }
3235
3336 @Test
@@ -39,12 +42,14 @@ class CustomSecurityExpressionRootTest {
3942 )
4043 `when `(authentication.authorities).thenReturn(authorities)
4144
42- val expressionRoot = SecurityContextCapabilityService (authentication)
45+ mockAuthentication(authentication) {
46+ val expressionRoot = SecurityContextCapabilityService ()
4347
44- // when & then
45- assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
46- assertThat(expressionRoot.hasCapability(" admin" )).isFalse()
47- assertThat(expressionRoot.hasCapability(" nonexistent" )).isFalse()
48+ // when & then
49+ assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
50+ assertThat(expressionRoot.hasCapability(" admin" )).isFalse()
51+ assertThat(expressionRoot.hasCapability(" nonexistent" )).isFalse()
52+ }
4853 }
4954
5055 @Test
@@ -53,47 +58,13 @@ class CustomSecurityExpressionRootTest {
5358 val authentication = mock(Authentication ::class .java)
5459 `when `(authentication.authorities).thenReturn(emptyList())
5560
56- val expressionRoot = SecurityContextCapabilityService (authentication)
61+ mockAuthentication(authentication) {
62+ val expressionRoot = SecurityContextCapabilityService ()
5763
58- // when & then
59- assertThat(expressionRoot.hasCapability(" user" )).isFalse()
60- assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
61- }
62-
63- @Test
64- fun `hasCapability is case sensitive` () = runTest {
65- // given
66- val authentication = mock(Authentication ::class .java)
67- val authorities = listOf (
68- LazyGrantedAuthority (" user" , """ {"id":"test-user-id"}""" )
69- )
70- `when `(authentication.authorities).thenReturn(authorities)
71-
72- val expressionRoot = SecurityContextCapabilityService (authentication)
73-
74- // when & then
75- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
76- assertThat(expressionRoot.hasCapability(" USER" )).isFalse()
77- assertThat(expressionRoot.hasCapability(" User" )).isFalse()
78- }
79-
80- @Test
81- fun `hasCapability ignores non-LazyGrantedAuthority authorities` () = runTest {
82- // given
83- val authentication = mock(Authentication ::class .java)
84- val authorities: List <GrantedAuthority > = listOf (
85- SimpleGrantedAuthority (" ROLE_USER" ),
86- LazyGrantedAuthority (" user" , """ {"id":"test-user-id"}""" ),
87- SimpleGrantedAuthority (" ROLE_ADMIN" )
88- )
89- `when `(authentication.authorities).thenReturn(authorities)
90-
91- val expressionRoot = SecurityContextCapabilityService (authentication)
92-
93- // when & then
94- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
95- assertThat(expressionRoot.hasCapability(" ROLE_USER" )).isFalse()
96- assertThat(expressionRoot.hasCapability(" ROLE_ADMIN" )).isFalse()
64+ // when & then
65+ assertThat(expressionRoot.hasCapability(" user" )).isFalse()
66+ assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
67+ }
9768 }
9869
9970 @Test
@@ -106,14 +77,15 @@ class CustomSecurityExpressionRootTest {
10677 LazyGrantedAuthority (" groups" , """ []""" )
10778 )
10879 `when `(authentication.authorities).thenReturn(authorities)
80+ mockAuthentication(authentication) {
81+ val expressionRoot = SecurityContextCapabilityService ()
10982
110- val expressionRoot = SecurityContextCapabilityService (authentication)
111-
112- // when & then
113- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
114- assertThat(expressionRoot.hasCapability(" agent" )).isTrue()
115- assertThat(expressionRoot.hasCapability(" groups" )).isTrue()
116- assertThat(expressionRoot.hasCapability(" admin" )).isFalse()
83+ // when & then
84+ assertThat(expressionRoot.hasCapability(" user" )).isTrue()
85+ assertThat(expressionRoot.hasCapability(" agent" )).isTrue()
86+ assertThat(expressionRoot.hasCapability(" groups" )).isTrue()
87+ assertThat(expressionRoot.hasCapability(" admin" )).isFalse()
88+ }
11789 }
11890
11991 @Test
@@ -122,154 +94,37 @@ class CustomSecurityExpressionRootTest {
12294 val authentication = mock(Authentication ::class .java)
12395 `when `(authentication.isAuthenticated).thenReturn(true )
12496
125- val expressionRoot = SecurityContextCapabilityService (authentication)
97+ mockAuthentication(authentication) {
98+ val expressionRoot = SecurityContextCapabilityService ()
12699
127- // when & then
128- assertThat(expressionRoot.hasToken()).isTrue()
100+ // when & then
101+ assertThat(expressionRoot.hasToken()).isTrue()
102+ }
129103 }
130104
131105 @Test
132106 fun `hasToken returns false when authentication is not authenticated` () = runTest {
133107 // given
134108 val authentication = mock(Authentication ::class .java)
135109 `when `(authentication.isAuthenticated).thenReturn(false )
110+ mockAuthentication(authentication) {
111+ val expressionRoot = SecurityContextCapabilityService ()
136112
137- val expressionRoot = SecurityContextCapabilityService (authentication)
138-
139- // when & then
140- assertThat(expressionRoot.hasToken()).isFalse()
141- }
142-
143- @Test
144- fun `hasToken returns true for authenticated user with capabilities` () = runTest {
145- // given
146- val authentication = mock(Authentication ::class .java)
147- `when `(authentication.isAuthenticated).thenReturn(true )
148- val authorities = listOf (
149- LazyGrantedAuthority (" user" , """ {"id":"test-user-id"}""" )
150- )
151- `when `(authentication.authorities).thenReturn(authorities)
152-
153- val expressionRoot = SecurityContextCapabilityService (authentication)
154-
155- // when & then
156- assertThat(expressionRoot.hasToken()).isTrue()
157- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
158- }
159-
160- @Test
161- fun `hasToken returns true even without capabilities` () = runTest {
162- // given
163- val authentication = mock(Authentication ::class .java)
164- `when `(authentication.isAuthenticated).thenReturn(true )
165- `when `(authentication.authorities).thenReturn(emptyList())
166-
167- val expressionRoot = SecurityContextCapabilityService (authentication)
168-
169- // when & then
170- assertThat(expressionRoot.hasToken()).isTrue()
171- assertThat(expressionRoot.hasCapability(" user" )).isFalse()
113+ // when & then
114+ assertThat(expressionRoot.hasToken()).isFalse()
115+ }
172116 }
173117
174- @Test
175- fun `combined test - hasToken true but specific capability false` () = runTest {
176- // given
177- val authentication = mock(Authentication ::class .java)
178- `when `(authentication.isAuthenticated).thenReturn(true )
179- val authorities = listOf (
180- LazyGrantedAuthority (" user" , """ {"id":"test-user-id"}""" )
181- )
182- `when `(authentication.authorities).thenReturn(authorities)
183-
184- val expressionRoot = SecurityContextCapabilityService (authentication)
185-
186- // when & then
187- assertThat(expressionRoot.hasToken()).isTrue()
188- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
189- assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
190- }
191-
192- @Test
193- fun `hasCapability with empty string returns false` () = runTest {
194- // given
195- val authentication = mock(Authentication ::class .java)
196- val authorities = listOf (
197- LazyGrantedAuthority (" user" , """ {"id":"test-user-id"}""" )
198- )
199- `when `(authentication.authorities).thenReturn(authorities)
200-
201- val expressionRoot = SecurityContextCapabilityService (authentication)
202-
203- // when & then
204- assertThat(expressionRoot.hasCapability(" " )).isFalse()
205- }
206-
207- @Test
208- fun `hasCapability with special characters in capability ID` () = runTest {
209- // given
210- val authentication = mock(Authentication ::class .java)
211- val authorities = listOf (
212- LazyGrantedAuthority (" special-capability-123" , """ {"id":"test"}""" )
213- )
214- `when `(authentication.authorities).thenReturn(authorities)
215-
216- val expressionRoot = SecurityContextCapabilityService (authentication)
217-
218- // when & then
219- assertThat(expressionRoot.hasCapability(" special-capability-123" )).isTrue()
220- assertThat(expressionRoot.hasCapability(" special-capability-456" )).isFalse()
221- }
222-
223- @Test
224- fun `real world scenario - user capability check` () = runTest {
225- // given - simulating a JWT token with user capability
226- val authentication = mock(Authentication ::class .java)
227- `when `(authentication.isAuthenticated).thenReturn(true )
228- val authorities = listOf (
229- LazyGrantedAuthority (" user" , """ {"id":"550e8400-e29b-41d4-a716-446655440000"}""" ),
230- LazyGrantedAuthority (" groups" , """ []""" )
231- )
232- `when `(authentication.authorities).thenReturn(authorities)
233-
234- val expressionRoot = SecurityContextCapabilityService (authentication)
235-
236- // when & then - user can access user-protected endpoints
237- assertThat(expressionRoot.hasToken()).isTrue()
238- assertThat(expressionRoot.hasCapability(" user" )).isTrue()
239- assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
240- }
241-
242- @Test
243- fun `real world scenario - agent capability check` () = runTest {
244- // given - simulating an agent/service token
245- val authentication = mock(Authentication ::class .java)
246- `when `(authentication.isAuthenticated).thenReturn(true )
247- val authorities = listOf (
248- LazyGrantedAuthority (" agent" , """ {"dummy":"service-123"}""" )
249- )
250- `when `(authentication.authorities).thenReturn(authorities)
251-
252- val expressionRoot = SecurityContextCapabilityService (authentication)
253-
254- // when & then - agent can only access agent-protected endpoints
255- assertThat(expressionRoot.hasToken()).isTrue()
256- assertThat(expressionRoot.hasCapability(" agent" )).isTrue()
257- assertThat(expressionRoot.hasCapability(" user" )).isFalse()
258- }
259-
260- @Test
261- fun `real world scenario - anonymous token-only access` () = runTest {
262- // given - simulating a token without specific capabilities (anonymous)
263- val authentication = mock(Authentication ::class .java)
264- `when `(authentication.isAuthenticated).thenReturn(true )
265- `when `(authentication.authorities).thenReturn(emptyList())
266-
267- val expressionRoot = SecurityContextCapabilityService (authentication)
118+ private fun mockAuthentication (authentication : Authentication , block : () -> Any ) {
119+ Mockito .mockStatic(SecurityContextHolder ::class .java).use { staticMock ->
120+ {
121+ val securityContext = mock(SecurityContext ::class .java)
122+ `when `(securityContext.authentication).thenReturn(authentication)
123+ `when `(SecurityContextHolder .getContext()).thenReturn(securityContext)
268124
269- // when & then - has token but no specific capabilities
270- assertThat(expressionRoot.hasToken()).isTrue()
271- assertThat(expressionRoot.hasCapability(" user" )).isFalse()
272- assertThat(expressionRoot.hasCapability(" agent" )).isFalse()
125+ block.invoke()
126+ }
127+ }
273128 }
274129}
275130
0 commit comments