Skip to content

Commit

Permalink
[SYNCOPE-1784] Allow custom scopes for OIDC client applications (#535)
Browse files Browse the repository at this point in the history
* [SYNCOPE-1784] Allow custom scopes for OIDC client applications
  • Loading branch information
SamuelGaro authored Oct 25, 2023
1 parent a1f4807 commit a9ea9fe
Show file tree
Hide file tree
Showing 10 changed files with 77 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,14 @@
import org.apache.syncope.client.ui.commons.panels.WizardModalPanel;
import org.apache.syncope.client.ui.commons.wizards.AbstractModalPanelBuilder;
import org.apache.syncope.client.ui.commons.wizards.AjaxWizard;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.policy.PolicyTO;
import org.apache.syncope.common.lib.to.ClientAppTO;
import org.apache.syncope.common.lib.to.RealmTO;
import org.apache.syncope.common.lib.types.ClientAppType;
import org.apache.syncope.common.lib.types.LogoutType;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.types.OIDCSubjectType;
import org.apache.syncope.common.lib.types.PolicyType;
import org.apache.syncope.common.lib.types.SAML2SPNameId;
Expand Down Expand Up @@ -319,10 +319,26 @@ protected void onUpdate(final AjaxRequestTarget target) {
new PropertyModel<>(clientAppTO, "supportedResponseTypes"),
new ListModel<>(List.of(OIDCResponseType.values()))));

fields.add(new AjaxPalettePanel.Builder<OIDCScope>().setName("scopes").build(
AutoCompleteSettings scopesSettings = new AutoCompleteSettings();
scopesSettings.setShowCompleteListOnFocusGain(true);
scopesSettings.setShowListOnEmptyInput(true);
AjaxSearchFieldPanel scopes = new AjaxSearchFieldPanel(
"panel", "scopes", new PropertyModel<>(clientAppTO, "scopes"), scopesSettings) {

private static final long serialVersionUID = 7160878678968866138L;

@Override
protected Iterator<String> getChoices(final String input) {
List<String> choices = new ArrayList<>(OIDCScopeConstants.ALL_STANDARD_SCOPES);
choices.add(OIDCScopeConstants.SYNCOPE);
return choices.iterator();
}
};
fields.add(new MultiFieldPanel.Builder<String>(
new PropertyModel<>(clientAppTO, "scopes")).build(
"field",
new PropertyModel<>(clientAppTO, "scopes"),
new ListModel<>(List.of(OIDCScope.values()))));
"scopes",
scopes));

AjaxTextFieldPanel logoutUri = new AjaxTextFieldPanel(
"field", "logoutUri", new PropertyModel<>(clientAppTO, "logoutUri"), false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,27 @@
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.syncope.common.lib.types;
package org.apache.syncope.common.lib;

public enum OIDCScope {
openid,
profile,
email,
address,
phone
import java.util.List;

public final class OIDCScopeConstants {

public static final String OPEN_ID = "openid";

public static final String PROFILE = "profile";

public static final String EMAIL = "email";

public static final String ADDRESS = "address";

public static final String PHONE = "phone";

public static final String SYNCOPE = "syncope";

public static final List<String> ALL_STANDARD_SCOPES = List.of(OPEN_ID, PROFILE, EMAIL, ADDRESS, PHONE);

private OIDCScopeConstants() {
// private constructor for static utility class
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.types.OIDCSubjectType;

@Schema(allOf = { ClientAppTO.class })
Expand All @@ -52,7 +51,7 @@ public class OIDCRPClientAppTO extends ClientAppTO {

private final List<OIDCResponseType> supportedResponseTypes = new ArrayList<>();

private final List<OIDCScope> scopes = new ArrayList<>();
private final List<String> scopes = new ArrayList<>();

private String logoutUri;

Expand Down Expand Up @@ -135,7 +134,7 @@ public void setJwtAccessToken(final boolean jwtAccessToken) {

@JacksonXmlElementWrapper(localName = "scopes")
@JacksonXmlProperty(localName = "scope")
public List<OIDCScope> getScopes() {
public List<String> getScopes() {
return scopes;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import java.util.Set;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.types.OIDCSubjectType;

public interface OIDCRPClientApp extends ClientApp {
Expand All @@ -40,7 +39,7 @@ public interface OIDCRPClientApp extends ClientApp {

Set<OIDCResponseType> getSupportedResponseTypes();

Set<OIDCScope> getScopes();
Set<String> getScopes();

boolean isSignIdToken();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
import javax.persistence.Transient;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.types.OIDCSubjectType;
import org.apache.syncope.core.persistence.api.entity.am.OIDCRPClientApp;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
Expand All @@ -59,8 +58,8 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien
new TypeReference<Set<OIDCResponseType>>() {
};

protected static final TypeReference<Set<OIDCScope>> SCOPE_TYPEREF =
new TypeReference<Set<OIDCScope>>() {
protected static final TypeReference<Set<String>> SCOPE_TYPEREF =
new TypeReference<Set<String>>() {
};

@Column(unique = true, nullable = false)
Expand Down Expand Up @@ -99,7 +98,7 @@ public class JPAOIDCRPClientApp extends AbstractClientApp implements OIDCRPClien
private String scopes;

@Transient
private Set<OIDCScope> scopesSet = new HashSet<>();
private Set<String> scopesSet = new HashSet<>();

private String logoutUri;

Expand Down Expand Up @@ -179,7 +178,7 @@ public Set<OIDCResponseType> getSupportedResponseTypes() {
}

@Override
public Set<OIDCScope> getScopes() {
public Set<String> getScopes() {
return scopesSet;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.console.SyncopeConsoleSession;
Expand All @@ -41,10 +40,10 @@
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxPalettePanel;
import org.apache.syncope.client.ui.commons.markup.html.form.AjaxTextFieldPanel;
import org.apache.syncope.client.ui.commons.wizards.AjaxWizardBuilder;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.to.ImplementationTO;
import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO;
import org.apache.syncope.common.lib.types.OIDCClientImplementationType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.wicket.PageReference;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.extensions.wizard.WizardModel;
Expand Down Expand Up @@ -270,7 +269,7 @@ protected void onUpdate(final AjaxRequestTarget target) {
});

AjaxTextFieldPanel value = new AjaxTextFieldPanel("panel", "scopes", new Model<>());
value.setChoices(Stream.of(OIDCScope.values()).map(OIDCScope::name).collect(Collectors.toList()));
value.setChoices(OIDCScopeConstants.ALL_STANDARD_SCOPES);
content.add(new MultiFieldPanel.Builder<String>(
new PropertyModel<>(opTO, "scopes")).build("scopes", "scopes", value));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.lib.types.ClientAppType;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.types.OIDCSubjectType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
Expand Down Expand Up @@ -132,9 +132,9 @@ protected static void oidcClientAppSetup(
clientApp.setLogoutUri(SRA_ADDRESS + "/logout");
clientApp.setAuthPolicy(getAuthPolicy().getKey());
clientApp.setAttrReleasePolicy(getAttrReleasePolicy().getKey());
clientApp.getScopes().add(OIDCScope.openid);
clientApp.getScopes().add(OIDCScope.profile);
clientApp.getScopes().add(OIDCScope.email);
clientApp.getScopes().add(OIDCScopeConstants.OPEN_ID);
clientApp.getScopes().add(OIDCScopeConstants.PROFILE);
clientApp.getScopes().add(OIDCScopeConstants.EMAIL);
clientApp.getSupportedGrantTypes().add(OIDCGrantType.password);
clientApp.getSupportedGrantTypes().add(OIDCGrantType.authorization_code);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.http.Consts;
Expand All @@ -47,13 +45,13 @@
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.syncope.client.ui.commons.panels.OIDCC4UIConstants;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.to.Item;
import org.apache.syncope.common.lib.to.OIDCC4UIProviderTO;
import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.lib.types.ClientAppType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.types.OIDCSubjectType;
import org.apache.syncope.common.rest.api.RESTHeaders;
import org.apache.syncope.common.rest.api.service.wa.WAConfigService;
Expand Down Expand Up @@ -97,9 +95,9 @@ private static void clientAppSetup(final String appName, final String baseAddres
Set.of(OIDCResponseType.CODE, OIDCResponseType.ID_TOKEN_TOKEN, OIDCResponseType.TOKEN));
clientApp.setAuthPolicy(getAuthPolicy().getKey());
clientApp.setAttrReleasePolicy(getAttrReleasePolicy().getKey());
clientApp.getScopes().add(OIDCScope.openid);
clientApp.getScopes().add(OIDCScope.profile);
clientApp.getScopes().add(OIDCScope.email);
clientApp.getScopes().add(OIDCScopeConstants.OPEN_ID);
clientApp.getScopes().add(OIDCScopeConstants.PROFILE);
clientApp.getScopes().add(OIDCScopeConstants.EMAIL);

CLIENT_APP_SERVICE.update(ClientAppType.OIDCRP, clientApp);
WA_CONFIG_SERVICE.pushToWA(WAConfigService.PushSubject.clientApps, List.of());
Expand Down Expand Up @@ -142,7 +140,7 @@ private static void oidcSetup(
cas.setUserinfoEndpoint(cas.getIssuer() + "/profile");
cas.setEndSessionEndpoint(cas.getIssuer() + "/logout");

cas.getScopes().addAll(Stream.of(OIDCScope.values()).map(OIDCScope::name).collect(Collectors.toList()));
cas.getScopes().addAll(OIDCScopeConstants.ALL_STANDARD_SCOPES);
cas.getScopes().add("syncope");

cas.setCreateUnmatching(createUnmatching);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.client.lib.SyncopeClient;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.rest.api.service.AttrRepoService;
import org.apache.syncope.common.rest.api.service.AuthModuleService;
import org.apache.syncope.common.rest.api.service.wa.WAClientAppService;
Expand Down Expand Up @@ -156,16 +156,16 @@ public PropertySource<?> locate(final Environment environment) {
map(p -> p.getAllowedAttributes().stream().collect(Collectors.toSet())).
ifPresent(claims::addAll);
}
if (rp.getScopes().contains(OIDCScope.profile)) {
if (rp.getScopes().contains(OIDCScopeConstants.PROFILE)) {
claims.removeAll(OidcProfileScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}
if (rp.getScopes().contains(OIDCScope.address)) {
if (rp.getScopes().contains(OIDCScopeConstants.ADDRESS)) {
claims.removeAll(OidcAddressScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}
if (rp.getScopes().contains(OIDCScope.email)) {
if (rp.getScopes().contains(OIDCScopeConstants.EMAIL)) {
claims.removeAll(OidcEmailScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}
if (rp.getScopes().contains(OIDCScope.phone)) {
if (rp.getScopes().contains(OIDCScopeConstants.PHONE)) {
claims.removeAll(OidcPhoneScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.syncope.common.lib.OIDCScopeConstants;
import org.apache.syncope.common.lib.to.ClientAppTO;
import org.apache.syncope.common.lib.to.OIDCRPClientAppTO;
import org.apache.syncope.common.lib.types.OIDCGrantType;
import org.apache.syncope.common.lib.types.OIDCResponseType;
import org.apache.syncope.common.lib.types.OIDCScope;
import org.apache.syncope.common.lib.wa.WAClientApp;
import org.apereo.cas.oidc.claims.OidcAddressScopeAttributeReleasePolicy;
import org.apereo.cas.oidc.claims.OidcCustomScopeAttributeReleasePolicy;
Expand All @@ -51,8 +51,6 @@

public class OIDCRPClientAppTOMapper extends AbstractClientAppMapper {

private static final String CUSTOM_SCOPE = "syncope";

@Override
public boolean supports(final ClientAppTO clientApp) {
return OIDCRPClientAppTO.class.equals(clientApp.getClass());
Expand Down Expand Up @@ -92,9 +90,7 @@ public RegisteredService map(
Optional.ofNullable(rp.getSubjectType()).ifPresent(st -> service.setSubjectType(st.name()));
service.setLogoutUrl(rp.getLogoutUri());

service.setScopes(rp.getScopes().stream().
map(OIDCScope::name).
collect(Collectors.toSet()));
service.setScopes(new HashSet<>(rp.getScopes()));

ChainingAttributeReleasePolicy chain;
if (attributeReleasePolicy instanceof ChainingAttributeReleasePolicy) {
Expand All @@ -104,19 +100,19 @@ public RegisteredService map(
Optional.ofNullable(attributeReleasePolicy).ifPresent(chain::addPolicies);
}

if (rp.getScopes().contains(OIDCScope.openid)) {
if (rp.getScopes().contains(OIDCScopeConstants.OPEN_ID)) {
chain.addPolicies(new OidcOpenIdScopeAttributeReleasePolicy());
}
if (rp.getScopes().contains(OIDCScope.profile)) {
if (rp.getScopes().contains(OIDCScopeConstants.PROFILE)) {
chain.addPolicies(new OidcProfileScopeAttributeReleasePolicy());
}
if (rp.getScopes().contains(OIDCScope.address)) {
if (rp.getScopes().contains(OIDCScopeConstants.ADDRESS)) {
chain.addPolicies(new OidcAddressScopeAttributeReleasePolicy());
}
if (rp.getScopes().contains(OIDCScope.email)) {
if (rp.getScopes().contains(OIDCScopeConstants.EMAIL)) {
chain.addPolicies(new OidcEmailScopeAttributeReleasePolicy());
}
if (rp.getScopes().contains(OIDCScope.phone)) {
if (rp.getScopes().contains(OIDCScopeConstants.PHONE)) {
chain.addPolicies(new OidcPhoneScopeAttributeReleasePolicy());
}

Expand All @@ -135,24 +131,24 @@ public RegisteredService map(
map(p -> p.getAllowedAttributes().stream().collect(Collectors.toSet())).
ifPresent(customClaims::addAll);
}
if (rp.getScopes().contains(OIDCScope.profile)) {
if (rp.getScopes().contains(OIDCScopeConstants.PROFILE)) {
customClaims.removeAll(OidcProfileScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}
if (rp.getScopes().contains(OIDCScope.address)) {
if (rp.getScopes().contains(OIDCScopeConstants.ADDRESS)) {
customClaims.removeAll(OidcAddressScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}
if (rp.getScopes().contains(OIDCScope.email)) {
if (rp.getScopes().contains(OIDCScopeConstants.EMAIL)) {
customClaims.removeAll(OidcEmailScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}
if (rp.getScopes().contains(OIDCScope.phone)) {
if (rp.getScopes().contains(OIDCScopeConstants.PHONE)) {
customClaims.removeAll(OidcPhoneScopeAttributeReleasePolicy.ALLOWED_CLAIMS);
}

if (!customClaims.isEmpty()) {
service.getScopes().add(CUSTOM_SCOPE);
service.getScopes().add(OIDCScopeConstants.SYNCOPE);

chain.addPolicies(new OidcCustomScopeAttributeReleasePolicy(
CUSTOM_SCOPE, customClaims.stream().collect(Collectors.toList())));
OIDCScopeConstants.SYNCOPE, customClaims.stream().collect(Collectors.toList())));
}

setPolicies(service, authPolicy, mfaPolicy, accessStrategy, chain,
Expand Down

0 comments on commit a9ea9fe

Please sign in to comment.