1010import org .cloudfoundry .identity .uaa .constants .OriginKeys ;
1111import org .cloudfoundry .identity .uaa .extensions .PollutionPreventionExtension ;
1212import org .cloudfoundry .identity .uaa .home .BuildInfo ;
13+ import org .cloudfoundry .identity .uaa .util .ZoneResolutionMode ;
1314import org .cloudfoundry .identity .uaa .oauth .client .ClientConstants ;
1415import org .cloudfoundry .identity .uaa .security .beans .SecurityContextAccessor ;
1516import org .cloudfoundry .identity .uaa .util .beans .TestBuildInfo ;
1617import org .cloudfoundry .identity .uaa .zone .MultitenantClientServices ;
1718import org .cloudfoundry .identity .uaa .zone .beans .IdentityZoneManager ;
1819import org .junit .jupiter .api .AfterEach ;
1920import org .junit .jupiter .api .BeforeEach ;
20- import org .junit .jupiter .api .Test ;
2121import org .junit .jupiter .api .extension .ExtendWith ;
2222import org .junit .jupiter .params .ParameterizedTest ;
23+ import org .junit .jupiter .params .provider .Arguments ;
24+ import org .junit .jupiter .params .provider .EnumSource ;
25+ import org .junit .jupiter .params .provider .MethodSource ;
2326import org .junit .jupiter .params .provider .ValueSource ;
27+ import org .springframework .http .HttpMethod ;
28+
29+ import java .util .stream .Stream ;
2430import org .mockito .ArgumentCaptor ;
2531import org .mockito .Mockito ;
2632import org .springframework .beans .factory .annotation .Autowired ;
6773import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .redirectedUrl ;
6874import static org .springframework .test .web .servlet .result .MockMvcResultMatchers .status ;
6975
76+ /**
77+ * MockMvc tests for ProfileController. Parameterized by {@link ZoneResolutionMode} (SUBDOMAIN and ZONE_PATH).
78+ * ZONE_PATH tests are expected to fail until ProfileController adds mappings for {@code /z/{subdomain}/profile}.
79+ */
7080@ ExtendWith (PollutionPreventionExtension .class )
7181@ WebAppConfiguration
7282@ SpringJUnitConfig (classes = ProfileControllerMockMvcTests .ContextConfiguration .class )
@@ -128,6 +138,8 @@ ProfileController profileController(ApprovalStore approvalsService,
128138
129139 private static final String THE_ULTIMATE_APP = "The Ultimate App" ;
130140 private static final String USER_ID = "userId" ;
141+ /** Non-empty subdomain for ZONE_PATH so request goes to /z/{subdomain}/profile (expected 404 until controller has zone path). */
142+ private static final String ZONE_PATH_SUBDOMAIN = "test-zone" ;
131143
132144 @ Autowired
133145 private WebApplicationContext webApplicationContext ;
@@ -195,53 +207,75 @@ void tearDown() {
195207 SecurityContextHolder .clearContext ();
196208 }
197209
198- @ Test
199- void getProfile () throws Exception {
200- getProfile (mockMvc , THE_ULTIMATE_APP , currentIdentityZoneId );
210+ private String subdomainFor (ZoneResolutionMode mode ) {
211+ return mode == ZoneResolutionMode .ZONE_PATH ? ZONE_PATH_SUBDOMAIN : "" ;
201212 }
202213
203- @ Test
204- void getProfileNoAppName () throws Exception {
214+ @ ParameterizedTest
215+ @ EnumSource (ZoneResolutionMode .class )
216+ void getProfile (ZoneResolutionMode mode ) throws Exception {
217+ String subdomain = subdomainFor (mode );
218+ getProfile (mockMvc , mode , subdomain , THE_ULTIMATE_APP , currentIdentityZoneId );
219+ }
220+
221+ @ ParameterizedTest
222+ @ EnumSource (ZoneResolutionMode .class )
223+ void getProfileNoAppName (ZoneResolutionMode mode ) throws Exception {
205224 UaaClientDetails appClient = new UaaClientDetails ("app" , "thing" , "thing.read,thing.write" , GRANT_TYPE_AUTHORIZATION_CODE , "" );
206225 when (clientDetailsService .loadClientByClientId ("app" , currentIdentityZoneId )).thenReturn (appClient );
207- getProfile (mockMvc , "app" , currentIdentityZoneId );
226+ String subdomain = subdomainFor (mode );
227+ getProfile (mockMvc , mode , subdomain , "app" , currentIdentityZoneId );
208228 }
209229
210- @ Test
211- void specialMessageWhenNoAppsAreAuthorized () throws Exception {
230+ @ ParameterizedTest
231+ @ EnumSource (ZoneResolutionMode .class )
232+ void specialMessageWhenNoAppsAreAuthorized (ZoneResolutionMode mode ) throws Exception {
212233 when (approvalStore .getApprovalsForUser (anyString (), eq (currentIdentityZoneId ))).thenReturn (Collections .emptyList ());
213234
214235 UaaPrincipal uaaPrincipal = new UaaPrincipal ("fake-user-id" , "username" , "email@example.com" , OriginKeys .UAA , null , currentIdentityZoneId );
215236 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken (uaaPrincipal , null );
237+ String subdomain = subdomainFor (mode );
216238
217- mockMvc .perform (get ( "/profile" ).principal (authentication ))
239+ mockMvc .perform (mode . createRequestBuilder ( subdomain , HttpMethod . GET , "/profile" ).principal (authentication ))
218240 .andExpect (status ().isOk ())
219241 .andExpect (model ().attributeExists ("approvals" ))
220242 .andExpect (content ().contentTypeCompatibleWith (TEXT_HTML ))
221243 .andExpect (content ().string (containsString ("You have not yet authorized any third party applications." )));
222244 }
223245
224- @ Test
225- void passwordLinkHiddenWhenUsersOriginIsNotUaa () throws Exception {
246+ @ ParameterizedTest
247+ @ EnumSource (ZoneResolutionMode .class )
248+ void passwordLinkHiddenWhenUsersOriginIsNotUaa (ZoneResolutionMode mode ) throws Exception {
226249 UaaPrincipal uaaPrincipal = new UaaPrincipal ("fake-user-id" , "username" , "email@example.com" , OriginKeys .LDAP , "dnEntryForLdapUser" , currentIdentityZoneId );
227250 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken (uaaPrincipal , null );
251+ String subdomain = subdomainFor (mode );
228252
229- mockMvc .perform (get ( "/profile" ).principal (authentication ))
253+ mockMvc .perform (mode . createRequestBuilder ( subdomain , HttpMethod . GET , "/profile" ).principal (authentication ))
230254 .andExpect (status ().isOk ())
231255 .andExpect (model ().attribute ("isUaaManagedUser" , false ))
232256 .andExpect (model ().attributeDoesNotExist ("email" ))
233257 .andExpect (content ().string (not (containsString ("Change Password" ))));
234258 }
235259
260+ static Stream <Arguments > updateProfilePaths () {
261+ return Stream .of (
262+ Arguments .of (ZoneResolutionMode .SUBDOMAIN , "/profile" ),
263+ Arguments .of (ZoneResolutionMode .SUBDOMAIN , "/profile/" ),
264+ Arguments .of (ZoneResolutionMode .ZONE_PATH , "/profile" ),
265+ Arguments .of (ZoneResolutionMode .ZONE_PATH , "/profile/" )
266+ );
267+ }
268+
236269 @ ParameterizedTest
237- @ ValueSource (strings = {"/profile" , "/profile/" })
238- void updateProfile (String url ) throws Exception {
239- MockHttpServletRequestBuilder post = post (url )
270+ @ MethodSource ("updateProfilePaths" )
271+ void updateProfile (ZoneResolutionMode mode , String url ) throws Exception {
272+ String subdomain = subdomainFor (mode );
273+ MockHttpServletRequestBuilder postReq = mode .createRequestBuilder (subdomain , HttpMethod .POST , url )
240274 .param ("checkedScopes" , "app-thing.read" )
241275 .param ("update" , "" )
242276 .param ("clientId" , "app" );
243277
244- mockMvc .perform (post )
278+ mockMvc .perform (postReq )
245279 .andExpect (status ().isFound ())
246280 .andExpect (redirectedUrl ("profile" ));
247281
@@ -267,25 +301,27 @@ void updateProfile(String url) throws Exception {
267301 assertThat (writeApproval .getStatus ()).isEqualTo (DENIED );
268302 }
269303
270- @ Test
271- void revokeApp () throws Exception {
272- MockHttpServletRequestBuilder post = post ("/profile" )
304+ @ ParameterizedTest
305+ @ EnumSource (ZoneResolutionMode .class )
306+ void revokeApp (ZoneResolutionMode mode ) throws Exception {
307+ String subdomain = subdomainFor (mode );
308+ MockHttpServletRequestBuilder postReq = mode .createRequestBuilder (subdomain , HttpMethod .POST , "/profile" )
273309 .param ("checkedScopes" , "app-resource.read" )
274310 .param ("delete" , "" )
275311 .param ("clientId" , "app" );
276312
277- mockMvc .perform (post )
313+ mockMvc .perform (postReq )
278314 .andExpect (status ().isFound ())
279315 .andExpect (redirectedUrl ("profile" ));
280316
281317 Mockito .verify (approvalStore , Mockito .times (1 )).revokeApprovalsForClientAndUser ("app" , USER_ID , currentIdentityZoneId );
282318 }
283319
284- private static void getProfile (final MockMvc mockMvc , final String name , final String currentIdentityZoneId ) throws Exception {
320+ private static void getProfile (final MockMvc mockMvc , final ZoneResolutionMode mode , final String subdomain , final String name , final String currentIdentityZoneId ) throws Exception {
285321 UaaPrincipal uaaPrincipal = new UaaPrincipal ("fake-user-id" , "username" , "email@example.com" , OriginKeys .UAA , null , currentIdentityZoneId );
286322 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken (uaaPrincipal , null );
287323
288- mockMvc .perform (get ( "/profile" ).principal (authentication ))
324+ mockMvc .perform (mode . createRequestBuilder ( subdomain , HttpMethod . GET , "/profile" ).principal (authentication ))
289325 .andExpect (status ().isOk ())
290326 .andExpect (model ().attributeExists ("clientnames" ))
291327 .andExpect (model ().attribute ("clientnames" , hasKey ("app" )))
0 commit comments