package utils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Properties; /** * Maps the attributes released by the SAML identity provider to a Security Shepherd user, using the * attribute names and affiliations configured in sso.properties.
* This file is part of the Security Shepherd Project. * *

The Security Shepherd project is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version.
* *

The Security Shepherd project is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details.
* *

You should have received a copy of the GNU General Public License along with the Security * Shepherd project. If not, see . */ public class SsoUserMapper { /** Result of mapping: either a user or an error message for the login page */ public static class Result { public final String ssoName; public final String userName; public final String userRole; public final String error; private Result(String ssoName, String userName, String userRole, String error) { this.ssoName = ssoName; this.userName = userName; this.userRole = userRole; this.error = error; } public boolean isValid() { return error == null; } } /** * Maps IdP attributes to a user. * * @param attributes Attributes from the SAML assertion. Values may be single objects or * collections. * @param ssoProperties sso.properties contents * @return The mapped user, or an error if a required attribute is missing or the user has none of * the configured affiliations */ public static Result map(Map attributes, Properties ssoProperties) { if (attributes == null || attributes.isEmpty()) { return error("You don't have any attributes"); } String ssoName = first(attributes, ssoProperties.getProperty("sso.saml.ssoName")); String userName = first(attributes, ssoProperties.getProperty("sso.saml.userName")); if (ssoName == null || userName == null) { return error("Required SSO attributes are missing"); } List affiliations = values(attributes, ssoProperties.getProperty("sso.saml.affiliation")); List adminAffiliations = splitList(ssoProperties.getProperty("sso.saml.adminAffiliation")); List playerAffiliations = splitList(ssoProperties.getProperty("sso.saml.playerAffiliation")); if (!Collections.disjoint(affiliations, adminAffiliations)) { return new Result(ssoName, userName, "admin", null); } if (!Collections.disjoint(affiliations, playerAffiliations)) { return new Result(ssoName, userName, "player", null); } StringBuilder message = new StringBuilder( "Authorization failed. Please ensure that you are a member of one of the" + " following groups: "); for (String affiliation : adminAffiliations) { message.append(affiliation).append(", "); } for (String affiliation : playerAffiliations) { message.append(affiliation).append(", "); } return error(message.toString()); } private static Result error(String message) { return new Result(null, null, null, message); } private static String first(Map attributes, String key) { List values = values(attributes, key); return values.isEmpty() ? null : values.get(0); } private static List values(Map attributes, String key) { List values = new ArrayList<>(); Object value = key == null ? null : attributes.get(key); if (value instanceof Collection) { for (Object item : (Collection) value) { addIfNotBlank(values, item); } } else { addIfNotBlank(values, value); } return values; } /** * Blank values count as missing: a blank ssoName would let different principals share one * account, since accounts are looked up by it. */ private static void addIfNotBlank(List values, Object value) { if (value != null && !value.toString().trim().isEmpty()) { values.add(value.toString()); } } private static List splitList(String value) { if (value == null || value.trim().isEmpty()) { return Collections.emptyList(); } return Arrays.asList(value.trim().split("\\s*,\\s*")); } }