package org.ovirt.engine.core.sso.utils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.ovirt.engine.api.extensions.Base;
import org.ovirt.engine.api.extensions.ExtMap;
import org.ovirt.engine.api.extensions.aaa.Authn;
import org.ovirt.engine.api.extensions.aaa.Authz;
import org.ovirt.engine.api.extensions.aaa.Mapping;
import org.ovirt.engine.core.extensions.mgr.ExtensionProxy;
import org.ovirt.engine.core.sso.search.AuthzUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AuthenticationUtils {
private static Logger log = LoggerFactory.getLogger(AuthenticationUtils.class);
public static void loginOnBehalf(SsoContext ssoContext, HttpServletRequest request, String username)
throws Exception {
log.debug("Entered AuthenticationUtils.loginOnBehalf");
int index = username.lastIndexOf("@");
String profile = null;
if (index != -1) {
profile = username.substring(index + 1);
username = username.substring(0, index);
}
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(profile)) {
throw new AuthenticationException(
ssoContext.getLocalizationUtils().localize(
SsoConstants.APP_ERROR_PROVIDE_USERNAME_AND_PROFILE,
(Locale) request.getAttribute(SsoConstants.LOCALE)));
}
ObjectMapper mapper = new ObjectMapper()
.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.enableDefaultTyping(ObjectMapper.DefaultTyping.OBJECT_AND_NON_CONCRETE);
mapper.getDeserializationConfig().addMixInAnnotations(ExtMap.class, JsonExtMapMixIn.class);
String authRecordJson = SsoUtils.getRequestParameter(request, SsoConstants.HTTP_PARAM_AUTH_RECORD, "");
ExtMap authRecord;
if (StringUtils.isNotEmpty(authRecordJson)) {
authRecord = mapper.readValue(authRecordJson, ExtMap.class);
} else {
authRecord = new ExtMap().mput(Authn.AuthRecord.PRINCIPAL, username);
}
SsoSession ssoSession = login(ssoContext,
request,
new Credentials(username,
null,
profile,
SsoUtils.getSsoContext(request).getSsoProfiles().contains(profile)),
authRecord,
false);
log.info("User {}@{} successfully logged in using login-on-behalf with client id : {} and scopes : {}",
username,
profile,
ssoSession.getClientId(),
ssoSession.getScope());
}
public static void handleCredentials(
SsoContext ssoContext,
HttpServletRequest request,
Credentials credentials) throws Exception {
handleCredentials(ssoContext, request, credentials, true);
}
public static void handleCredentials(
SsoContext ssoContext,
HttpServletRequest request,
Credentials credentials,
boolean interactive) throws Exception {
log.debug("Entered AuthenticationUtils.handleCredentials");
if (StringUtils.isEmpty(credentials.getUsername()) || StringUtils.isEmpty(credentials.getProfile())) {
throw new AuthenticationException(
ssoContext.getLocalizationUtils().localize(
SsoConstants.APP_ERROR_PROVIDE_USERNAME_PASSWORD_AND_PROFILE,
(Locale) request.getAttribute(SsoConstants.LOCALE)));
}
SsoSession ssoSession = login(ssoContext, request, credentials, null, interactive);
log.info("User {}@{} successfully logged in with scopes: {}",
credentials.getUsername(),
credentials.getProfile(),
ssoSession.getScope());
}
private static SsoSession login(
SsoContext ssoContext,
HttpServletRequest request,
Credentials credentials,
ExtMap authRecord,
boolean interactive) throws Exception {
ExtensionProfile profile = getExtensionProfile(ssoContext, credentials.getProfile());
String user = mapUser(profile, credentials);
if (authRecord == null) {
log.debug("AuthenticationUtils.handleCredentials invoking AUTHENTICATE_CREDENTIALS on authn");
ExtMap outputMap = profile.authn.invoke(new ExtMap().mput(
Base.InvokeKeys.COMMAND,
Authn.InvokeCommands.AUTHENTICATE_CREDENTIALS
).mput(
Authn.InvokeKeys.USER,
user
).mput(
Authn.InvokeKeys.CREDENTIALS,
credentials.getPassword()
)
);
if (outputMap.<Integer>get(Base.InvokeKeys.RESULT) != Base.InvokeResult.SUCCESS ||
outputMap.<Integer>get(Authn.InvokeKeys.RESULT) != Authn.AuthResult.SUCCESS) {
if (interactive) {
SsoUtils.getSsoSession(request).setChangePasswdCredentials(credentials);
}
log.debug("AuthenticationUtils.handleCredentials AUTHENTICATE_CREDENTIALS on authn failed");
throw new AuthenticationException(
AuthnMessageMapper.mapMessageErrorCode(
ssoContext,
request,
credentials.getProfile(),
outputMap));
}
log.debug("AuthenticationUtils.handleCredentials AUTHENTICATE_CREDENTIALS on authn succeeded");
authRecord = outputMap.get(Authn.InvokeKeys.AUTH_RECORD);
}
if (profile.mapper != null) {
log.debug("AuthenticationUtils.handleCredentials invoking MAP_AUTH_RECORD on mapper");
authRecord = profile.mapper.invoke(new ExtMap()
.mput(
Base.InvokeKeys.COMMAND,
Mapping.InvokeCommands.MAP_AUTH_RECORD
).mput(
Authn.InvokeKeys.AUTH_RECORD,
authRecord
),
true
).get(
Authn.InvokeKeys.AUTH_RECORD,
authRecord
);
}
log.debug("AuthenticationUtils.handleCredentials invoking FETCH_PRINCIPAL_RECORD on authz");
ExtMap output = profile.authz.invoke(new ExtMap().mput(
Base.InvokeKeys.COMMAND,
Authz.InvokeCommands.FETCH_PRINCIPAL_RECORD
).mput(
Authn.InvokeKeys.AUTH_RECORD,
authRecord
).mput(
Authz.InvokeKeys.QUERY_FLAGS,
Authz.QueryFlags.RESOLVE_GROUPS | Authz.QueryFlags.RESOLVE_GROUPS_RECURSIVE
));
log.debug("AuthenticationUtils.handleCredentials saving data in session data");
return SsoUtils.persistAuthInfoInContextWithToken(request,
credentials.getPassword(),
credentials.getProfile(),
authRecord,
output.get(Authz.InvokeKeys.PRINCIPAL_RECORD));
}
public static void changePassword(SsoContext context, HttpServletRequest request, Credentials credentials)
throws AuthenticationException {
ExtensionProfile profile = getExtensionProfile(context, credentials.getProfile());
String user = mapUser(profile, credentials);
log.debug("AuthenticationUtils.changePassword invoking CREDENTIALS_CHANGE on authn");
ExtMap outputMap = profile.authn.invoke(new ExtMap()
.mput(
Base.InvokeKeys.COMMAND,
Authn.InvokeCommands.CREDENTIALS_CHANGE
).mput(
Authn.InvokeKeys.USER,
user
).mput(
Authn.InvokeKeys.CREDENTIALS,
credentials.getCredentials()
).mput(
Authn.InvokeKeys.CREDENTIALS_NEW,
credentials.getNewCredentials()
)
);
if (outputMap.<Integer>get(Base.InvokeKeys.RESULT) != Base.InvokeResult.SUCCESS ||
outputMap.<Integer>get(Authn.InvokeKeys.RESULT) != Authn.AuthResult.SUCCESS) {
SsoUtils.getSsoSession(request).setChangePasswdCredentials(credentials);
log.debug("AuthenticationUtils.changePassword CREDENTIALS_CHANGE on authn failed");
throw new AuthenticationException(
AuthnMessageMapper.mapMessageErrorCode(
context,
request,
credentials.getProfile(),
outputMap));
}
log.debug("AuthenticationUtils.changePassword CREDENTIALS_CHANGE on authn succeeded");
}
public static Map<String, List<String>> getAvailableNamesSpaces(SsoExtensionsManager extensionsManager) {
Map<String, List<String>> namespacesMap = new HashMap<>();
extensionsManager.getExtensionsByService(Authz.class.getName())
.forEach(authz -> {
String authzName = authz.getContext().get(Base.ContextKeys.INSTANCE_NAME);
authz.getContext().<Collection<String>>get(Authz.ContextKeys.AVAILABLE_NAMESPACES,
Collections.<String>emptyList())
.forEach(namespace -> {
if (!namespacesMap.containsKey(authzName)) {
namespacesMap.put(authzName, new ArrayList<>());
}
namespacesMap.get(authzName).add(namespace);
});
});
namespacesMap.values().forEach(Collections::sort);
return namespacesMap;
}
public static List<Map<String, Object>> getProfileList(SsoExtensionsManager extensionsManager) {
return extensionsManager.getExtensionsByService(Authn.class.getName()).stream()
.map(authn -> getProfileEntry(extensionsManager, authn)).collect(Collectors.toList());
}
public static String getDefaultProfile(SsoExtensionsManager extensionsManager) {
Optional<ExtensionProxy> defaultExtension = extensionsManager.getExtensionsByService(Authn.class.getName())
.stream()
.filter(a -> Boolean.valueOf(a.getContext().<Properties>get(Base.ContextKeys.CONFIGURATION)
.getProperty(Authn.ConfigKeys.DEFAULT_PROFILE)))
.findFirst();
return defaultExtension.isPresent() ? getProfileName(defaultExtension.get()) : null;
}
public static List<String> getAvailableProfiles(SsoExtensionsManager extensionsManager) {
return extensionsManager.getExtensionsByService(Authn.class.getName()).stream()
.map(AuthenticationUtils::getProfileName)
.collect(Collectors.toList());
}
public static List<String> getAvailableProfilesSupportingPasswd(SsoExtensionsManager extensionsManager) {
return getAvailableProfilesImpl(extensionsManager, Authn.Capabilities.AUTHENTICATE_PASSWORD);
}
public static List<String> getAvailableProfilesSupportingPasswdChange(SsoExtensionsManager extensionsManager) {
return getAvailableProfilesImpl(extensionsManager, Authn.Capabilities.CREDENTIALS_CHANGE);
}
public static ExtensionProfile getExtensionProfile(SsoContext ssoContext, String profileName) {
Optional<ExtensionProfile> profile = getExtensionProfileImpl(ssoContext, profileName, null);
if (!profile.isPresent()) {
log.debug("AuthenticationUtils.getExtensionProfile authn and authz NOT found for profile {}", profileName);
throw new RuntimeException(String.format("Error in obtaining profile %s", profileName));
}
log.debug("AuthenticationUtils.getExtensionProfile authn and authz found for profile %s", profileName);
return profile.get();
}
public static ExtensionProfile getExtensionProfileByAuthzName(SsoContext ssoContext, String authzName) {
Optional<ExtensionProfile> profile = getExtensionProfileImpl(ssoContext, null, authzName);
if (!profile.isPresent()) {
log.debug("AuthenticationUtils.getExtensionProfile authn and authz NOT found for authz {}", authzName);
throw new RuntimeException(String.format("Error in obtaining profile for authz %s", authzName));
}
log.debug("AuthenticationUtils.getExtensionProfile authn and authz found for authz %s", authzName);
return profile.get();
}
private static List<String> getAvailableProfilesImpl(SsoExtensionsManager extensionsManager,
long capability) {
return extensionsManager.getExtensionsByService(Authn.class.getName()).stream()
.filter(a -> (a.getContext().<Long>get(Authn.ContextKeys.CAPABILITIES, 0L)
& capability) != 0)
.map(AuthenticationUtils::getProfileName)
.sorted()
.collect(Collectors.toList());
}
private static String getProfileName(ExtensionProxy proxy) {
return proxy.getContext().<Properties>get(Base.ContextKeys.CONFIGURATION)
.getProperty(Authn.ConfigKeys.PROFILE_NAME);
}
private static Map<String, Object> getProfileEntry(SsoExtensionsManager extensionsManager, ExtensionProxy authn) {
Map<String, Object> profileEntry = new HashMap<>();
profileEntry.put("authn_name", getProfileName(authn));
ExtensionProxy authz = extensionsManager.getExtensionByName(getAuthzName(authn));
profileEntry.put("authz_name", AuthzUtils.getName(authz));
profileEntry.put("capability_password_auth", AuthzUtils.supportsPasswordAuthentication(authz));
return profileEntry;
}
private static String getAuthzName(ExtensionProxy proxy) {
return proxy.getContext().<Properties>get(Base.ContextKeys.CONFIGURATION)
.getProperty(Authn.ConfigKeys.AUTHZ_PLUGIN);
}
private static Optional<ExtensionProfile> getExtensionProfileImpl(SsoContext ssoContext,
final String searchProfileName,
final String searchAuthzName) {
return ssoContext.getSsoExtensionsManager().getExtensionsByService(Authn.class.getName()).stream()
.filter(a -> matchesSearchName(a, searchProfileName, searchAuthzName))
.map(a -> mapToExtensionProfile(ssoContext, a))
.findFirst();
}
private static boolean matchesSearchName(ExtensionProxy authn,
String searchProfileName,
String searchAuthzName) {
return (StringUtils.isNotEmpty(searchProfileName) && searchProfileName.equals(getProfileName(authn))) ||
(StringUtils.isNotEmpty(searchAuthzName) && searchAuthzName.equals(getAuthzName(authn)));
}
private static ExtensionProfile mapToExtensionProfile(SsoContext ssoContext, ExtensionProxy authn) {
ExtensionProfile profile = new ExtensionProfile();
String mapperName = authn.getContext().<Properties>get(Base.ContextKeys.CONFIGURATION)
.getProperty(Authn.ConfigKeys.MAPPING_PLUGIN);
profile.mapper = mapperName != null ?
ssoContext.getSsoExtensionsManager().getExtensionByName(mapperName) : null;
profile.authn = authn;
profile.authz = ssoContext.getSsoExtensionsManager().getExtensionByName(getAuthzName(authn));
return profile;
}
private static String mapUser(ExtensionProfile profile, Credentials credentials) {
String user = credentials.getUsername();
if (profile.mapper != null) {
log.debug("AuthenticationUtils.handleCredentials invoking MAP_USER on mapper");
user = profile.mapper.invoke(new ExtMap()
.mput(
Base.InvokeKeys.COMMAND,
Mapping.InvokeCommands.MAP_USER
)
.mput(
Mapping.InvokeKeys.USER,
user
),
true).get(Mapping.InvokeKeys.USER, user);
}
return user;
}
public static class ExtensionProfile {
private ExtensionProxy authn;
private ExtensionProxy authz;
private ExtensionProxy mapper;
public ExtensionProxy getAuthn() {
return authn;
}
public ExtensionProxy getAuthz() {
return authz;
}
public ExtensionProxy getMapper() {
return mapper;
}
}
}