commit
d0954ada62
|
@ -76,7 +76,9 @@
|
|||
<prop key="saml.acrs.reserverdPrefixes">urn:cesnet:</prop>
|
||||
<prop key="saml.acrs.enableComparison">false</prop>
|
||||
<prop key="saml.acrs.onlyreserved.append">urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</prop>
|
||||
<prop key="saml.user.attrIdentifier">eppn</prop><!-- eppn|epuid|eptid|uid|uniqueIdentifier -->
|
||||
<prop key="saml.user.attrIdentifier">eppn</prop><!-- eppn|epuid|eptid|uid|uniqueIdentifier|perunUserId -->
|
||||
<prop key="saml.user.lookup">original_auth</prop><!-- original_auth|perun_user_id|static_ext_source -->
|
||||
<prop key="saml.static_ext_idp"/>
|
||||
<!-- STATS JDBC -->
|
||||
<prop key="stats.jdbc.url">jdbc:mariadb://localhost:3306/STATS</prop>
|
||||
<prop key="stats.jdbc.user">user</prop>
|
||||
|
@ -146,6 +148,8 @@
|
|||
<property name="acrReservedPrefixes" value="#{'${saml.acrs.reserverdPrefixes}'.split('\s*,\s*')}"/>
|
||||
<property name="acrsToBeAdded" value="#{'${saml.acrs.onlyreserved.append}'.split('\s*,\s*')}"/>
|
||||
<property name="userIdentifierAttribute" value="${saml.user.attrIdentifier}"/>
|
||||
<property name="userLookupMode" value="${saml.user.lookup}"/>
|
||||
<property name="staticUserExtSource" value="${saml.static_ext_idp}"/>
|
||||
</bean>
|
||||
|
||||
<bean id="nonOverwrittenAttributeProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
|
||||
|
|
|
@ -23,7 +23,7 @@ public class PerunSamlUserDetailsService implements SAMLUserDetailsService {
|
|||
@Override
|
||||
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
|
||||
log.debug("Loading user for SAML credential");
|
||||
return FiltersUtils.getPerunUser(credential, perunAdapter, samlProperties.getUserIdentifierAttribute());
|
||||
return FiltersUtils.getPerunUser(credential, perunAdapter, samlProperties);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,17 +1,30 @@
|
|||
package cz.muni.ics.oidc.saml;
|
||||
|
||||
import cz.muni.ics.oidc.exceptions.ConfigurationException;
|
||||
import java.io.File;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SamlProperties implements InitializingBean {
|
||||
|
||||
public static final String LOOKUP_ORIGINAL_AUTH = "original_auth";
|
||||
public static final String LOOKUP_PERUN_USER_ID = "perun_user_id";
|
||||
public static final String LOOKUP_STATIC_EXT_SOURCE = "static_ext_source";
|
||||
|
||||
private String entityID;
|
||||
private String keystoreLocation;
|
||||
private String keystorePassword;
|
||||
|
@ -23,70 +36,8 @@ public class SamlProperties implements InitializingBean {
|
|||
private String[] acrReservedPrefixes;
|
||||
private String[] acrsToBeAdded;
|
||||
private String userIdentifierAttribute;
|
||||
|
||||
public String getEntityID() {
|
||||
return entityID;
|
||||
}
|
||||
|
||||
public void setEntityID(String entityID) {
|
||||
this.entityID = entityID;
|
||||
}
|
||||
|
||||
public String getKeystoreLocation() {
|
||||
return keystoreLocation;
|
||||
}
|
||||
|
||||
public void setKeystoreLocation(String keystoreLocation) {
|
||||
this.keystoreLocation = keystoreLocation;
|
||||
}
|
||||
|
||||
public String getKeystorePassword() {
|
||||
return keystorePassword;
|
||||
}
|
||||
|
||||
public void setKeystorePassword(String keystorePassword) {
|
||||
this.keystorePassword = keystorePassword;
|
||||
}
|
||||
|
||||
public String getKeystoreDefaultKey() {
|
||||
return keystoreDefaultKey;
|
||||
}
|
||||
|
||||
public void setKeystoreDefaultKey(String keystoreDefaultKey) {
|
||||
this.keystoreDefaultKey = keystoreDefaultKey;
|
||||
}
|
||||
|
||||
public String getKeystoreDefaultKeyPassword() {
|
||||
return keystoreDefaultKeyPassword;
|
||||
}
|
||||
|
||||
public void setKeystoreDefaultKeyPassword(String keystoreDefaultKeyPassword) {
|
||||
this.keystoreDefaultKeyPassword = keystoreDefaultKeyPassword;
|
||||
}
|
||||
|
||||
public String getDefaultIdpEntityId() {
|
||||
return defaultIdpEntityId;
|
||||
}
|
||||
|
||||
public void setDefaultIdpEntityId(String defaultIdpEntityId) {
|
||||
this.defaultIdpEntityId = defaultIdpEntityId;
|
||||
}
|
||||
|
||||
public String getIdpMetadataFile() {
|
||||
return idpMetadataFile;
|
||||
}
|
||||
|
||||
public void setIdpMetadataFile(String idpMetadataFile) {
|
||||
this.idpMetadataFile = idpMetadataFile;
|
||||
}
|
||||
|
||||
public String getIdpMetadataUrl() {
|
||||
return idpMetadataUrl;
|
||||
}
|
||||
|
||||
public void setIdpMetadataUrl(String idpMetadataUrl) {
|
||||
this.idpMetadataUrl = idpMetadataUrl;
|
||||
}
|
||||
private String userLookupMode;
|
||||
private String staticUserExtSource;
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
@ -105,10 +56,29 @@ public class SamlProperties implements InitializingBean {
|
|||
if (!f.exists()) {
|
||||
throw new IllegalStateException("File '" + idpMetadataFile + "' does not exist");
|
||||
}
|
||||
}
|
||||
|
||||
public String[] getAcrReservedPrefixes() {
|
||||
return acrReservedPrefixes;
|
||||
if (!StringUtils.hasText(userIdentifierAttribute)) {
|
||||
throw new ConfigurationException("No user identifier attribute has been configured");
|
||||
}
|
||||
|
||||
switch (userLookupMode) {
|
||||
case LOOKUP_STATIC_EXT_SOURCE: {
|
||||
if (!StringUtils.hasText(staticUserExtSource)) {
|
||||
throw new ConfigurationException(
|
||||
"No static ext source has been configured, while static ext source lookup has been set");
|
||||
}
|
||||
} break;
|
||||
case LOOKUP_ORIGINAL_AUTH:
|
||||
case LOOKUP_PERUN_USER_ID: {
|
||||
// nothing that needs to be checked
|
||||
} break;
|
||||
default: {
|
||||
throw new ConfigurationException(
|
||||
"Invalid configuration - unknown user lookup method, check your config. Allowed values are: "
|
||||
+ LOOKUP_ORIGINAL_AUTH + ", " + LOOKUP_PERUN_USER_ID + ", " + LOOKUP_STATIC_EXT_SOURCE
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setAcrReservedPrefixes(String[] acrReservedPrefixes) {
|
||||
|
@ -126,19 +96,4 @@ public class SamlProperties implements InitializingBean {
|
|||
}
|
||||
}
|
||||
|
||||
public String[] getAcrsToBeAdded() {
|
||||
return acrsToBeAdded;
|
||||
}
|
||||
|
||||
public void setAcrsToBeAdded(String[] acrsToBeAdded) {
|
||||
this.acrsToBeAdded = acrsToBeAdded;
|
||||
}
|
||||
|
||||
public String getUserIdentifierAttribute() {
|
||||
return userIdentifierAttribute;
|
||||
}
|
||||
|
||||
public void setUserIdentifierAttribute(String userIdentifierAttribute) {
|
||||
this.userIdentifierAttribute = userIdentifierAttribute;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -338,4 +338,6 @@ public interface PerunAdapterMethods {
|
|||
|
||||
boolean isUserInVo(Long userId, String voShortName);
|
||||
|
||||
PerunUser getPerunUser(Long userId);
|
||||
|
||||
}
|
||||
|
|
|
@ -400,4 +400,17 @@ public class PerunAdapterImpl extends PerunAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerunUser getPerunUser(Long userId) {
|
||||
try {
|
||||
return this.getAdapterPrimary().getPerunUser(userId);
|
||||
} catch (UnsupportedOperationException e) {
|
||||
if (this.isCallFallback()) {
|
||||
return this.getAdapterFallback().getPerunUser(userId);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -100,20 +100,7 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
|
|||
FilterBuilder filter = and(
|
||||
equal(OBJECT_CLASS, PERUN_USER), equal(EDU_PERSON_PRINCIPAL_NAMES, extLogin)
|
||||
);
|
||||
SearchScope scope = SearchScope.ONELEVEL;
|
||||
String[] attributes = new String[]{PERUN_USER_ID, GIVEN_NAME, SN};
|
||||
EntryMapper<PerunUser> mapper = e -> {
|
||||
if (!checkHasAttributes(e, new String[] { PERUN_USER_ID, SN })) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long id = Long.parseLong(e.get(PERUN_USER_ID).getString());
|
||||
String firstName = (e.get(GIVEN_NAME) != null) ? e.get(GIVEN_NAME).getString() : null;
|
||||
String lastName = e.get(SN).getString();
|
||||
return new PerunUser(id, firstName, lastName);
|
||||
};
|
||||
|
||||
return connectorLdap.searchFirst(OU_PEOPLE, filter, scope, attributes, mapper);
|
||||
return getPerunUser(filter);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -535,6 +522,31 @@ public class PerunAdapterLdap extends PerunAdapterWithMappingServices implements
|
|||
return vo != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerunUser getPerunUser(Long userId) {
|
||||
FilterBuilder filter = and(
|
||||
equal(OBJECT_CLASS, PERUN_USER), equal(PERUN_USER_ID, userId.toString())
|
||||
);
|
||||
return getPerunUser(filter);
|
||||
}
|
||||
|
||||
private PerunUser getPerunUser(FilterBuilder filter) {
|
||||
SearchScope scope = SearchScope.ONELEVEL;
|
||||
String[] attributes = new String[]{PERUN_USER_ID, GIVEN_NAME, SN};
|
||||
EntryMapper<PerunUser> mapper = e -> {
|
||||
if (!checkHasAttributes(e, new String[] { PERUN_USER_ID, SN })) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long id = Long.parseLong(e.get(PERUN_USER_ID).getString());
|
||||
String firstName = (e.get(GIVEN_NAME) != null) ? e.get(GIVEN_NAME).getString() : null;
|
||||
String lastName = e.get(SN).getString();
|
||||
return new PerunUser(id, firstName, lastName);
|
||||
};
|
||||
|
||||
return connectorLdap.searchFirst(OU_PEOPLE, filter, scope, attributes, mapper);
|
||||
}
|
||||
|
||||
private List<Group> getGroups(Collection<?> objects, String objectAttribute) {
|
||||
List<Group> result;
|
||||
if (objects == null || objects.size() <= 0) {
|
||||
|
|
|
@ -927,6 +927,20 @@ public class PerunAdapterRpc extends PerunAdapterWithMappingServices implements
|
|||
return hasApplicationForm(vo.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public PerunUser getPerunUser(Long userId) {
|
||||
if (!this.connectorRpc.isEnabled()) {
|
||||
return null;
|
||||
} else if (userId == null) {
|
||||
throw new IllegalArgumentException("No userId");
|
||||
}
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("id", userId);
|
||||
|
||||
JsonNode response = connectorRpc.post(USERS_MANAGER, "getUserById", map);
|
||||
return RpcMapper.mapPerunUser(response);
|
||||
}
|
||||
|
||||
private Member getMemberByUser(Long userId, Long voId) {
|
||||
if (!this.connectorRpc.isEnabled()) {
|
||||
return null;
|
||||
|
|
|
@ -93,8 +93,7 @@ public class AuthProcFiltersContainer extends GenericFilterBean {
|
|||
AuthProcFiltersContainer.class.getSimpleName(), client.getClientId(), e);
|
||||
}
|
||||
}
|
||||
PerunUser user = FiltersUtils.getPerunUser(req, perunAdapter,
|
||||
samlProperties.getUserIdentifierAttribute());
|
||||
PerunUser user = FiltersUtils.getPerunUser(req, perunAdapter, samlProperties);
|
||||
FilterParams params = new FilterParams(client, facility, user);
|
||||
for (AuthProcFilter filter : filters) {
|
||||
if (!filter.doFilter(req, res, params)) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import cz.muni.ics.oauth2.service.ClientDetailsEntityService;
|
|||
import cz.muni.ics.oidc.models.Facility;
|
||||
import cz.muni.ics.oidc.models.PerunAttributeValue;
|
||||
import cz.muni.ics.oidc.models.PerunUser;
|
||||
import cz.muni.ics.oidc.saml.SamlProperties;
|
||||
import cz.muni.ics.oidc.server.adapters.PerunAdapter;
|
||||
import cz.muni.ics.oidc.server.configurations.FacilityAttrsConfig;
|
||||
import cz.muni.ics.oidc.web.controllers.ControllerUtils;
|
||||
|
@ -97,22 +98,42 @@ public class FiltersUtils {
|
|||
|
||||
public static PerunUser getPerunUser(HttpServletRequest request,
|
||||
PerunAdapter perunAdapter,
|
||||
String samlIdAttribute)
|
||||
{
|
||||
return getPerunUser(getSamlCredential(request), perunAdapter, samlIdAttribute);
|
||||
SamlProperties samlProperties) {
|
||||
return getPerunUser(getSamlCredential(request), perunAdapter, samlProperties);
|
||||
}
|
||||
|
||||
public static PerunUser getPerunUser(SAMLCredential samlCredential,
|
||||
PerunAdapter perunAdapter,
|
||||
String samlIdAttribute) {
|
||||
SamlProperties samlProperties) {
|
||||
if (perunAdapter == null) {
|
||||
throw new IllegalArgumentException("Cannot fetch user, no adapter passed");
|
||||
}
|
||||
if (samlCredential == null) {
|
||||
return null;
|
||||
}
|
||||
String extLogin = getExtLogin(samlCredential, samlIdAttribute);
|
||||
String extSourceName = getExtSourceName(samlCredential);
|
||||
switch (samlProperties.getUserLookupMode()) {
|
||||
case SamlProperties.LOOKUP_ORIGINAL_AUTH:
|
||||
case SamlProperties.LOOKUP_STATIC_EXT_SOURCE: {
|
||||
return getPerunUserByExtSourceAndExtLogin(perunAdapter, samlCredential, samlProperties);
|
||||
}
|
||||
case SamlProperties.LOOKUP_PERUN_USER_ID: {
|
||||
return getPerunUserById(perunAdapter, samlCredential, samlProperties);
|
||||
}
|
||||
default: {
|
||||
log.debug("Could not find user, invalid user lookup configured");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static PerunUser getPerunUserByExtSourceAndExtLogin(PerunAdapter perunAdapter, SAMLCredential samlCredential, SamlProperties samlProperties) {
|
||||
String extSourceName;
|
||||
if (SamlProperties.LOOKUP_STATIC_EXT_SOURCE.equalsIgnoreCase(samlProperties.getUserLookupMode())) {
|
||||
extSourceName = samlProperties.getStaticUserExtSource();
|
||||
} else {
|
||||
extSourceName = getExtSourceName(samlCredential);
|
||||
}
|
||||
String extLogin = getExtLogin(samlCredential, samlProperties.getUserIdentifierAttribute());
|
||||
if (!StringUtils.hasText(extLogin)) {
|
||||
return null;
|
||||
} else if (!StringUtils.hasText(extSourceName)) {
|
||||
|
@ -121,6 +142,23 @@ public class FiltersUtils {
|
|||
return perunAdapter.getPreauthenticatedUserId(extLogin, extSourceName);
|
||||
}
|
||||
|
||||
public static PerunUser getPerunUserById(PerunAdapter perunAdapter, SAMLCredential samlCredential, SamlProperties samlProperties) {
|
||||
String userIdString = getExtLogin(samlCredential, samlProperties.getUserIdentifierAttribute());
|
||||
if (!StringUtils.hasText(userIdString)) {
|
||||
return null;
|
||||
}
|
||||
Long userId = null;
|
||||
try {
|
||||
userId = Long.parseLong(userIdString);
|
||||
} catch (NumberFormatException e) {
|
||||
log.debug("UserID '{}' cannot be parsed as long", userId);
|
||||
}
|
||||
if (userId == null) {
|
||||
return null;
|
||||
}
|
||||
return perunAdapter.getPerunUser(userId);
|
||||
}
|
||||
|
||||
public static SAMLCredential getSamlCredential(HttpServletRequest request) {
|
||||
ExpiringUsernameAuthenticationToken p = (ExpiringUsernameAuthenticationToken) request.getUserPrincipal();
|
||||
if (p == null) {
|
||||
|
|
|
@ -38,6 +38,7 @@ public class PerunFilterConstants {
|
|||
public static final String SAML_EPTID = "urn:oid:1.3.6.1.4.1.5923.1.1.1.10";
|
||||
public static final String SAML_UID = "urn:oid:0.9.2342.19200300.100.1.1";
|
||||
public static final String SAML_UNIQUE_IDENTIFIER = "urn:oid:0.9.2342.19200300.100.1.44";
|
||||
public static final String SAML_PERUN_USERID_IDENTIFIER = "urn:cesnet:proxyidp:attribute:perunUserId";
|
||||
|
||||
public static final String REFEDS_MFA = "https://refeds.org/profile/mfa";
|
||||
public static final String PROMPT_LOGIN = "login";
|
||||
|
@ -50,6 +51,7 @@ public class PerunFilterConstants {
|
|||
SAML_IDS.put("eptid", SAML_EPTID);
|
||||
SAML_IDS.put("uid", SAML_UID);
|
||||
SAML_IDS.put("uniqueIdentifier", SAML_UNIQUE_IDENTIFIER);
|
||||
SAML_IDS.put("perunUserId", SAML_PERUN_USERID_IDENTIFIER);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ public class PerunForceAupFilter extends AuthProcFilter {
|
|||
return true;
|
||||
}
|
||||
|
||||
PerunUser user = FiltersUtils.getPerunUser(req, perunAdapter, samlProperties.getUserIdentifierAttribute());
|
||||
PerunUser user = FiltersUtils.getPerunUser(req, perunAdapter, samlProperties);
|
||||
if (user == null || user.getId() == null) {
|
||||
log.debug("{} - skip filter execution: no user provider", filterName);
|
||||
return true;
|
||||
|
|
Loading…
Reference in New Issue