Skip to content

Commit 3fd7dd9

Browse files
authored
add Integration test for conditional access (Azure#19642)
1 parent 430d027 commit 3fd7dd9

25 files changed

+356
-9
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
package com.azure.test.aad.webapi.conditional.access.webapi.a;
2+
3+
import com.azure.spring.test.AppRunner;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.beans.factory.annotation.Autowired;
7+
import org.springframework.boot.autoconfigure.SpringBootApplication;
8+
import org.springframework.context.annotation.Bean;
9+
import org.springframework.security.access.prepost.PreAuthorize;
10+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
11+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
12+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
13+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
14+
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
15+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
16+
import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
17+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
18+
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
19+
import org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction;
20+
import org.springframework.web.bind.annotation.GetMapping;
21+
import org.springframework.web.bind.annotation.RestController;
22+
import org.springframework.web.reactive.function.client.WebClient;
23+
24+
import java.util.HashMap;
25+
import java.util.Map;
26+
27+
import static com.azure.spring.test.EnvironmentVariable.AAD_TENANT_ID_1;
28+
import static com.azure.spring.test.EnvironmentVariable.CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_ID;
29+
import static com.azure.spring.test.EnvironmentVariable.CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_SECRET;
30+
import static com.azure.spring.test.EnvironmentVariable.CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_B_CLIENT_ID;
31+
import static org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient;
32+
33+
public class ConditionalAccessPolicyTestWebApiA {
34+
private static final Logger LOGGER = LoggerFactory.getLogger(ConditionalAccessPolicyTestWebApiA.class);
35+
private static final String WEB_API_B_ENDPOINT = "http://localhost:8883/webapiB";
36+
37+
private static void start() {
38+
Map<String, String> properties = new HashMap<>();
39+
properties.put("server.port", "8882");
40+
properties.put("azure.activedirectory.client-id", CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_ID);
41+
properties.put("azure.activedirectory.client-secret", CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_SECRET);
42+
properties.put("azure.activedirectory.tenant-id", AAD_TENANT_ID_1);
43+
properties.put("azure.activedirectory.authorization-clients.webapiB.scopes",
44+
"api://" + CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_B_CLIENT_ID + "/File.Read");
45+
properties.put("azure.activedirectory.app-id-uri",
46+
"api://" + CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_ID);
47+
AppRunner app = new AppRunner(DumbApp.class);
48+
properties.forEach(app::property);
49+
app.start();
50+
}
51+
52+
public static void main(String[] args) {
53+
start();
54+
}
55+
56+
@EnableWebSecurity
57+
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
58+
@SpringBootApplication
59+
@RestController
60+
public static class DumbApp extends WebSecurityConfigurerAdapter {
61+
62+
@Autowired
63+
private WebClient webClient;
64+
65+
@Override
66+
protected void configure(HttpSecurity http) throws Exception {
67+
http.authorizeRequests()
68+
.anyRequest().authenticated()
69+
.and()
70+
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
71+
}
72+
73+
@Bean
74+
public static WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
75+
OAuth2AuthorizedClientRepository authorizedClientRepository) {
76+
ServletOAuth2AuthorizedClientExchangeFilterFunction function =
77+
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrationRepository,
78+
authorizedClientRepository);
79+
return WebClient.builder()
80+
.apply(function.oauth2Configuration())
81+
.build();
82+
}
83+
84+
85+
/**
86+
* webapiA, return response body.
87+
*
88+
* @param webapiB authorized client for webapiB
89+
* @return Response response body data.
90+
*/
91+
@GetMapping("webapiA")
92+
@PreAuthorize("hasAuthority('SCOPE_File.Read')")
93+
public String callCustom(@RegisteredOAuth2AuthorizedClient("webapiB") OAuth2AuthorizedClient webapiB) {
94+
return callCustomLocalFileEndpoint(webapiB);
95+
}
96+
97+
/**
98+
* Call webapiB endpoint
99+
*
100+
* @param webapiB Authorized Client
101+
* @return Response string data.
102+
*/
103+
private String callCustomLocalFileEndpoint(OAuth2AuthorizedClient webapiB) {
104+
String body = webClient
105+
.get()
106+
.uri(WEB_API_B_ENDPOINT)
107+
.attributes(oauth2AuthorizedClient(webapiB))
108+
.retrieve()
109+
.bodyToMono(String.class)
110+
.block();
111+
LOGGER.info("Response from webapiB: {}", body);
112+
return body;
113+
}
114+
}
115+
}

sdk/spring/azure-spring-boot-test-aad-obo/src/test/java/com/azure/test/aad/webapi/AADWebApiOboIT.java renamed to sdk/spring/azure-spring-boot-test-aad-obo/src/test/java/com/azure/test/aad/webapi/obo/AADWebApiOboIT.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
package com.azure.test.aad.webapi;
4+
package com.azure.test.aad.webapi.obo;
55

66
import com.azure.spring.test.AppRunner;
77
import com.azure.spring.test.aad.AADWebApiITHelper;
@@ -71,7 +71,7 @@ public void testCallGraph() {
7171
}
7272

7373
private void runApp(Consumer<AppRunner> command) {
74-
try (AppRunner app = new AppRunner(AADWebApiOboIT.DumbApp.class)) {
74+
try (AppRunner app = new AppRunner(DumbApp.class)) {
7575
app.start();
7676
command.accept(app);
7777
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.azure.test.aad.conditional.access.webapi.b;
2+
3+
import com.azure.spring.test.AppRunner;
4+
import org.springframework.boot.autoconfigure.SpringBootApplication;
5+
import org.springframework.security.access.prepost.PreAuthorize;
6+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
7+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
8+
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
9+
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
10+
import org.springframework.security.config.annotation.web.configurers.oauth2.server.resource.OAuth2ResourceServerConfigurer;
11+
import org.springframework.web.bind.annotation.GetMapping;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
import java.util.HashMap;
15+
import java.util.Map;
16+
17+
import static com.azure.spring.test.EnvironmentVariable.CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_B_CLIENT_ID;
18+
19+
public class ConditionalAccessPolicyTestWebApiB {
20+
private static void start() {
21+
Map<String, String> properties = new HashMap<>();
22+
properties.put("azure.activedirectory.client-id", CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_B_CLIENT_ID);
23+
properties.put("azure.activedirectory.app-id-uri", "api://" + CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_B_CLIENT_ID);
24+
properties.put("server.port", "8883");
25+
AppRunner app = new AppRunner(DumbApp.class);
26+
properties.forEach(app::property);
27+
app.start();
28+
}
29+
30+
public static void main(String[] args) {
31+
start();
32+
}
33+
34+
@EnableWebSecurity
35+
@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
36+
@SpringBootApplication
37+
@RestController
38+
public static class DumbApp extends WebSecurityConfigurerAdapter {
39+
40+
@Override
41+
protected void configure(HttpSecurity http) throws Exception {
42+
http.authorizeRequests()
43+
.anyRequest().authenticated()
44+
.and()
45+
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
46+
}
47+
48+
@GetMapping("/webapiB")
49+
@PreAuthorize("hasAuthority('SCOPE_File.Read')")
50+
public String file() {
51+
return "Response from webapiB.";
52+
}
53+
}
54+
}

sdk/spring/azure-spring-boot-test-aad/README.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,78 @@
22

33
## Key concepts
44
## Getting started
5+
6+
### How to run AADConditionalAccessIT.
7+
8+
##### What is AAD Conditional Access in AAD?
9+
10+
Azure Active Directory is a token-based authentication platform, [On-Behalf-Of flow] is the middle-tier service to make authenticated requests to the downstream service through token.
11+
[Conditional Access] is the tool used by Azure Active Directory to bring signals together, to make decisions, and enforce organizational policies.
12+
In some scenarios, Conditional Access may cause the token get from middle-tier service was useless. AAD stater can help us complete the verification again and obtain a valid token again.
13+
![Policy Flow](docs/image-conditional-access-flow.png)
14+
15+
##### Create applications and configure it.
16+
1. First, we need create three applications. The registered application name is ***webapp***, ***webapiA*** and ***webapiB***.![Application Name](docs/image-application-name.png)
17+
1. Under ***webapiA*** and ***webapiB*** application, select Expose an API -> Add a scope. Then set the Scope name to File.Read. click Add scope button.![API Permissions](docs/image-webapiA-add-scope.png) ![API Permissions](docs/image-webapiB-add-scope.png)
18+
1. After creating the scope. Expose scopes for ***webapp*** and ***webapiA***. Select API permissions > Add a permission > My APIs, select ***webapiA*** or ***webapiB*** application name.![Select MyAPIs](docs/image-webapp-select-myapis.png) ![Select MyAPIs](docs/image-webapiA-select-myapis.png)
19+
1. **Delegated permissions** is selected by default, Select **File** > **File.Read** permission, select **Add permission** to complete the process.![Add Permissions](docs/image-webapp-add-permissions.png) ![Add Permissions](docs/image-webapiA-add-permissions.png)
20+
1. Grant admin consent for ***webapiA*** and ***webapiB*** permissions.![API Permissions](docs/image-webapp-add-grant-admin-consent.png) ![API Permissions](docs/image-webapiA-add-grant-admin-consent.png)
21+
22+
##### Azure subscriptions
23+
24+
##### Config Conditional Access Policy.
25+
1. Open the home of azure -> Select Security. ![Select Permissions](docs/image-conditional-access-home.png)
26+
1. Select Conditional Access button.![Select Button](docs/image-conditional-access-button.png)
27+
1. Then create a new policy.![Create Policy](docs/image-conditional-access-new-policy.png)
28+
1. We need to prepare a user or a group. As below, we need to configure users or groups to follow this policy.![Add User](docs/image-conditional-access-add-user.png)
29+
1. As before, we need to configure one or more applications to follow this policy. In our case, we need to configure the ***webapiB*** application.![Add Application](docs/image-conditional-access-add-application.png)
30+
1. In our case, we use [Multi-Factor Authentication] as Conditional Access Policy. So select `Require multi-factor authentication` in ***Grant***.![API Policy](docs/image-conditional-access-add-MFA.png)
31+
1. Finally, enable it.(If can’t create a policy, maybe you need to close [Access management for Azure resources].)![Enable Conditional Access](docs/image-conditional-access-enable.png)
32+
33+
##### Run AADConditionalAccessIT
34+
35+
This case is difficult to run automatically, we need to execute it manually.
36+
Here are the steps to start this case.
37+
38+
- Prepare application and Conditional Access Policy as above.
39+
- Prepare environment variables for `ConditionalAccessPolicyTestWebApiB`(***com.azure.test.aad.conditional.access.webapi.b***) as below. The environment variables are the parameters of our ***webapiB*** application and application server port Will start at 8883.
40+
41+
| webapiB application requires environment variable name | Actual Value |
42+
| ---: | :----: |
43+
| CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_B_CLIENT_ID | your webapiB application id |
44+
45+
- Same as `ConditionalAccessPolicyTestWebApiB`, The environment variables of `ConditionalAccessPolicyTestWebApiA`(***com.azure.test.aad.webapi.conditional.access.webapi.a***) is the parameters of our ***webapiA*** application and application server port Will start at 8882.
46+
47+
| webapiA application requires environment variable name | Actual Value |
48+
| ---: | :----: |
49+
| CONDITIONAL_ACCESS_POLICY_TEST_WEA_API_A_CLIENT_ID | your webapiA application id |
50+
| CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_SECRET | your webapiA application secret |
51+
| CONDITIONAL_ACCESS_POLICY_TEST_WEA_API_B_CLIENT_ID | your webapiB application id |
52+
| AAD_TENANT_ID_1 | your tenant id |
53+
54+
- At last, Prepare the environment variables of `AADConditionalAccessIT` through the parameters of ***webapp*** application.
55+
56+
| webapp application requires environment variable name | Actual Value |
57+
| ---: | :----: |
58+
| CONDITIONAL_ACCESS_POLICY_TEST_WEB_API_A_CLIENT_ID | your webapiA application id |
59+
| AAD_SINGLE_TENANT_CLIENT_ID | your webapp application id |
60+
| AAD_SINGLE_TENANT_CLIENT_SECRET | your webapp application secret |
61+
| AAD_TENANT_ID_1 | your tenant id |
62+
| AZURE_CLOUD_TYPE | your azure subscription type |
63+
64+
- Start the `conditionalAccessTest()` method.
65+
- Then our Authenticator App in mobile phone will receive a message that requires your approval for this login request.(If you log in for the first time, you need to follow the instructions to set up the Authenticator App) After we agree, we can obtain a valid token.
66+
- Finally, we can get the data from ***webapiB***, this case passed.
67+
68+
69+
570
## Examples
671
## Troubleshooting
772
## Next steps
873
## Contributing
74+
75+
<!-- LINKS -->
76+
[On-Behalf-Of flow]: https://docs.microsoft.com/azure/active-directory/develop/v2-oauth2-on-behalf-of-flow
77+
[Multi-Factor Authentication]: https://docs.microsoft.com/azure/active-directory/authentication/tutorial-enable-azure-mfa
78+
[Conditional Access]: https://docs.microsoft.com/azure/active-directory/conditional-access/overview
79+
[Access management for Azure resources]: https://docs.microsoft.com/azure/active-directory/fundamentals/concept-fundamentals-security-defaults
151 KB
Loading
123 KB
Loading
144 KB
Loading
149 KB
Loading
138 KB
Loading
69.3 KB
Loading

0 commit comments

Comments
 (0)