1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.fit.ui;
20
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertNotNull;
23 import static org.junit.jupiter.api.Assertions.assertTrue;
24 import static org.junit.jupiter.api.Assertions.fail;
25
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.List;
29 import java.util.Optional;
30 import java.util.Set;
31 import javax.ws.rs.core.MediaType;
32 import javax.ws.rs.core.Response;
33 import org.apache.http.Consts;
34 import org.apache.http.HttpHeaders;
35 import org.apache.http.HttpStatus;
36 import org.apache.http.NameValuePair;
37 import org.apache.http.client.entity.UrlEncodedFormEntity;
38 import org.apache.http.client.methods.CloseableHttpResponse;
39 import org.apache.http.client.methods.HttpGet;
40 import org.apache.http.client.methods.HttpPost;
41 import org.apache.http.client.protocol.HttpClientContext;
42 import org.apache.http.impl.client.BasicCookieStore;
43 import org.apache.http.impl.client.CloseableHttpClient;
44 import org.apache.http.impl.client.HttpClients;
45 import org.apache.http.message.BasicNameValuePair;
46 import org.apache.http.util.EntityUtils;
47 import org.apache.syncope.client.ui.commons.panels.OIDCC4UIConstants;
48 import org.apache.syncope.common.lib.OIDCScopeConstants;
49 import org.apache.syncope.common.lib.SyncopeConstants;
50 import org.apache.syncope.common.lib.to.Item;
51 import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO;
52 import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
53 import org.apache.syncope.common.lib.types.ClientAppType;
54 import org.apache.syncope.common.lib.types.OIDCResponseType;
55 import org.apache.syncope.common.lib.types.OIDCSubjectType;
56 import org.apache.syncope.common.rest.api.RESTHeaders;
57 import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
58 import org.jsoup.Jsoup;
59 import org.junit.jupiter.api.BeforeAll;
60
61 public class OIDCC4UIITCase extends AbstractUIITCase {
62
63 private static void clientAppSetup(final String appName, final String baseAddress, final long appId) {
64 OIDCRPClientAppTO clientApp = CLIENT_APP_SERVICE.list(ClientAppType.OIDCRP).stream().
65 filter(app -> appName.equals(app.getName())).
66 map(OIDCRPClientAppTO.class::cast).
67 findFirst().
68 orElseGet(() -> {
69 OIDCRPClientAppTO app = new OIDCRPClientAppTO();
70 app.setName(appName);
71 app.setRealm(SyncopeConstants.ROOT_REALM);
72 app.setClientAppId(appId);
73 app.setClientId(appName);
74 app.setClientSecret(appName);
75 app.setBypassApprovalPrompt(false);
76
77 Response response = CLIENT_APP_SERVICE.create(ClientAppType.OIDCRP, app);
78 if (response.getStatusInfo().getStatusCode() != Response.Status.CREATED.getStatusCode()) {
79 fail("Could not create OIDC Client App");
80 }
81
82 return CLIENT_APP_SERVICE.read(
83 ClientAppType.OIDCRP, response.getHeaderString(RESTHeaders.RESOURCE_KEY));
84 });
85
86 clientApp.setClientId(appName);
87 clientApp.setClientSecret(appName);
88 clientApp.setSubjectType(OIDCSubjectType.PUBLIC);
89 clientApp.getRedirectUris().clear();
90 clientApp.getRedirectUris().add(baseAddress + OIDCC4UIConstants.URL_CONTEXT + "/code-consumer");
91 clientApp.setSignIdToken(true);
92 clientApp.setJwtAccessToken(true);
93 clientApp.setLogoutUri(baseAddress + OIDCC4UIConstants.URL_CONTEXT + "/logout");
94 clientApp.getSupportedResponseTypes().addAll(
95 Set.of(OIDCResponseType.CODE, OIDCResponseType.ID_TOKEN_TOKEN, OIDCResponseType.TOKEN));
96 clientApp.setAuthPolicy(getAuthPolicy().getKey());
97 clientApp.setAttrReleasePolicy(getAttrReleasePolicy().getKey());
98 clientApp.getScopes().add(OIDCScopeConstants.OPEN_ID);
99 clientApp.getScopes().add(OIDCScopeConstants.PROFILE);
100 clientApp.getScopes().add(OIDCScopeConstants.EMAIL);
101
102 CLIENT_APP_SERVICE.update(ClientAppType.OIDCRP, clientApp);
103 WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.clientApps, List.of());
104 }
105
106 private static String getAppName(final String address) {
107 return CONSOLE_ADDRESS.equals(address)
108 ? OIDCC4UIITCase.class.getName() + "_Console"
109 : OIDCC4UIITCase.class.getName() + "_Enduser";
110 }
111
112 @BeforeAll
113 public static void consoleClientAppSetup() {
114 clientAppSetup(getAppName(CONSOLE_ADDRESS), CONSOLE_ADDRESS, 7L);
115 }
116
117 @BeforeAll
118 public static void enduserClientAppSetup() {
119 clientAppSetup(getAppName(ENDUSER_ADDRESS), ENDUSER_ADDRESS, 8L);
120 }
121
122 private static void oidcSetup(
123 final String appName,
124 final boolean createUnmatching,
125 final boolean selfRegUnmatching) {
126
127 Optional<OIDCC4UIProviderTO> ops = OIDCC4UI_PROVIDER_SERVICE.list().stream().
128 filter(op -> op.getName().equals(appName)).findFirst();
129 if (ops.isEmpty()) {
130 OIDCC4UIProviderTO cas = new OIDCC4UIProviderTO();
131 cas.setName(appName);
132
133 cas.setClientID(appName);
134 cas.setClientSecret(appName);
135
136 cas.setIssuer(WA_ADDRESS + "/oidc");
137 cas.setAuthorizationEndpoint(cas.getIssuer() + "/authorize");
138 cas.setTokenEndpoint(cas.getIssuer() + "/accessToken");
139 cas.setJwksUri(cas.getIssuer() + "/jwks");
140 cas.setUserinfoEndpoint(cas.getIssuer() + "/profile");
141 cas.setEndSessionEndpoint(cas.getIssuer() + "/logout");
142
143 cas.getScopes().addAll(OIDCScopeConstants.ALL_STANDARD_SCOPES);
144 cas.getScopes().add("syncope");
145
146 cas.setCreateUnmatching(createUnmatching);
147 cas.setSelfRegUnmatching(selfRegUnmatching);
148
149 Item item = new Item();
150 item.setIntAttrName("username");
151 item.setExtAttrName("preferred_username");
152 item.setConnObjectKey(true);
153 cas.setConnObjectKeyItem(item);
154
155 item = new Item();
156 item.setIntAttrName("email");
157 item.setExtAttrName("email");
158 cas.add(item);
159
160 item = new Item();
161 item.setIntAttrName("userId");
162 item.setExtAttrName("email");
163 cas.add(item);
164
165 item = new Item();
166 item.setIntAttrName("firstname");
167 item.setExtAttrName("given_name");
168 cas.add(item);
169
170 item = new Item();
171 item.setIntAttrName("surname");
172 item.setExtAttrName("family_name");
173 cas.add(item);
174
175 item = new Item();
176 item.setIntAttrName("fullname");
177 item.setExtAttrName("name");
178 cas.add(item);
179
180 OIDCC4UI_PROVIDER_SERVICE.create(cas);
181 }
182 }
183
184 @BeforeAll
185 public static void consoleOIDCSetup() {
186 oidcSetup(getAppName(CONSOLE_ADDRESS), true, false);
187 }
188
189 @BeforeAll
190 public static void enduserOIDCSetup() {
191 oidcSetup(getAppName(ENDUSER_ADDRESS), false, true);
192 }
193
194 @Override
195 protected void sso(final String baseURL, final String username, final String password) throws IOException {
196 CloseableHttpClient httpclient = HttpClients.createDefault();
197 HttpClientContext context = HttpClientContext.create();
198 context.setCookieStore(new BasicCookieStore());
199
200
201 HttpGet get = new HttpGet(baseURL);
202 CloseableHttpResponse response = httpclient.execute(get, context);
203 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
204
205
206 get = new HttpGet(baseURL + OIDCC4UIConstants.URL_CONTEXT + "/login?op=" + getAppName(baseURL));
207 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
208 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
209 response = httpclient.execute(get, context);
210 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
211
212
213 String responseBody = EntityUtils.toString(response.getEntity());
214 response = authenticateToWA(username, password, responseBody, httpclient, context);
215
216
217 if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
218 responseBody = EntityUtils.toString(response.getEntity());
219
220
221 assertTrue(responseBody.contains("identifier"));
222 assertTrue(responseBody.contains("[value1]"));
223
224 String execution = extractWAExecution(responseBody);
225
226 List<NameValuePair> form = new ArrayList<>();
227 form.add(new BasicNameValuePair("_eventId", "confirm"));
228 form.add(new BasicNameValuePair("execution", execution));
229 form.add(new BasicNameValuePair("option", "1"));
230 form.add(new BasicNameValuePair("reminder", "30"));
231 form.add(new BasicNameValuePair("reminderTimeUnit", "days"));
232
233 HttpPost post = new HttpPost(WA_ADDRESS + "/login");
234 post.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
235 post.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
236 post.setEntity(new UrlEncodedFormEntity(form, Consts.UTF_8));
237 response = httpclient.execute(post, context);
238 }
239 assertEquals(HttpStatus.SC_MOVED_TEMPORARILY, response.getStatusLine().getStatusCode());
240
241
242 get = new HttpGet(response.getLastHeader(HttpHeaders.LOCATION).getValue());
243 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
244 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
245 response = httpclient.execute(get, context);
246 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
247
248 responseBody = EntityUtils.toString(response.getEntity());
249
250 String allow = Jsoup.parse(responseBody).body().
251 getElementsByTag("a").select("a[name=allow]").first().
252 attr("href");
253 assertNotNull(allow);
254
255
256 get = new HttpGet(allow);
257 get.addHeader(HttpHeaders.ACCEPT, MediaType.TEXT_HTML);
258 get.addHeader(HttpHeaders.ACCEPT_LANGUAGE, EN_LANGUAGE);
259 response = httpclient.execute(get, context);
260
261
262 assertEquals(HttpStatus.SC_OK, response.getStatusLine().getStatusCode());
263 assertTrue(EntityUtils.toString(response.getEntity()).contains(username));
264 }
265
266 @Override
267 protected void doSelfReg(final Runnable runnable) {
268 runnable.run();
269 }
270 }