Merge pull request #3220 from abstractj/KEYCLOAK-3535

KEYCLOAK-3535 - Check if SSSD is available via DBUS
This commit is contained in:
Stian Thorgersen 2016-09-09 08:15:11 +02:00 committed by GitHub
commit 1630b9a20c
8 changed files with 108 additions and 26 deletions

View file

@ -31,7 +31,7 @@ public class LibraryLoader {
private static final String VERSION = "0.0.8"; private static final String VERSION = "0.0.8";
private static boolean loadSucceeded; private static boolean loadSucceeded;
public static void load() { public static LibraryLoader load() {
for (String path : PATHS) { for (String path : PATHS) {
try { try {
System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION)); System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION));
@ -45,5 +45,11 @@ public class LibraryLoader {
if (!loadSucceeded) LOGGER.log(Level.WARNING, "libunix_dbus_java not found\n" + if (!loadSucceeded) LOGGER.log(Level.WARNING, "libunix_dbus_java not found\n" +
"Please, make sure you have the package libunix-dbus-java installed."); "Please, make sure you have the package libunix-dbus-java installed.");
return new LibraryLoader();
}
public boolean succeed() {
return loadSucceeded;
} }
} }

View file

@ -30,18 +30,22 @@ import java.util.List;
import java.util.Map; import java.util.Map;
public interface DBus extends DBusInterface { public interface DBus extends DBusInterface {
public static final int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01;
public static final int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02; String BUSNAME = "org.freedesktop.DBus";
public static final int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04; String OBJECTPATH = "/org/freedesktop/DBus";
public static final int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1;
public static final int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2; int DBUS_NAME_FLAG_ALLOW_REPLACEMENT = 0x01;
public static final int DBUS_REQUEST_NAME_REPLY_EXISTS = 3; int DBUS_NAME_FLAG_REPLACE_EXISTING = 0x02;
public static final int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4; int DBUS_NAME_FLAG_DO_NOT_QUEUE = 0x04;
public static final int DBUS_RELEASE_NAME_REPLY_RELEASED = 1; int DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER = 1;
public static final int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2; int DBUS_REQUEST_NAME_REPLY_IN_QUEUE = 2;
public static final int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3; int DBUS_REQUEST_NAME_REPLY_EXISTS = 3;
public static final int DBUS_START_REPLY_SUCCESS = 1; int DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER = 4;
public static final int DBUS_START_REPLY_ALREADY_RUNNING = 2; int DBUS_RELEASEME_REPLY_RELEASED = 1;
int DBUS_RELEASE_NAME_REPLY_NON_EXISTANT = 2;
int DBUS_RELEASE_NAME_REPLY_NOT_OWNER = 3;
int DBUS_START_REPLY_SUCCESS = 1;
int DBUS_START_REPLY_ALREADY_RUNNING = 2;
/** /**
* All DBus Applications should respond to the Ping method on this interface * All DBus Applications should respond to the Ping method on this interface

View file

@ -32,6 +32,8 @@ import java.util.Map;
public interface InfoPipe extends DBusInterface { public interface InfoPipe extends DBusInterface {
String OBJECTPATH = "/org/freedesktop/sssd/infopipe"; String OBJECTPATH = "/org/freedesktop/sssd/infopipe";
String BUSNAME = "org.freedesktop.sssd.infopipe";
@DBusMemberName("GetUserAttr") @DBusMemberName("GetUserAttr")
Map<String, Variant> getUserAttributes(String user, List<String> attr); Map<String, Variant> getUserAttributes(String user, List<String> attr);

View file

@ -19,6 +19,7 @@ package org.keycloak.federation.sssd;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.federation.sssd.api.Sssd;
import org.keycloak.federation.sssd.impl.PAMAuthenticator; import org.keycloak.federation.sssd.impl.PAMAuthenticator;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
@ -26,6 +27,7 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory; import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserFederationSyncResult; import org.keycloak.models.UserFederationSyncResult;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import java.util.Date; import java.util.Date;
import java.util.HashSet; import java.util.HashSet;
@ -35,7 +37,7 @@ import java.util.Set;
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a> * @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class SSSDFederationProviderFactory implements UserFederationProviderFactory { public class SSSDFederationProviderFactory implements UserFederationProviderFactory, EnvironmentDependentProviderFactory {
private static final String PROVIDER_NAME = "sssd"; private static final String PROVIDER_NAME = "sssd";
private static final Logger logger = Logger.getLogger(SSSDFederationProvider.class); private static final Logger logger = Logger.getLogger(SSSDFederationProvider.class);
@ -99,4 +101,8 @@ public class SSSDFederationProviderFactory implements UserFederationProviderFact
return new PAMAuthenticator(username, factors); return new PAMAuthenticator(username, factors);
} }
@Override
public boolean isSupported() {
return Sssd.isAvailable();
}
} }

View file

@ -17,6 +17,8 @@
package org.keycloak.federation.sssd.api; package org.keycloak.federation.sssd.api;
import cx.ath.matthew.LibraryLoader;
import org.freedesktop.DBus;
import org.freedesktop.dbus.DBusConnection; import org.freedesktop.dbus.DBusConnection;
import org.freedesktop.dbus.Variant; import org.freedesktop.dbus.Variant;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
@ -35,8 +37,6 @@ import java.util.Vector;
*/ */
public class Sssd { public class Sssd {
public static final String BUSNAME = "org.freedesktop.sssd.infopipe";
public static User user() { public static User user() {
return SingletonHolder.USER_OBJECT; return SingletonHolder.USER_OBJECT;
} }
@ -45,6 +45,7 @@ public class Sssd {
return SingletonHolder.INFOPIPE_OBJECT; return SingletonHolder.INFOPIPE_OBJECT;
} }
public static void disconnect() { public static void disconnect() {
SingletonHolder.DBUS_CONNECTION.disconnect(); SingletonHolder.DBUS_CONNECTION.disconnect();
} }
@ -67,10 +68,10 @@ public class Sssd {
static { static {
try { try {
DBUS_CONNECTION = DBusConnection.getConnection(DBusConnection.SYSTEM); DBUS_CONNECTION = DBusConnection.getConnection(DBusConnection.SYSTEM);
INFOPIPE_OBJECT = DBUS_CONNECTION.getRemoteObject(BUSNAME, InfoPipe.OBJECTPATH, InfoPipe.class); INFOPIPE_OBJECT = DBUS_CONNECTION.getRemoteObject(InfoPipe.BUSNAME, InfoPipe.OBJECTPATH, InfoPipe.class);
USER_OBJECT = DBUS_CONNECTION.getRemoteObject(BUSNAME, User.OBJECTPATH, User.class); USER_OBJECT = DBUS_CONNECTION.getRemoteObject(InfoPipe.BUSNAME, User.OBJECTPATH, User.class);
} catch (DBusException e) { } catch (DBusException e) {
e.printStackTrace(); logger.error("Failed to obtain D-Bus connection", e);
} }
} }
} }
@ -108,4 +109,24 @@ public class Sssd {
} }
return userGroups; return userGroups;
} }
public static boolean isAvailable(){
boolean sssdAvailable = false;
try {
if (LibraryLoader.load().succeed()) {
DBusConnection connection = DBusConnection.getConnection(DBusConnection.SYSTEM);
DBus dbus = connection.getRemoteObject(DBus.BUSNAME, DBus.OBJECTPATH, DBus.class);
sssdAvailable = Arrays.asList(dbus.ListNames()).contains(InfoPipe.BUSNAME);
if (!sssdAvailable) {
logger.debugv("SSSD is not available in your system. Federation provider will be disabled.");
} else {
sssdAvailable = true;
}
connection.disconnect();
}
} catch (DBusException e) {
logger.error("Failed to check the status of SSSD", e);
}
return sssdAvailable;
}
} }

View file

@ -0,0 +1,33 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.provider;
/**
* Providers that are only supported in some environments can implement this interface to be able to determine if they
* should be available or not.
*
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface EnvironmentDependentProviderFactory {
/**
* @return <code>true</code> if the provider is supported and should be available, <code>false</code> otherwise
*/
boolean isSupported();
}

View file

@ -20,6 +20,7 @@ import org.keycloak.Config;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.EnvironmentDependentProviderFactory;
import org.keycloak.provider.Provider; import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener; import org.keycloak.provider.ProviderEventListener;
@ -190,8 +191,7 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
} }
Config.Scope scope = Config.scope(spi.getName(), provider); Config.Scope scope = Config.scope(spi.getName(), provider);
if (scope.getBoolean("enabled", true)) { if (isEnabled(factory, scope)) {
factory.init(scope); factory.init(scope);
if (spi.isInternal() && !isInternal(factory)) { if (spi.isInternal() && !isInternal(factory)) {
@ -202,10 +202,11 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
logger.debugv("Loaded SPI {0} (provider = {1})", spi.getName(), provider); logger.debugv("Loaded SPI {0} (provider = {1})", spi.getName(), provider);
} }
} else { } else {
for (ProviderFactory factory : pm.load(spi)) { for (ProviderFactory factory : pm.load(spi)) {
Config.Scope scope = Config.scope(spi.getName(), factory.getId()); Config.Scope scope = Config.scope(spi.getName(), factory.getId());
if (scope.getBoolean("enabled", true)) { if (isEnabled(factory, scope)) {
factory.init(scope); factory.init(scope);
if (spi.isInternal() && !isInternal(factory)) { if (spi.isInternal() && !isInternal(factory)) {
@ -220,7 +221,16 @@ public class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, Pr
} }
} }
return factoryMap; return factoryMap;
}
private boolean isEnabled(ProviderFactory factory, Config.Scope scope) {
if (!scope.getBoolean("enabled", true)) {
return false;
}
if (factory instanceof EnvironmentDependentProviderFactory) {
return ((EnvironmentDependentProviderFactory) factory).isSupported();
}
return true;
} }
protected void loadSPIs(ProviderManager pm, List<Spi> spiList) { protected void loadSPIs(ProviderManager pm, List<Spi> spiList) {

View file

@ -51,7 +51,7 @@ public class UserFederationTest extends AbstractAdminTest {
@Test @Test
public void testProviderFactories() { public void testProviderFactories() {
List<UserFederationProviderFactoryRepresentation> providerFactories = userFederation().getProviderFactories(); List<UserFederationProviderFactoryRepresentation> providerFactories = userFederation().getProviderFactories();
Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable", "sssd"); Assert.assertNames(providerFactories, "ldap", "kerberos", "dummy", "dummy-configurable");
// Builtin provider without properties // Builtin provider without properties
UserFederationProviderFactoryRepresentation ldapProvider = userFederation().getProviderFactory("ldap"); UserFederationProviderFactoryRepresentation ldapProvider = userFederation().getProviderFactory("ldap");