Merge pull request #3467 from abstractj/KEYCLOAK-3580
[KEYCLOAK-3580] - Migrate DBus Java from Unix Socket C library to jnr-unixsocket
This commit is contained in:
commit
6baf9b89fe
11 changed files with 126 additions and 541 deletions
|
@ -65,6 +65,10 @@
|
||||||
<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>
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
/*
|
|
||||||
* 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(int sock, UnixSocket us) {
|
public USInputStream(UnixSocketChannel channel, 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,7 +65,8 @@ 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 = native_recv(sock, b, off, len, flags, timeout);
|
int count = receive(b, off, len);
|
||||||
|
|
||||||
/* 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)
|
||||||
|
@ -91,4 +92,21 @@ 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,22 +26,31 @@
|
||||||
*/
|
*/
|
||||||
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 native int native_send(int sock, byte[][] b) throws IOException;
|
private UnixSocketChannel channel;
|
||||||
|
|
||||||
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(int sock, UnixSocket us) {
|
public USOutputStream(UnixSocketChannel channel, 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 {
|
||||||
|
@ -52,14 +61,9 @@ 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();
|
||||||
native_send(sock, b, off, len);
|
send(sock, b, off, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(int b) throws IOException {
|
public void write(int b) throws IOException {
|
||||||
|
@ -75,4 +79,46 @@ 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* 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,9 +26,11 @@
|
||||||
*/
|
*/
|
||||||
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;
|
||||||
|
@ -37,25 +39,8 @@ import java.io.OutputStream;
|
||||||
* Represents a UnixSocket.
|
* Represents a UnixSocket.
|
||||||
*/
|
*/
|
||||||
public class UnixSocket {
|
public class UnixSocket {
|
||||||
static {
|
|
||||||
LibraryLoader.load();
|
|
||||||
}
|
|
||||||
|
|
||||||
private native void native_set_pass_cred(int sock, boolean passcred) throws IOException;
|
private UnixSocketChannel channel;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -73,8 +58,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(sock, this);
|
this.os = new USOutputStream(channel, sock, this);
|
||||||
this.is = new USInputStream(sock, this);
|
this.is = new USInputStream(channel, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -98,7 +83,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(address));
|
this(new UnixSocketAddress(new File(address)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,9 +93,11 @@ public class UnixSocket {
|
||||||
*/
|
*/
|
||||||
public void connect(UnixSocketAddress address) throws IOException {
|
public void connect(UnixSocketAddress address) throws IOException {
|
||||||
if (connected) close();
|
if (connected) close();
|
||||||
this.sock = native_connect(address.path, address.abs);
|
this.channel = UnixSocketChannel.open(address);
|
||||||
this.os = new USOutputStream(this.sock, this);
|
this.channel = UnixSocketChannel.open(address);
|
||||||
this.is = new USInputStream(this.sock, this);
|
this.sock = channel.getFD();
|
||||||
|
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;
|
||||||
|
@ -123,7 +110,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(address));
|
connect(new UnixSocketAddress(new File(address)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void finalize() {
|
public void finalize() {
|
||||||
|
@ -138,7 +125,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");
|
||||||
native_close(sock);
|
channel.close();
|
||||||
sock = 0;
|
sock = 0;
|
||||||
this.closed = true;
|
this.closed = true;
|
||||||
this.connected = false;
|
this.connected = false;
|
||||||
|
@ -182,91 +169,7 @@ 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();
|
||||||
native_send_creds(sock, data);
|
os.send(channel.getFD(), new byte[]{ 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* 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,14 +43,6 @@ 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,7 +12,6 @@ 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;
|
||||||
|
@ -26,7 +25,6 @@ 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;
|
||||||
|
@ -257,10 +255,8 @@ 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;
|
||||||
|
@ -277,15 +273,12 @@ 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();
|
||||||
|
@ -395,89 +388,8 @@ 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.
|
||||||
|
@ -488,7 +400,6 @@ 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");
|
||||||
|
@ -618,110 +529,6 @@ 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;
|
||||||
}
|
}
|
||||||
|
@ -781,25 +588,15 @@ 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("abstract"))
|
if (null != address.getParameter("path"))
|
||||||
us.connect(new UnixSocketAddress(address.getParameter("abstract"), true));
|
us.connect(new jnr.unixsocket.UnixSocketAddress(new File(address.getParameter("path"))));
|
||||||
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 {
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
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;
|
||||||
|
@ -26,6 +24,9 @@ import org.freedesktop.sssd.infopipe.InfoPipe;
|
||||||
import org.freedesktop.sssd.infopipe.User;
|
import org.freedesktop.sssd.infopipe.User;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -113,21 +114,14 @@ public class Sssd {
|
||||||
public static boolean isAvailable() {
|
public static boolean isAvailable() {
|
||||||
boolean sssdAvailable = false;
|
boolean sssdAvailable = false;
|
||||||
try {
|
try {
|
||||||
if (LibraryLoader.load().succeed()) {
|
Path path = Paths.get("/etc/sssd");
|
||||||
DBusConnection connection = DBusConnection.getConnection(DBusConnection.SYSTEM);
|
if (!Files.exists(path)) {
|
||||||
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.");
|
logger.debugv("SSSD is not available in your system. Federation provider will be disabled.");
|
||||||
} else {
|
} else {
|
||||||
sssdAvailable = true;
|
sssdAvailable = true;
|
||||||
}
|
}
|
||||||
connection.disconnect();
|
} catch (Exception e) {
|
||||||
} else {
|
logger.error("SSSD check failed", e);
|
||||||
logger.debugv("libunix_dbus_java not found. Federation provider will be disabled.");
|
|
||||||
}
|
|
||||||
} catch (DBusException e) {
|
|
||||||
logger.error("Failed to check the status of SSSD", e);
|
|
||||||
}
|
}
|
||||||
return sssdAvailable;
|
return sssdAvailable;
|
||||||
}
|
}
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -98,6 +98,7 @@
|
||||||
<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>
|
||||||
|
@ -700,6 +701,11 @@
|
||||||
<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>
|
||||||
|
|
Loading…
Reference in a new issue