Skip to content

Commit c484800

Browse files
committed
[SYNCOPE-2007] Adding configurable JCacheSessionStore for OIDCC4UI
1 parent 55d5ffc commit c484800

18 files changed

Lines changed: 376 additions & 71 deletions

File tree

‎client/idm/console/src/main/java/org/apache/syncope/client/console/topology/Topology.java‎

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ public class Topology extends BasePage {
7373

7474
public static final String CONNECTOR_SERVER_LOCATION_PREFIX = "connid://";
7575

76+
public static String sanitizeMarkupId(final String input) {
77+
return input.replaceAll("[^a-zA-Z0-9._-]", "_");
78+
}
79+
7680
@SpringBean
7781
protected ServiceOps serviceOps;
7882

@@ -224,7 +228,7 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
224228
// Add Syncope (root topologynode)
225229
// -----------------------------------------
226230
String rootName = StringUtils.capitalize(Constants.SYNCOPE);
227-
final TopologyNode syncopeTopologyNode = new TopologyNode(rootName, rootName, TopologyNode.Kind.SYNCOPE);
231+
TopologyNode syncopeTopologyNode = new TopologyNode(rootName, rootName, TopologyNode.Kind.SYNCOPE);
228232
syncopeTopologyNode.setX(origX);
229233
syncopeTopologyNode.setY(origY);
230234

@@ -234,9 +238,9 @@ public void onClick(final AjaxRequestTarget target, final Serializable ignore) {
234238

235239
body.add(topologyNodePanel(Constants.SYNCOPE, syncopeTopologyNode, false));
236240

237-
Map<Serializable, Map<Serializable, TopologyNode>> connections = new HashMap<>();
238-
Map<Serializable, TopologyNode> syncopeConnections = new HashMap<>();
239-
connections.put(syncopeTopologyNode.getKey(), syncopeConnections);
241+
Map<String, Map<String, TopologyNode>> connections = new HashMap<>();
242+
Map<String, TopologyNode> syncopeConnections = new HashMap<>();
243+
connections.put(sanitizeMarkupId(syncopeTopologyNode.getKey()), syncopeConnections);
240244

241245
// required to retrieve parent positions
242246
Map<String, TopologyNode> servers = new HashMap<>();
@@ -273,8 +277,8 @@ protected void populateItem(final ListItem<URI> item) {
273277

274278
item.add(topologyNodePanel("cs", topologynode, false));
275279

276-
syncopeConnections.put(url, topologynode);
277-
connections.put(url, new HashMap<>());
280+
syncopeConnections.put(sanitizeMarkupId(url), topologynode);
281+
connections.put(sanitizeMarkupId(url), new HashMap<>());
278282
}
279283
};
280284

@@ -312,8 +316,8 @@ protected void populateItem(final ListItem<URI> item) {
312316

313317
item.add(topologyNodePanel("fp", topologynode, false));
314318

315-
syncopeConnections.put(url, topologynode);
316-
connections.put(url, new HashMap<>());
319+
syncopeConnections.put(sanitizeMarkupId(url), topologynode);
320+
connections.put(sanitizeMarkupId(url), new HashMap<>());
317321
}
318322
};
319323

@@ -369,7 +373,7 @@ protected void populateItem(final ListItem<ConnInstanceTO> item) {
369373
item.add(topologyNodePanel("conn", topologynode, conn.isErrored()));
370374

371375
// Update connections
372-
Map<Serializable, TopologyNode> remoteConnections;
376+
Map<String, TopologyNode> remoteConnections;
373377
if (connections.containsKey(conn.getLocation())) {
374378
remoteConnections = connections.get(conn.getLocation());
375379
} else {
@@ -404,7 +408,7 @@ protected void populateItem(final ListItem<ConnInstanceTO> item) {
404408
TopologyNode topologynode = new TopologyNode(
405409
resourceTO.getKey(), resourceTO.getKey(), TopologyNode.Kind.RESOURCE);
406410

407-
Map<Serializable, TopologyNode> remoteConnections;
411+
Map<String, TopologyNode> remoteConnections;
408412
if (connections.containsKey(resourceTO.getConnector())) {
409413
remoteConnections = connections.get(resourceTO.getConnector());
410414
} else {
@@ -480,10 +484,11 @@ protected void populateItem(final ListItem<TopologyNode> item) {
480484

481485
@Override
482486
public void renderHead(final Component component, final IHeaderResponse response) {
483-
final StringBuilder jsPlumbConf = new StringBuilder();
484-
jsPlumbConf.append(String.format(Locale.US, "activate(%.2f);", 0.68f));
487+
StringBuilder jsPlumbConf = new StringBuilder(String.format(Locale.US, "activate(%.2f);", 0.68f));
488+
489+
connections.forEach((key, value) -> value.forEach((label, node) -> jsPlumbConf.append(
490+
"connect('%s','%s','%s');".formatted(sanitizeMarkupId(key), label, node.getKind()))));
485491

486-
createConnections(connections).forEach(jsPlumbConf::append);
487492
jsPlumbConf.append("restoreTopologyLayout();");
488493

489494
response.render(OnDomReadyHeaderItem.forScript(jsPlumbConf.toString()));
@@ -538,18 +543,9 @@ protected void populateItem(final ListItem<TopologyNode> item) {
538543
newlyCreatedContainer.add(newlyCreated);
539544
}
540545

541-
private static List<String> createConnections(final Map<Serializable, Map<Serializable, TopologyNode>> targets) {
542-
List<String> list = new ArrayList<>();
543-
544-
targets.forEach((key, value) -> value.forEach((label, node) -> list.add(
545-
"connect('%s','%s','%s');".formatted(key, label, node.getKind()))));
546-
547-
return list;
548-
}
549-
550546
private TopologyNodePanel topologyNodePanel(final String id, final TopologyNode node, final boolean errored) {
551547
TopologyNodePanel panel = new TopologyNodePanel(id, node, errored);
552-
panel.setMarkupId(String.valueOf(node.getKey()));
548+
panel.setMarkupId(sanitizeMarkupId(node.getKey()));
553549
panel.setOutputMarkupId(true);
554550

555551
List<Behavior> behaviors = new ArrayList<>();
@@ -561,7 +557,7 @@ private TopologyNodePanel topologyNodePanel(final String id, final TopologyNode
561557
@Override
562558
public void renderHead(final Component component, final IHeaderResponse response) {
563559
response.render(OnDomReadyHeaderItem.forScript("setPosition('%s', %d, %d)".
564-
formatted(node.getKey(), node.getX(), node.getY())));
560+
formatted(sanitizeMarkupId(node.getKey()), node.getX(), node.getY())));
565561
}
566562
});
567563

‎client/idm/console/src/main/java/org/apache/syncope/client/console/topology/TopologyNodePanel.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public TopologyNodePanel(final String id, final TopologyNode node, final boolean
9494
add(AttributeModifier.append("data-original-title", title));
9595
}
9696

97-
this.setMarkupId(node.getDisplayName());
97+
setMarkupId(Topology.sanitizeMarkupId(node.getDisplayName()));
9898
}
9999

100100
@Override
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
# with Core replication, enabling the session store requires to setup a JCache distributed provider
19+
oidc.c4ui.session-store-enabled=false
20+
oidc.c4ui.session-store-expiration-minutes=10

‎ext/oidcc4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/oidcc4ui/BeforeLogoutResource.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected ResourceResponse newResourceResponse(final Attributes attributes) {
5050
response.setStatusCode(Response.Status.FOUND.getStatusCode());
5151
response.getHeaders().addHeader(HttpHeaders.CACHE_CONTROL, "no-cache, no-store");
5252
response.getHeaders().addHeader("Pragma", "no-cache");
53-
response.getHeaders().addHeader(HttpHeaders.LOCATION, logoutRequest.getLocation());
53+
response.getHeaders().addHeader(HttpHeaders.LOCATION, logoutRequest.location());
5454

5555
Session.get().invalidate();
5656

‎ext/oidcc4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/oidcc4ui/CodeConsumerResource.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ protected ResourceResponse newResourceResponse(final Attributes attributes) {
6161

6262
OIDCC4UIService service = BaseSession.class.cast(Session.get()).getAnonymousService(OIDCC4UIService.class);
6363
OIDCLoginResponse response = service.login(
64+
Session.get().getAttribute(OIDCConstants.REQUEST_ID).toString(),
6465
request.getRequestURL().toString(),
6566
authorizationCode,
6667
Session.get().getAttribute(OIDCConstants.OP).toString());

‎ext/oidcc4ui/client-common-ui/src/main/java/org/apache/syncope/client/ui/commons/resources/oidcc4ui/LoginResource.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,13 @@ protected ResourceResponse newResourceResponse(final Attributes attributes) {
5454
OIDCRequest loginRequest = service.createLoginRequest(redirectURI, op, reauth);
5555

5656
Session.get().setAttribute(OIDCConstants.OP, op);
57+
Session.get().setAttribute(OIDCConstants.REQUEST_ID, loginRequest.id());
5758

5859
ResourceResponse response = new ResourceResponse();
5960
response.setStatusCode(Response.Status.FOUND.getStatusCode());
6061
response.getHeaders().addHeader(HttpHeaders.CACHE_CONTROL, "no-cache, no-store");
6162
response.getHeaders().addHeader("Pragma", "no-cache");
62-
response.getHeaders().addHeader(HttpHeaders.LOCATION, loginRequest.getLocation());
63+
response.getHeaders().addHeader(HttpHeaders.LOCATION, loginRequest.location());
6364

6465
return response;
6566
}

‎ext/oidcc4ui/common-lib/src/main/java/org/apache/syncope/common/lib/oidc/OIDCConstants.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public final class OIDCConstants {
2626

2727
public static final String OP = "op";
2828

29+
public static final String REQUEST_ID = "requestId";
30+
2931
public static final String LOGOUT_TOKEN = "logout_token";
3032

3133
private OIDCConstants() {

‎ext/oidcc4ui/common-lib/src/main/java/org/apache/syncope/common/lib/oidc/OIDCRequest.java‎

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,6 @@
2020

2121
import java.io.Serializable;
2222

23-
public class OIDCRequest implements Serializable {
23+
public record OIDCRequest(String id, String location) implements Serializable {
2424

25-
private static final long serialVersionUID = -3509031322459942441L;
26-
27-
private String location;
28-
29-
public String getLocation() {
30-
return location;
31-
}
32-
33-
public void setLocation(final String location) {
34-
this.location = location;
35-
}
3625
}

‎ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogic.java‎

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@
4141
import org.apache.syncope.common.lib.types.CipherAlgorithm;
4242
import org.apache.syncope.common.lib.types.ClientExceptionType;
4343
import org.apache.syncope.common.lib.types.IdRepoEntitlement;
44-
import org.apache.syncope.core.logic.oidc.NoOpSessionStore;
4544
import org.apache.syncope.core.logic.oidc.OIDCC4UIContext;
4645
import org.apache.syncope.core.logic.oidc.OIDCClientCache;
4746
import org.apache.syncope.core.logic.oidc.OIDCUserManager;
@@ -56,6 +55,7 @@
5655
import org.apache.syncope.core.spring.security.AuthDataAccessor;
5756
import org.pac4j.core.context.CallContext;
5857
import org.pac4j.core.context.WebContext;
58+
import org.pac4j.core.context.session.SessionStore;
5959
import org.pac4j.core.credentials.Credentials;
6060
import org.pac4j.core.credentials.SessionKeyCredentials;
6161
import org.pac4j.core.exception.http.WithLocationAction;
@@ -73,6 +73,8 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
7373

7474
protected static final String JWT_CLAIM_ID_TOKEN = "ID_TOKEN";
7575

76+
protected static final String NO_OP_REQUEST_ID = "noop";
77+
7678
protected final OIDCClientCache oidcClientCacheLogin;
7779

7880
protected final OIDCClientCache oidcClientCacheLogout;
@@ -89,6 +91,8 @@ public class OIDCC4UILogic extends AbstractTransactionalLogic<EntityTO> {
8991

9092
protected final EncryptorManager encryptorManager;
9193

94+
protected final SessionStore sessionStore;
95+
9296
public OIDCC4UILogic(
9397
final OIDCClientCache oidcClientCacheLogin,
9498
final OIDCClientCache oidcClientCacheLogout,
@@ -97,7 +101,8 @@ public OIDCC4UILogic(
97101
final OIDCC4UIProviderDAO opDAO,
98102
final AccessTokenDAO accessTokenDAO,
99103
final OIDCUserManager userManager,
100-
final EncryptorManager encryptorManager) {
104+
final EncryptorManager encryptorManager,
105+
final SessionStore sessionStore) {
101106

102107
this.oidcClientCacheLogin = oidcClientCacheLogin;
103108
this.oidcClientCacheLogout = oidcClientCacheLogout;
@@ -107,6 +112,7 @@ public OIDCC4UILogic(
107112
this.accessTokenDAO = accessTokenDAO;
108113
this.userManager = userManager;
109114
this.encryptorManager = encryptorManager;
115+
this.sessionStore = sessionStore;
110116
}
111117

112118
protected OidcClient getOidcClient(
@@ -147,22 +153,25 @@ protected Map<String, String> buildParams(final WebContext webContext) {
147153
});
148154

149155
// 2. create OIDCRequest
150-
WithLocationAction action = oidcClient.getRedirectionAction(
151-
new CallContext(new OIDCC4UIContext(), NoOpSessionStore.INSTANCE)).
156+
OIDCC4UIContext ctx = new OIDCC4UIContext();
157+
WithLocationAction action = oidcClient.getRedirectionAction(new CallContext(ctx, sessionStore)).
152158
map(WithLocationAction.class::cast).
153159
orElseThrow(() -> {
154160
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
155161
sce.getElements().add("No RedirectionAction generated for LoginRequest");
156162
return sce;
157163
});
158164

159-
OIDCRequest loginRequest = new OIDCRequest();
160-
loginRequest.setLocation(action.getLocation());
161-
return loginRequest;
165+
return new OIDCRequest(Optional.ofNullable(ctx.getId()).orElse(NO_OP_REQUEST_ID), action.getLocation());
162166
}
163167

164168
@PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
165-
public OIDCLoginResponse login(final String redirectURI, final String authorizationCode, final String opName) {
169+
public OIDCLoginResponse login(
170+
final String requestId,
171+
final String redirectURI,
172+
final String authorizationCode,
173+
final String opName) {
174+
166175
// 0. look for OP
167176
OIDCC4UIProvider op = opDAO.findByName(opName).
168177
orElseThrow(() -> new NotFoundException("OIDC Provider '" + opName + '\''));
@@ -171,14 +180,16 @@ public OIDCLoginResponse login(final String redirectURI, final String authorizat
171180
OidcClient oidcClient = getOidcClient(oidcClientCacheLogin, op, redirectURI);
172181

173182
// 2. get OpenID Connect tokens
183+
OIDCC4UIContext ctx = new OIDCC4UIContext();
184+
ctx.setId(requestId);
185+
174186
String idTokenHint = null;
175187
JWTClaimsSet claimsSet = null;
176188
try {
177189
OidcCredentials credentials = new OidcCredentials();
178190
credentials.setCode(authorizationCode);
179191

180-
oidcClient.getAuthenticator().validate(
181-
new CallContext(new OIDCC4UIContext(), NoOpSessionStore.INSTANCE), credentials);
192+
oidcClient.getAuthenticator().validate(new CallContext(ctx, sessionStore), credentials);
182193

183194
JWT idToken = credentials.toIdToken();
184195
if (idToken == null) {
@@ -324,8 +335,9 @@ public OIDCRequest createLogoutRequest(final String accessToken, final String re
324335
OidcProfile profile = new OidcProfile();
325336
profile.setIdTokenString((String) claimsSet.getClaim(JWT_CLAIM_ID_TOKEN));
326337

338+
OIDCC4UIContext ctx = new OIDCC4UIContext();
327339
WithLocationAction action = oidcClient.getLogoutAction(
328-
new CallContext(new OIDCC4UIContext(), NoOpSessionStore.INSTANCE),
340+
new CallContext(ctx, sessionStore),
329341
profile,
330342
redirectURI).
331343
map(WithLocationAction.class::cast).
@@ -335,9 +347,7 @@ public OIDCRequest createLogoutRequest(final String accessToken, final String re
335347
return sce;
336348
});
337349

338-
OIDCRequest logoutRequest = new OIDCRequest();
339-
logoutRequest.setLocation(action.getLocation());
340-
return logoutRequest;
350+
return new OIDCRequest(Optional.ofNullable(ctx.getId()).orElse(NO_OP_REQUEST_ID), action.getLocation());
341351
}
342352

343353
@PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')")
@@ -371,7 +381,7 @@ public Optional<String> getRequestParameter(final String name) {
371381
}
372382
return Optional.empty();
373383
}
374-
}, NoOpSessionStore.INSTANCE)).orElseThrow(() -> {
384+
}, sessionStore)).orElseThrow(() -> {
375385
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.Unknown);
376386
sce.getElements().add("Could not validate the logout token");
377387
return sce;

0 commit comments

Comments
 (0)