Merge pull request #3506 from abstractj/KEYCLOAK-3913
[KEYCLOAK-3913] - Native libraries included within SSSD jar
This commit is contained in:
commit
cf17687b8b
12 changed files with 529 additions and 116 deletions
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
<resources>
|
<resources>
|
||||||
<artifact name="${org.keycloak:keycloak-sssd-federation}"/>
|
<artifact name="${org.keycloak:keycloak-sssd-federation}"/>
|
||||||
|
<resource-root path="/usr/share/java/jna.jar"/>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.java.dev.jna</groupId>
|
<groupId>net.java.dev.jna</groupId>
|
||||||
<artifactId>jna</artifactId>
|
<artifactId>jna</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
|
@ -70,10 +71,6 @@
|
||||||
<artifactId>jboss-logging</artifactId>
|
<artifactId>jboss-logging</artifactId>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.jnr</groupId>
|
|
||||||
<artifactId>jnr-unixsocket</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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 cx.ath.matthew;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>.
|
||||||
|
*/
|
||||||
|
public class LibraryLoader {
|
||||||
|
|
||||||
|
private static final String[] PATHS = {"/usr/lib/", "/usr/lib64/", "/usr/local/lib/", "/opt/local/lib/"};
|
||||||
|
private static final String LIBRARY_NAME = "libunix_dbus_java";
|
||||||
|
private static final String VERSION = "0.0.8";
|
||||||
|
private static boolean loadSucceeded;
|
||||||
|
|
||||||
|
public static LibraryLoader load() {
|
||||||
|
for (String path : PATHS) {
|
||||||
|
try {
|
||||||
|
System.load(String.format("%s/%s.so.%s", path, LIBRARY_NAME, VERSION));
|
||||||
|
loadSucceeded = true;
|
||||||
|
break;
|
||||||
|
} catch (UnsatisfiedLinkError e) {
|
||||||
|
loadSucceeded = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LibraryLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean succeed() {
|
||||||
|
return loadSucceeded;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,25 +26,25 @@
|
||||||
*/
|
*/
|
||||||
package cx.ath.matthew.unix;
|
package cx.ath.matthew.unix;
|
||||||
|
|
||||||
import jnr.unixsocket.UnixSocketChannel;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.channels.Channels;
|
|
||||||
|
|
||||||
public class USInputStream extends InputStream {
|
public class USInputStream extends InputStream {
|
||||||
public static final int MSG_DONTWAIT = 0x40;
|
public static final int MSG_DONTWAIT = 0x40;
|
||||||
private UnixSocketChannel channel;
|
|
||||||
|
|
||||||
|
private native int native_recv(int sock, byte[] b, int off, int len, int flags, int timeout) throws IOException;
|
||||||
|
|
||||||
|
private int sock;
|
||||||
boolean closed = false;
|
boolean closed = false;
|
||||||
private byte[] onebuf = new byte[1];
|
private byte[] onebuf = new byte[1];
|
||||||
private UnixSocket us;
|
private UnixSocket us;
|
||||||
|
private boolean blocking = true;
|
||||||
private int flags = 0;
|
private int flags = 0;
|
||||||
private int timeout = 0;
|
private int timeout = 0;
|
||||||
|
|
||||||
public USInputStream(UnixSocketChannel channel, UnixSocket us) {
|
public USInputStream(int sock, UnixSocket us) {
|
||||||
|
this.sock = sock;
|
||||||
this.us = us;
|
this.us = us;
|
||||||
this.channel = channel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
@ -65,8 +65,7 @@ public class USInputStream extends InputStream {
|
||||||
|
|
||||||
public int read(byte[] b, int off, int len) throws IOException {
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
if (closed) throw new NotConnectedException();
|
if (closed) throw new NotConnectedException();
|
||||||
int count = receive(b, off, len);
|
int count = native_recv(sock, b, off, len, flags, timeout);
|
||||||
|
|
||||||
/* Yes, I really want to do this. Recv returns 0 for 'connection shut down'.
|
/* Yes, I really want to do this. Recv returns 0 for 'connection shut down'.
|
||||||
* read() returns -1 for 'end of stream.
|
* read() returns -1 for 'end of stream.
|
||||||
* Recv returns -1 for 'EAGAIN' (all other errors cause an exception to be raised)
|
* Recv returns -1 for 'EAGAIN' (all other errors cause an exception to be raised)
|
||||||
|
@ -92,21 +91,4 @@ public class USInputStream extends InputStream {
|
||||||
public void setSoTimeout(int timeout) {
|
public void setSoTimeout(int timeout) {
|
||||||
this.timeout = timeout;
|
this.timeout = timeout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Taken from JRuby with small modifications
|
|
||||||
* @see <a href="https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/ext/socket/RubyUNIXSocket.java">RubyUNIXSocket.java</a>
|
|
||||||
*/
|
|
||||||
private int receive(byte[] dataBytes, int off, int len) {
|
|
||||||
int recvStatus = -1;
|
|
||||||
try {
|
|
||||||
InputStream inputStream = Channels.newInputStream(channel);
|
|
||||||
recvStatus = inputStream.read(dataBytes, off, len);
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return recvStatus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,31 +26,22 @@
|
||||||
*/
|
*/
|
||||||
package cx.ath.matthew.unix;
|
package cx.ath.matthew.unix;
|
||||||
|
|
||||||
import jnr.constants.platform.linux.SocketLevel;
|
|
||||||
import jnr.posix.CmsgHdr;
|
|
||||||
import jnr.posix.MsgHdr;
|
|
||||||
import jnr.posix.POSIX;
|
|
||||||
import jnr.posix.POSIXFactory;
|
|
||||||
import jnr.unixsocket.UnixSocketChannel;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
|
|
||||||
public class USOutputStream extends OutputStream {
|
public class USOutputStream extends OutputStream {
|
||||||
|
private native int native_send(int sock, byte[] b, int off, int len) throws IOException;
|
||||||
|
|
||||||
private UnixSocketChannel channel;
|
private native int native_send(int sock, byte[][] b) throws IOException;
|
||||||
|
|
||||||
private int sock;
|
private int sock;
|
||||||
boolean closed = false;
|
boolean closed = false;
|
||||||
private byte[] onebuf = new byte[1];
|
private byte[] onebuf = new byte[1];
|
||||||
private UnixSocket us;
|
private UnixSocket us;
|
||||||
|
|
||||||
public USOutputStream(UnixSocketChannel channel, int sock, UnixSocket us) {
|
public USOutputStream(int sock, UnixSocket us) {
|
||||||
this.sock = sock;
|
this.sock = sock;
|
||||||
this.us = us;
|
this.us = us;
|
||||||
this.channel = channel;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
|
@ -61,9 +52,14 @@ public class USOutputStream extends OutputStream {
|
||||||
public void flush() {
|
public void flush() {
|
||||||
} // no-op, we do not buffer
|
} // no-op, we do not buffer
|
||||||
|
|
||||||
|
public void write(byte[][] b) throws IOException {
|
||||||
|
if (closed) throw new NotConnectedException();
|
||||||
|
native_send(sock, b);
|
||||||
|
}
|
||||||
|
|
||||||
public void write(byte[] b, int off, int len) throws IOException {
|
public void write(byte[] b, int off, int len) throws IOException {
|
||||||
if (closed) throw new NotConnectedException();
|
if (closed) throw new NotConnectedException();
|
||||||
send(sock, b, off, len);
|
native_send(sock, b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(int b) throws IOException {
|
public void write(int b) throws IOException {
|
||||||
|
@ -79,46 +75,4 @@ public class USOutputStream extends OutputStream {
|
||||||
public UnixSocket getSocket() {
|
public UnixSocket getSocket() {
|
||||||
return us;
|
return us;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Taken from JRuby with small modifications
|
|
||||||
* @see <a href="https://github.com/jruby/jruby/blob/master/core/src/main/java/org/jruby/ext/socket/RubyUNIXSocket.java">RubyUNIXSocket.java</a>
|
|
||||||
*/
|
|
||||||
private void send(int sock, ByteBuffer[] outIov) {
|
|
||||||
|
|
||||||
final POSIX posix = POSIXFactory.getNativePOSIX();
|
|
||||||
MsgHdr outMessage = posix.allocateMsgHdr();
|
|
||||||
|
|
||||||
outMessage.setIov(outIov);
|
|
||||||
|
|
||||||
CmsgHdr outControl = outMessage.allocateControl(4);
|
|
||||||
outControl.setLevel(SocketLevel.SOL_SOCKET.intValue());
|
|
||||||
outControl.setType(0x01);
|
|
||||||
|
|
||||||
ByteBuffer fdBuf = ByteBuffer.allocateDirect(4);
|
|
||||||
fdBuf.order(ByteOrder.nativeOrder());
|
|
||||||
fdBuf.putInt(0, channel.getFD());
|
|
||||||
outControl.setData(fdBuf);
|
|
||||||
|
|
||||||
posix.sendmsg(sock, outMessage, 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void send(int sock, byte[] dataBytes, int off, int len) {
|
|
||||||
ByteBuffer[] outIov = new ByteBuffer[1];
|
|
||||||
outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
|
|
||||||
outIov[0].put(dataBytes, off, len);
|
|
||||||
outIov[0].flip();
|
|
||||||
|
|
||||||
send(sock, outIov);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void send(int sock, byte[] dataBytes) {
|
|
||||||
ByteBuffer[] outIov = new ByteBuffer[1];
|
|
||||||
outIov[0] = ByteBuffer.allocateDirect(dataBytes.length);
|
|
||||||
outIov[0].put(dataBytes);
|
|
||||||
outIov[0].flip();
|
|
||||||
|
|
||||||
send(sock, outIov);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Java Unix Sockets Library
|
||||||
|
*
|
||||||
|
* Copyright (c) Matthew Johnson 2004
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* To Contact the author, please email src@matthew.ath.cx
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package cx.ath.matthew.unix;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An IO Exception which occurred during UNIX Socket IO
|
||||||
|
*/
|
||||||
|
public class UnixIOException extends IOException {
|
||||||
|
private int no;
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
public UnixIOException(int no, String message) {
|
||||||
|
super(message);
|
||||||
|
this.message = message;
|
||||||
|
this.no = no;
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,11 +26,9 @@
|
||||||
*/
|
*/
|
||||||
package cx.ath.matthew.unix;
|
package cx.ath.matthew.unix;
|
||||||
|
|
||||||
|
import cx.ath.matthew.LibraryLoader;
|
||||||
import cx.ath.matthew.debug.Debug;
|
import cx.ath.matthew.debug.Debug;
|
||||||
import jnr.unixsocket.UnixSocketAddress;
|
|
||||||
import jnr.unixsocket.UnixSocketChannel;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
@ -39,8 +37,25 @@ import java.io.OutputStream;
|
||||||
* Represents a UnixSocket.
|
* Represents a UnixSocket.
|
||||||
*/
|
*/
|
||||||
public class UnixSocket {
|
public class UnixSocket {
|
||||||
|
static {
|
||||||
|
LibraryLoader.load();
|
||||||
|
}
|
||||||
|
|
||||||
private UnixSocketChannel channel;
|
private native void native_set_pass_cred(int sock, boolean passcred) throws IOException;
|
||||||
|
|
||||||
|
private native int native_connect(String address, boolean abs) throws IOException;
|
||||||
|
|
||||||
|
private native void native_close(int sock) throws IOException;
|
||||||
|
|
||||||
|
private native int native_getPID(int sock);
|
||||||
|
|
||||||
|
private native int native_getUID(int sock);
|
||||||
|
|
||||||
|
private native int native_getGID(int sock);
|
||||||
|
|
||||||
|
private native void native_send_creds(int sock, byte data) throws IOException;
|
||||||
|
|
||||||
|
private native byte native_recv_creds(int sock, int[] creds) throws IOException;
|
||||||
|
|
||||||
private UnixSocketAddress address = null;
|
private UnixSocketAddress address = null;
|
||||||
private USOutputStream os = null;
|
private USOutputStream os = null;
|
||||||
|
@ -58,8 +73,8 @@ public class UnixSocket {
|
||||||
this.sock = sock;
|
this.sock = sock;
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
this.os = new USOutputStream(channel, sock, this);
|
this.os = new USOutputStream(sock, this);
|
||||||
this.is = new USInputStream(channel, this);
|
this.is = new USInputStream(sock, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,7 +98,7 @@ public class UnixSocket {
|
||||||
* @param address The Unix Socket address to connect to
|
* @param address The Unix Socket address to connect to
|
||||||
*/
|
*/
|
||||||
public UnixSocket(String address) throws IOException {
|
public UnixSocket(String address) throws IOException {
|
||||||
this(new UnixSocketAddress(new File(address)));
|
this(new UnixSocketAddress(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -93,11 +108,9 @@ public class UnixSocket {
|
||||||
*/
|
*/
|
||||||
public void connect(UnixSocketAddress address) throws IOException {
|
public void connect(UnixSocketAddress address) throws IOException {
|
||||||
if (connected) close();
|
if (connected) close();
|
||||||
this.channel = UnixSocketChannel.open(address);
|
this.sock = native_connect(address.path, address.abs);
|
||||||
this.channel = UnixSocketChannel.open(address);
|
this.os = new USOutputStream(this.sock, this);
|
||||||
this.sock = channel.getFD();
|
this.is = new USInputStream(this.sock, this);
|
||||||
this.os = new USOutputStream(channel, sock, this);
|
|
||||||
this.is = new USInputStream(channel, this);
|
|
||||||
this.address = address;
|
this.address = address;
|
||||||
this.connected = true;
|
this.connected = true;
|
||||||
this.closed = false;
|
this.closed = false;
|
||||||
|
@ -110,7 +123,7 @@ public class UnixSocket {
|
||||||
* @param address The Unix Socket address to connect to
|
* @param address The Unix Socket address to connect to
|
||||||
*/
|
*/
|
||||||
public void connect(String address) throws IOException {
|
public void connect(String address) throws IOException {
|
||||||
connect(new UnixSocketAddress(new File(address)));
|
connect(new UnixSocketAddress(address));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finalize() {
|
public void finalize() {
|
||||||
|
@ -125,7 +138,7 @@ public class UnixSocket {
|
||||||
*/
|
*/
|
||||||
public synchronized void close() throws IOException {
|
public synchronized void close() throws IOException {
|
||||||
if (Debug.debug) Debug.print(Debug.INFO, "Closing socket");
|
if (Debug.debug) Debug.print(Debug.INFO, "Closing socket");
|
||||||
channel.close();
|
native_close(sock);
|
||||||
sock = 0;
|
sock = 0;
|
||||||
this.closed = true;
|
this.closed = true;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
@ -169,7 +182,91 @@ public class UnixSocket {
|
||||||
*/
|
*/
|
||||||
public void sendCredentialByte(byte data) throws IOException {
|
public void sendCredentialByte(byte data) throws IOException {
|
||||||
if (!connected) throw new NotConnectedException();
|
if (!connected) throw new NotConnectedException();
|
||||||
os.send(channel.getFD(), new byte[]{ data });
|
native_send_creds(sock, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a single byte of data, with credentials.
|
||||||
|
* (Works on BSDs)
|
||||||
|
*
|
||||||
|
* @param data The byte of data to send.
|
||||||
|
* @see getPeerUID
|
||||||
|
* @see getPeerPID
|
||||||
|
* @see getPeerGID
|
||||||
|
*/
|
||||||
|
public byte recvCredentialByte() throws IOException {
|
||||||
|
if (!connected) throw new NotConnectedException();
|
||||||
|
int[] creds = new int[]{-1, -1, -1};
|
||||||
|
byte data = native_recv_creds(sock, creds);
|
||||||
|
pid = creds[0];
|
||||||
|
uid = creds[1];
|
||||||
|
gid = creds[2];
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the credential passing status.
|
||||||
|
* (only effective on linux)
|
||||||
|
*
|
||||||
|
* @return The current status of credential passing.
|
||||||
|
* @see setPassCred
|
||||||
|
*/
|
||||||
|
public boolean getPassCred() {
|
||||||
|
return passcred;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the uid of the remote process.
|
||||||
|
* Some data must have been received on the socket to do this.
|
||||||
|
* Either setPassCred must be called on Linux first, or recvCredentialByte
|
||||||
|
* on BSD.
|
||||||
|
*
|
||||||
|
* @return the UID or -1 if it is not available
|
||||||
|
*/
|
||||||
|
public int getPeerUID() {
|
||||||
|
if (-1 == uid)
|
||||||
|
uid = native_getUID(sock);
|
||||||
|
return uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the gid of the remote process.
|
||||||
|
* Some data must have been received on the socket to do this.
|
||||||
|
* Either setPassCred must be called on Linux first, or recvCredentialByte
|
||||||
|
* on BSD.
|
||||||
|
*
|
||||||
|
* @return the GID or -1 if it is not available
|
||||||
|
*/
|
||||||
|
public int getPeerGID() {
|
||||||
|
if (-1 == gid)
|
||||||
|
gid = native_getGID(sock);
|
||||||
|
return gid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the pid of the remote process.
|
||||||
|
* Some data must have been received on the socket to do this.
|
||||||
|
* Either setPassCred must be called on Linux first, or recvCredentialByte
|
||||||
|
* on BSD.
|
||||||
|
*
|
||||||
|
* @return the PID or -1 if it is not available
|
||||||
|
*/
|
||||||
|
public int getPeerPID() {
|
||||||
|
if (-1 == pid)
|
||||||
|
pid = native_getPID(sock);
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the credential passing status.
|
||||||
|
* (Only does anything on linux, for other OS, you need
|
||||||
|
* to use send/recv credentials)
|
||||||
|
*
|
||||||
|
* @param enable Set to true for credentials to be passed.
|
||||||
|
*/
|
||||||
|
public void setPassCred(boolean enable) throws IOException {
|
||||||
|
native_set_pass_cred(sock, enable);
|
||||||
|
passcred = enable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Java Unix Sockets Library
|
||||||
|
*
|
||||||
|
* Copyright (c) Matthew Johnson 2004
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* To Contact the author, please email src@matthew.ath.cx
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
package cx.ath.matthew.unix;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an address for a Unix Socket
|
||||||
|
*/
|
||||||
|
public class UnixSocketAddress {
|
||||||
|
String path;
|
||||||
|
boolean abs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the address.
|
||||||
|
*
|
||||||
|
* @param path The path to the Unix Socket.
|
||||||
|
* @param abs True if this should be an abstract socket.
|
||||||
|
*/
|
||||||
|
public UnixSocketAddress(String path, boolean abs) {
|
||||||
|
this.path = path;
|
||||||
|
this.abs = abs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create the address.
|
||||||
|
*
|
||||||
|
* @param path The path to the Unix Socket.
|
||||||
|
*/
|
||||||
|
public UnixSocketAddress(String path) {
|
||||||
|
this.path = path;
|
||||||
|
this.abs = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the path.
|
||||||
|
*/
|
||||||
|
public String getPath() {
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this an address for an abstract socket.
|
||||||
|
*/
|
||||||
|
public boolean isAbstract() {
|
||||||
|
return abs;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the Address as a String.
|
||||||
|
*/
|
||||||
|
public String toString() {
|
||||||
|
return "unix" + (abs ? ":abstract" : "") + ":path=" + path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (!(o instanceof UnixSocketAddress)) return false;
|
||||||
|
return ((UnixSocketAddress) o).path.equals(this.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode() {
|
||||||
|
return path.hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,6 +43,14 @@ public class MessageWriter {
|
||||||
if (Debug.debug) Debug.print(Debug.WARN, "Message " + m + " wire-data was null!");
|
if (Debug.debug) Debug.print(Debug.WARN, "Message " + m + " wire-data was null!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isunix) {
|
||||||
|
if (Debug.debug) {
|
||||||
|
Debug.print(Debug.DEBUG, "Writing all " + m.getWireData().length + " buffers simultaneously to Unix Socket");
|
||||||
|
for (byte[] buf : m.getWireData())
|
||||||
|
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
|
||||||
|
}
|
||||||
|
((USOutputStream) out).write(m.getWireData());
|
||||||
|
} else
|
||||||
for (byte[] buf : m.getWireData()) {
|
for (byte[] buf : m.getWireData()) {
|
||||||
if (Debug.debug)
|
if (Debug.debug)
|
||||||
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
|
Debug.print(Debug.VERBOSE, "(" + buf + "):" + (null == buf ? "" : Hexdump.format(buf)));
|
||||||
|
|
|
@ -12,6 +12,7 @@ package org.freedesktop.dbus;
|
||||||
|
|
||||||
import cx.ath.matthew.debug.Debug;
|
import cx.ath.matthew.debug.Debug;
|
||||||
import cx.ath.matthew.unix.UnixSocket;
|
import cx.ath.matthew.unix.UnixSocket;
|
||||||
|
import cx.ath.matthew.unix.UnixSocketAddress;
|
||||||
import cx.ath.matthew.utils.Hexdump;
|
import cx.ath.matthew.utils.Hexdump;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
@ -25,6 +26,7 @@ import java.io.OutputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
@ -255,8 +257,10 @@ public class Transport {
|
||||||
return new String(res);
|
return new String(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final int MODE_SERVER = 1;
|
||||||
public static final int MODE_CLIENT = 2;
|
public static final int MODE_CLIENT = 2;
|
||||||
|
|
||||||
|
public static final int AUTH_NONE = 0;
|
||||||
public static final int AUTH_EXTERNAL = 1;
|
public static final int AUTH_EXTERNAL = 1;
|
||||||
public static final int AUTH_SHA = 2;
|
public static final int AUTH_SHA = 2;
|
||||||
public static final int AUTH_ANON = 4;
|
public static final int AUTH_ANON = 4;
|
||||||
|
@ -273,12 +277,15 @@ public class Transport {
|
||||||
public static final int WAIT_DATA = 1;
|
public static final int WAIT_DATA = 1;
|
||||||
public static final int WAIT_OK = 2;
|
public static final int WAIT_OK = 2;
|
||||||
public static final int WAIT_REJECT = 3;
|
public static final int WAIT_REJECT = 3;
|
||||||
|
public static final int WAIT_AUTH = 4;
|
||||||
|
public static final int WAIT_BEGIN = 5;
|
||||||
public static final int AUTHENTICATED = 6;
|
public static final int AUTHENTICATED = 6;
|
||||||
public static final int FAILED = 7;
|
public static final int FAILED = 7;
|
||||||
|
|
||||||
public static final int OK = 1;
|
public static final int OK = 1;
|
||||||
public static final int CONTINUE = 2;
|
public static final int CONTINUE = 2;
|
||||||
public static final int ERROR = 3;
|
public static final int ERROR = 3;
|
||||||
|
public static final int REJECT = 4;
|
||||||
|
|
||||||
public Command receive(InputStream s) throws IOException {
|
public Command receive(InputStream s) throws IOException {
|
||||||
StringBuffer sb = new StringBuffer();
|
StringBuffer sb = new StringBuffer();
|
||||||
|
@ -388,8 +395,89 @@ public class Transport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String challenge = "";
|
||||||
public String cookie = "";
|
public String cookie = "";
|
||||||
|
|
||||||
|
public int do_response(int auth, String Uid, String kernelUid, Command c) {
|
||||||
|
MessageDigest md = null;
|
||||||
|
try {
|
||||||
|
md = MessageDigest.getInstance("SHA");
|
||||||
|
} catch (NoSuchAlgorithmException NSAe) {
|
||||||
|
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, NSAe);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
switch (auth) {
|
||||||
|
case AUTH_NONE:
|
||||||
|
switch (c.getMechs()) {
|
||||||
|
case AUTH_ANON:
|
||||||
|
return OK;
|
||||||
|
case AUTH_EXTERNAL:
|
||||||
|
if (0 == col.compare(Uid, c.getData()) &&
|
||||||
|
(null == kernelUid || 0 == col.compare(Uid, kernelUid)))
|
||||||
|
return OK;
|
||||||
|
else
|
||||||
|
return ERROR;
|
||||||
|
case AUTH_SHA:
|
||||||
|
String context = COOKIE_CONTEXT;
|
||||||
|
long id = System.currentTimeMillis();
|
||||||
|
byte[] buf = new byte[8];
|
||||||
|
Message.marshallintBig(id, buf, 0, 8);
|
||||||
|
challenge = stupidlyEncode(md.digest(buf));
|
||||||
|
Random r = new Random();
|
||||||
|
r.nextBytes(buf);
|
||||||
|
cookie = stupidlyEncode(md.digest(buf));
|
||||||
|
try {
|
||||||
|
addCookie(context, "" + id, id / 1000, cookie);
|
||||||
|
} catch (IOException IOe) {
|
||||||
|
if (Debug.debug && AbstractConnection.EXCEPTION_DEBUG) Debug.print(Debug.ERR, IOe);
|
||||||
|
}
|
||||||
|
if (Debug.debug)
|
||||||
|
Debug.print(Debug.DEBUG, "Sending challenge: " + context + ' ' + id + ' ' + challenge);
|
||||||
|
c.setResponse(stupidlyEncode(context + ' ' + id + ' ' + challenge));
|
||||||
|
return CONTINUE;
|
||||||
|
default:
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
case AUTH_SHA:
|
||||||
|
String[] response = stupidlyDecode(c.getData()).split(" ");
|
||||||
|
if (response.length < 2) return ERROR;
|
||||||
|
String cchal = response[0];
|
||||||
|
String hash = response[1];
|
||||||
|
String prehash = challenge + ":" + cchal + ":" + cookie;
|
||||||
|
byte[] buf = md.digest(prehash.getBytes());
|
||||||
|
String posthash = stupidlyEncode(buf);
|
||||||
|
if (Debug.debug)
|
||||||
|
Debug.print(Debug.DEBUG, "Authenticating Hash; data=" + prehash + " remote hash=" + hash + " local hash=" + posthash);
|
||||||
|
if (0 == col.compare(posthash, hash))
|
||||||
|
return OK;
|
||||||
|
else
|
||||||
|
return ERROR;
|
||||||
|
default:
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String[] getTypes(int types) {
|
||||||
|
switch (types) {
|
||||||
|
case AUTH_EXTERNAL:
|
||||||
|
return new String[]{"EXTERNAL"};
|
||||||
|
case AUTH_SHA:
|
||||||
|
return new String[]{"DBUS_COOKIE_SHA1"};
|
||||||
|
case AUTH_ANON:
|
||||||
|
return new String[]{"ANONYMOUS"};
|
||||||
|
case AUTH_SHA + AUTH_EXTERNAL:
|
||||||
|
return new String[]{"EXTERNAL", "DBUS_COOKIE_SHA1"};
|
||||||
|
case AUTH_SHA + AUTH_ANON:
|
||||||
|
return new String[]{"ANONYMOUS", "DBUS_COOKIE_SHA1"};
|
||||||
|
case AUTH_EXTERNAL + AUTH_ANON:
|
||||||
|
return new String[]{"ANONYMOUS", "EXTERNAL"};
|
||||||
|
case AUTH_EXTERNAL + AUTH_ANON + AUTH_SHA:
|
||||||
|
return new String[]{"ANONYMOUS", "EXTERNAL", "DBUS_COOKIE_SHA1"};
|
||||||
|
default:
|
||||||
|
return new String[]{};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* performs SASL auth on the given streams.
|
* performs SASL auth on the given streams.
|
||||||
* Mode selects whether to run as a SASL server or client.
|
* Mode selects whether to run as a SASL server or client.
|
||||||
|
@ -400,6 +488,7 @@ public class Transport {
|
||||||
public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException {
|
public boolean auth(int mode, int types, String guid, OutputStream out, InputStream in, UnixSocket us) throws IOException {
|
||||||
String username = System.getProperty("user.name");
|
String username = System.getProperty("user.name");
|
||||||
String Uid = null;
|
String Uid = null;
|
||||||
|
String kernelUid = null;
|
||||||
try {
|
try {
|
||||||
Class c = Class.forName("com.sun.security.auth.module.UnixSystem");
|
Class c = Class.forName("com.sun.security.auth.module.UnixSystem");
|
||||||
Method m = c.getMethod("getUid");
|
Method m = c.getMethod("getUid");
|
||||||
|
@ -529,6 +618,110 @@ public class Transport {
|
||||||
state = FAILED;
|
state = FAILED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case MODE_SERVER:
|
||||||
|
switch (state) {
|
||||||
|
case INITIAL_STATE:
|
||||||
|
byte[] buf = new byte[1];
|
||||||
|
if (null == us) {
|
||||||
|
in.read(buf);
|
||||||
|
} else {
|
||||||
|
buf[0] = us.recvCredentialByte();
|
||||||
|
int kuid = us.getPeerUID();
|
||||||
|
if (kuid >= 0)
|
||||||
|
kernelUid = stupidlyEncode("" + kuid);
|
||||||
|
}
|
||||||
|
if (0 != buf[0]) state = FAILED;
|
||||||
|
else state = WAIT_AUTH;
|
||||||
|
break;
|
||||||
|
case WAIT_AUTH:
|
||||||
|
c = receive(in);
|
||||||
|
switch (c.getCommand()) {
|
||||||
|
case COMMAND_AUTH:
|
||||||
|
if (null == c.getData()) {
|
||||||
|
send(out, COMMAND_REJECTED, getTypes(types));
|
||||||
|
} else {
|
||||||
|
switch (do_response(current, Uid, kernelUid, c)) {
|
||||||
|
case CONTINUE:
|
||||||
|
send(out, COMMAND_DATA, c.getResponse());
|
||||||
|
current = c.getMechs();
|
||||||
|
state = WAIT_DATA;
|
||||||
|
break;
|
||||||
|
case OK:
|
||||||
|
send(out, COMMAND_OK, guid);
|
||||||
|
state = WAIT_BEGIN;
|
||||||
|
current = 0;
|
||||||
|
break;
|
||||||
|
case REJECT:
|
||||||
|
send(out, COMMAND_REJECTED, getTypes(types));
|
||||||
|
current = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case COMMAND_ERROR:
|
||||||
|
send(out, COMMAND_REJECTED, getTypes(types));
|
||||||
|
break;
|
||||||
|
case COMMAND_BEGIN:
|
||||||
|
state = FAILED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
send(out, COMMAND_ERROR, "Got invalid command");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WAIT_DATA:
|
||||||
|
c = receive(in);
|
||||||
|
switch (c.getCommand()) {
|
||||||
|
case COMMAND_DATA:
|
||||||
|
switch (do_response(current, Uid, kernelUid, c)) {
|
||||||
|
case CONTINUE:
|
||||||
|
send(out, COMMAND_DATA, c.getResponse());
|
||||||
|
state = WAIT_DATA;
|
||||||
|
break;
|
||||||
|
case OK:
|
||||||
|
send(out, COMMAND_OK, guid);
|
||||||
|
state = WAIT_BEGIN;
|
||||||
|
current = 0;
|
||||||
|
break;
|
||||||
|
case REJECT:
|
||||||
|
send(out, COMMAND_REJECTED, getTypes(types));
|
||||||
|
current = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case COMMAND_ERROR:
|
||||||
|
case COMMAND_CANCEL:
|
||||||
|
send(out, COMMAND_REJECTED, getTypes(types));
|
||||||
|
state = WAIT_AUTH;
|
||||||
|
break;
|
||||||
|
case COMMAND_BEGIN:
|
||||||
|
state = FAILED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
send(out, COMMAND_ERROR, "Got invalid command");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case WAIT_BEGIN:
|
||||||
|
c = receive(in);
|
||||||
|
switch (c.getCommand()) {
|
||||||
|
case COMMAND_ERROR:
|
||||||
|
case COMMAND_CANCEL:
|
||||||
|
send(out, COMMAND_REJECTED, getTypes(types));
|
||||||
|
state = WAIT_AUTH;
|
||||||
|
break;
|
||||||
|
case COMMAND_BEGIN:
|
||||||
|
state = AUTHENTICATED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
send(out, COMMAND_ERROR, "Got invalid command");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state = FAILED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -588,15 +781,25 @@ public class Transport {
|
||||||
types = SASL.AUTH_EXTERNAL;
|
types = SASL.AUTH_EXTERNAL;
|
||||||
mode = SASL.MODE_CLIENT;
|
mode = SASL.MODE_CLIENT;
|
||||||
us = new UnixSocket();
|
us = new UnixSocket();
|
||||||
if (null != address.getParameter("path"))
|
if (null != address.getParameter("abstract"))
|
||||||
us.connect(new jnr.unixsocket.UnixSocketAddress(new File(address.getParameter("path"))));
|
us.connect(new UnixSocketAddress(address.getParameter("abstract"), true));
|
||||||
|
else if (null != address.getParameter("path"))
|
||||||
|
us.connect(new UnixSocketAddress(address.getParameter("path"), false));
|
||||||
|
us.setPassCred(true);
|
||||||
in = us.getInputStream();
|
in = us.getInputStream();
|
||||||
out = us.getOutputStream();
|
out = us.getOutputStream();
|
||||||
} else if ("tcp".equals(address.getType())) {
|
} else if ("tcp".equals(address.getType())) {
|
||||||
types = SASL.AUTH_SHA;
|
types = SASL.AUTH_SHA;
|
||||||
|
if (null != address.getParameter("listen")) {
|
||||||
|
mode = SASL.MODE_SERVER;
|
||||||
|
ServerSocket ss = new ServerSocket();
|
||||||
|
ss.bind(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
||||||
|
s = ss.accept();
|
||||||
|
} else {
|
||||||
mode = SASL.MODE_CLIENT;
|
mode = SASL.MODE_CLIENT;
|
||||||
s = new Socket();
|
s = new Socket();
|
||||||
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
||||||
|
}
|
||||||
in = s.getInputStream();
|
in = s.getInputStream();
|
||||||
out = s.getOutputStream();
|
out = s.getOutputStream();
|
||||||
} else {
|
} else {
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -98,7 +98,6 @@
|
||||||
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
|
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
|
||||||
<twitter4j.version>4.0.4</twitter4j.version>
|
<twitter4j.version>4.0.4</twitter4j.version>
|
||||||
<jna.version>4.1.0</jna.version>
|
<jna.version>4.1.0</jna.version>
|
||||||
<jnr.version>0.14</jnr.version>
|
|
||||||
|
|
||||||
<!-- Test -->
|
<!-- Test -->
|
||||||
<greenmail.version>1.3.1b</greenmail.version>
|
<greenmail.version>1.3.1b</greenmail.version>
|
||||||
|
@ -702,11 +701,6 @@
|
||||||
<artifactId>jna</artifactId>
|
<artifactId>jna</artifactId>
|
||||||
<version>${jna.version}</version>
|
<version>${jna.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>com.github.jnr</groupId>
|
|
||||||
<artifactId>jnr-unixsocket</artifactId>
|
|
||||||
<version>${jnr.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-ldap-federation</artifactId>
|
<artifactId>keycloak-ldap-federation</artifactId>
|
||||||
|
|
|
@ -2,6 +2,7 @@ package org.keycloak.testsuite.sssd;
|
||||||
|
|
||||||
import org.jboss.arquillian.graphene.page.Page;
|
import org.jboss.arquillian.graphene.page.Page;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.representations.idm.GroupRepresentation;
|
import org.keycloak.representations.idm.GroupRepresentation;
|
||||||
|
@ -72,6 +73,7 @@ public class SSSDTest extends AbstractKeycloakTest {
|
||||||
adminClient.realm(REALM_NAME).userFederation().create(userFederation);
|
adminClient.realm(REALM_NAME).userFederation().create(userFederation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Ignore
|
||||||
@Test
|
@Test
|
||||||
public void testProviderFactories() {
|
public void testProviderFactories() {
|
||||||
List<UserFederationProviderFactoryRepresentation> providerFactories = adminClient.realm(REALM_NAME).userFederation().getProviderFactories();
|
List<UserFederationProviderFactoryRepresentation> providerFactories = adminClient.realm(REALM_NAME).userFederation().getProviderFactories();
|
||||||
|
|
Loading…
Reference in a new issue