Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
14dc0ff92f
57 changed files with 1167 additions and 687 deletions
|
@ -118,12 +118,11 @@
|
|||
/**
|
||||
* Obtains all entitlements from a Keycloak Server based on a give resourceServerId.
|
||||
*/
|
||||
this.entitlement = function (resourceSeververId) {
|
||||
this.entitlement = function (resourceSeververId, entitlementRequest ) {
|
||||
this.then = function (onGrant, onDeny, onError) {
|
||||
var request = new XMLHttpRequest();
|
||||
|
||||
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
|
||||
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
|
||||
|
||||
|
||||
request.onreadystatechange = function () {
|
||||
if (request.readyState == 4) {
|
||||
|
@ -149,7 +148,19 @@
|
|||
}
|
||||
};
|
||||
|
||||
request.send(null);
|
||||
var erJson = null
|
||||
|
||||
if(entitlementRequest) {
|
||||
request.open('POST', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
|
||||
request.setRequestHeader("Content-type", "application/json");
|
||||
erJson = JSON.stringify(entitlementRequest)
|
||||
} else {
|
||||
request.open('GET', keycloak.authServerUrl + '/realms/' + keycloak.realm + '/authz/entitlement/' + resourceSeververId, true);
|
||||
}
|
||||
|
||||
request.setRequestHeader('Authorization', 'Bearer ' + keycloak.token)
|
||||
request.send(erJson);
|
||||
|
||||
};
|
||||
|
||||
return this;
|
||||
|
|
|
@ -64,6 +64,8 @@ JBoss Drools, which require ``photoz-authz-policy`` artifact installed into your
|
|||
cd examples/authz/photoz
|
||||
mvn clean install
|
||||
|
||||
> Please make sure you have the environment variable M2_HOME set. It should reference the path for your Maven installation. If not set, you will see some WARN messages in the logs when booting Keycloak.
|
||||
|
||||
Now, let's import another configuration using the Administration Console in order to configure the client application ``photoz-restful-api`` as a resource server with all resources, scopes, permissions and policies.
|
||||
|
||||
Click on ``Clients`` on the left side menu. Click on the ``photoz-restful-api`` on the client listing page. This will
|
||||
|
@ -71,7 +73,7 @@ open the ``Client Details`` page. Once there, click on the `Authorization` tab.
|
|||
|
||||
Click on the ``Select file`` button, which means you want to import a resource server configuration. Now select the file that is located at:
|
||||
|
||||
examples/authz/photoz/photoz-restful-api/photoz-restful-api-authz-config.json
|
||||
examples/authz/photoz/photoz-restful-api/src/main/resources/photoz-restful-api-authz-service.json
|
||||
|
||||
Now click ``Upload`` and the resource server will be updated accordingly.
|
||||
|
||||
|
|
|
@ -55,6 +55,12 @@
|
|||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jboss.as.plugins</groupId>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
"logic": "POSITIVE",
|
||||
"decisionStrategy": "UNANIMOUS",
|
||||
"config": {
|
||||
"mavenArtifactVersion": "2.1.0-SNAPSHOT",
|
||||
"mavenArtifactVersion": "${project.version}",
|
||||
"mavenArtifactId": "photoz-authz-policy",
|
||||
"sessionName": "MainOwnerSession",
|
||||
"mavenArtifactGroupId": "org.keycloak",
|
|
@ -65,6 +65,10 @@
|
|||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jnr</groupId>
|
||||
<artifactId>jnr-unixsocket</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</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;
|
||||
|
||||
import jnr.unixsocket.UnixSocketChannel;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.channels.Channels;
|
||||
|
||||
public class USInputStream extends InputStream {
|
||||
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;
|
||||
private byte[] onebuf = new byte[1];
|
||||
private UnixSocket us;
|
||||
private boolean blocking = true;
|
||||
private int flags = 0;
|
||||
private int timeout = 0;
|
||||
|
||||
public USInputStream(int sock, UnixSocket us) {
|
||||
this.sock = sock;
|
||||
public USInputStream(UnixSocketChannel channel, UnixSocket us) {
|
||||
this.us = us;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
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 {
|
||||
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'.
|
||||
* read() returns -1 for 'end of stream.
|
||||
* 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) {
|
||||
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;
|
||||
|
||||
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.OutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
|
||||
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;
|
||||
boolean closed = false;
|
||||
private byte[] onebuf = new byte[1];
|
||||
private UnixSocket us;
|
||||
|
||||
public USOutputStream(int sock, UnixSocket us) {
|
||||
public USOutputStream(UnixSocketChannel channel, int sock, UnixSocket us) {
|
||||
this.sock = sock;
|
||||
this.us = us;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
|
@ -52,14 +61,9 @@ public class USOutputStream extends OutputStream {
|
|||
public void flush() {
|
||||
} // 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 {
|
||||
if (closed) throw new NotConnectedException();
|
||||
native_send(sock, b, off, len);
|
||||
send(sock, b, off, len);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
|
@ -75,4 +79,46 @@ public class USOutputStream extends OutputStream {
|
|||
public UnixSocket getSocket() {
|
||||
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;
|
||||
|
||||
import cx.ath.matthew.LibraryLoader;
|
||||
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.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
@ -37,25 +39,8 @@ import java.io.OutputStream;
|
|||
* Represents a UnixSocket.
|
||||
*/
|
||||
public class UnixSocket {
|
||||
static {
|
||||
LibraryLoader.load();
|
||||
}
|
||||
|
||||
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 UnixSocketChannel channel;
|
||||
|
||||
private UnixSocketAddress address = null;
|
||||
private USOutputStream os = null;
|
||||
|
@ -73,8 +58,8 @@ public class UnixSocket {
|
|||
this.sock = sock;
|
||||
this.address = address;
|
||||
this.connected = true;
|
||||
this.os = new USOutputStream(sock, this);
|
||||
this.is = new USInputStream(sock, this);
|
||||
this.os = new USOutputStream(channel, sock, this);
|
||||
this.is = new USInputStream(channel, this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,7 +83,7 @@ public class UnixSocket {
|
|||
* @param address The Unix Socket address to connect to
|
||||
*/
|
||||
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 {
|
||||
if (connected) close();
|
||||
this.sock = native_connect(address.path, address.abs);
|
||||
this.os = new USOutputStream(this.sock, this);
|
||||
this.is = new USInputStream(this.sock, this);
|
||||
this.channel = UnixSocketChannel.open(address);
|
||||
this.channel = UnixSocketChannel.open(address);
|
||||
this.sock = channel.getFD();
|
||||
this.os = new USOutputStream(channel, sock, this);
|
||||
this.is = new USInputStream(channel, this);
|
||||
this.address = address;
|
||||
this.connected = true;
|
||||
this.closed = false;
|
||||
|
@ -123,7 +110,7 @@ public class UnixSocket {
|
|||
* @param address The Unix Socket address to connect to
|
||||
*/
|
||||
public void connect(String address) throws IOException {
|
||||
connect(new UnixSocketAddress(address));
|
||||
connect(new UnixSocketAddress(new File(address)));
|
||||
}
|
||||
|
||||
public void finalize() {
|
||||
|
@ -138,7 +125,7 @@ public class UnixSocket {
|
|||
*/
|
||||
public synchronized void close() throws IOException {
|
||||
if (Debug.debug) Debug.print(Debug.INFO, "Closing socket");
|
||||
native_close(sock);
|
||||
channel.close();
|
||||
sock = 0;
|
||||
this.closed = true;
|
||||
this.connected = false;
|
||||
|
@ -182,91 +169,7 @@ public class UnixSocket {
|
|||
*/
|
||||
public void sendCredentialByte(byte data) throws IOException {
|
||||
if (!connected) throw new NotConnectedException();
|
||||
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;
|
||||
os.send(channel.getFD(), new byte[]{ data });
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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!");
|
||||
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()) {
|
||||
if (Debug.debug)
|
||||
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.unix.UnixSocket;
|
||||
import cx.ath.matthew.unix.UnixSocketAddress;
|
||||
import cx.ath.matthew.utils.Hexdump;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
|
@ -26,7 +25,6 @@ import java.io.OutputStream;
|
|||
import java.io.PrintWriter;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
@ -257,10 +255,8 @@ public class Transport {
|
|||
return new String(res);
|
||||
}
|
||||
|
||||
public static final int MODE_SERVER = 1;
|
||||
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_SHA = 2;
|
||||
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_OK = 2;
|
||||
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 FAILED = 7;
|
||||
|
||||
public static final int OK = 1;
|
||||
public static final int CONTINUE = 2;
|
||||
public static final int ERROR = 3;
|
||||
public static final int REJECT = 4;
|
||||
|
||||
public Command receive(InputStream s) throws IOException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
@ -395,89 +388,8 @@ public class Transport {
|
|||
}
|
||||
}
|
||||
|
||||
public String challenge = "";
|
||||
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.
|
||||
* 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 {
|
||||
String username = System.getProperty("user.name");
|
||||
String Uid = null;
|
||||
String kernelUid = null;
|
||||
try {
|
||||
Class c = Class.forName("com.sun.security.auth.module.UnixSystem");
|
||||
Method m = c.getMethod("getUid");
|
||||
|
@ -618,110 +529,6 @@ public class Transport {
|
|||
state = FAILED;
|
||||
}
|
||||
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:
|
||||
return false;
|
||||
}
|
||||
|
@ -781,25 +588,15 @@ public class Transport {
|
|||
types = SASL.AUTH_EXTERNAL;
|
||||
mode = SASL.MODE_CLIENT;
|
||||
us = new UnixSocket();
|
||||
if (null != address.getParameter("abstract"))
|
||||
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);
|
||||
if (null != address.getParameter("path"))
|
||||
us.connect(new jnr.unixsocket.UnixSocketAddress(new File(address.getParameter("path"))));
|
||||
in = us.getInputStream();
|
||||
out = us.getOutputStream();
|
||||
} else if ("tcp".equals(address.getType())) {
|
||||
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;
|
||||
s = new Socket();
|
||||
s.connect(new InetSocketAddress(address.getParameter("host"), Integer.parseInt(address.getParameter("port"))));
|
||||
}
|
||||
in = s.getInputStream();
|
||||
out = s.getOutputStream();
|
||||
} else {
|
||||
|
|
|
@ -17,8 +17,6 @@
|
|||
|
||||
package org.keycloak.federation.sssd.api;
|
||||
|
||||
import cx.ath.matthew.LibraryLoader;
|
||||
import org.freedesktop.DBus;
|
||||
import org.freedesktop.dbus.DBusConnection;
|
||||
import org.freedesktop.dbus.Variant;
|
||||
import org.freedesktop.dbus.exceptions.DBusException;
|
||||
|
@ -26,6 +24,9 @@ import org.freedesktop.sssd.infopipe.InfoPipe;
|
|||
import org.freedesktop.sssd.infopipe.User;
|
||||
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.List;
|
||||
import java.util.Map;
|
||||
|
@ -113,21 +114,14 @@ public class Sssd {
|
|||
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) {
|
||||
Path path = Paths.get("/etc/sssd");
|
||||
if (!Files.exists(path)) {
|
||||
logger.debugv("SSSD is not available in your system. Federation provider will be disabled.");
|
||||
} else {
|
||||
sssdAvailable = true;
|
||||
}
|
||||
connection.disconnect();
|
||||
} else {
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
logger.error("SSSD check failed", e);
|
||||
}
|
||||
return sssdAvailable;
|
||||
}
|
||||
|
|
|
@ -108,14 +108,10 @@ public class CachedResourceStore implements ResourceStore {
|
|||
|
||||
@Override
|
||||
public List<Resource> findByOwner(String ownerId) {
|
||||
List<String> cachedIds = this.cache.get(getResourceOwnerCacheKey(ownerId));
|
||||
|
||||
if (cachedIds == null) {
|
||||
for (Resource resource : getDelegate().findByOwner(ownerId)) {
|
||||
updateCachedIds(getResourceOwnerCacheKey(ownerId), resource, true);
|
||||
}
|
||||
cachedIds = this.cache.getOrDefault(getResourceOwnerCacheKey(ownerId), Collections.emptyList());
|
||||
}
|
||||
|
||||
return ((List<String>) this.cache.getOrDefault(getResourceOwnerCacheKey(ownerId), Collections.emptyList())).stream().map(this::findById)
|
||||
.filter(resource -> resource != null)
|
||||
|
|
6
pom.xml
6
pom.xml
|
@ -98,6 +98,7 @@
|
|||
<servlet.api.30.version>1.0.2.Final</servlet.api.30.version>
|
||||
<twitter4j.version>4.0.4</twitter4j.version>
|
||||
<jna.version>4.1.0</jna.version>
|
||||
<jnr.version>0.14</jnr.version>
|
||||
|
||||
<!-- Test -->
|
||||
<greenmail.version>1.3.1b</greenmail.version>
|
||||
|
@ -700,6 +701,11 @@
|
|||
<artifactId>jna</artifactId>
|
||||
<version>${jna.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.jnr</groupId>
|
||||
<artifactId>jnr-unixsocket</artifactId>
|
||||
<version>${jnr.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-ldap-federation</artifactId>
|
||||
|
|
|
@ -21,9 +21,11 @@ package org.keycloak.authorization.store;
|
|||
import org.keycloak.authorization.store.syncronization.ClientApplicationSynchronizer;
|
||||
import org.keycloak.authorization.store.syncronization.RealmSynchronizer;
|
||||
import org.keycloak.authorization.store.syncronization.Synchronizer;
|
||||
import org.keycloak.authorization.store.syncronization.UserSynchronizer;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel.ClientRemovedEvent;
|
||||
import org.keycloak.models.RealmModel.RealmRemovedEvent;
|
||||
import org.keycloak.models.UserModel.UserRemovedEvent;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
|
@ -45,6 +47,7 @@ public interface AuthorizationStoreFactory extends ProviderFactory<StoreFactory>
|
|||
|
||||
synchronizers.put(ClientRemovedEvent.class, new ClientApplicationSynchronizer());
|
||||
synchronizers.put(RealmRemovedEvent.class, new RealmSynchronizer());
|
||||
synchronizers.put(UserRemovedEvent.class, new UserSynchronizer());
|
||||
|
||||
factory.register(event -> {
|
||||
try {
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* 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.authorization.store.syncronization;
|
||||
|
||||
import org.keycloak.authorization.AuthorizationProvider;
|
||||
import org.keycloak.authorization.store.PolicyStore;
|
||||
import org.keycloak.authorization.store.ResourceStore;
|
||||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserModel.UserRemovedEvent;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UserSynchronizer implements Synchronizer<UserRemovedEvent> {
|
||||
|
||||
@Override
|
||||
public void synchronize(UserRemovedEvent event, KeycloakSessionFactory factory) {
|
||||
ProviderFactory<AuthorizationProvider> providerFactory = factory.getProviderFactory(AuthorizationProvider.class);
|
||||
AuthorizationProvider authorizationProvider = providerFactory.create(event.getKeycloakSession());
|
||||
StoreFactory storeFactory = authorizationProvider.getStoreFactory();
|
||||
UserModel userModel = event.getUser();
|
||||
ResourceStore resourceStore = storeFactory.getResourceStore();
|
||||
PolicyStore policyStore = storeFactory.getPolicyStore();
|
||||
|
||||
resourceStore.findByOwner(userModel.getId()).forEach(resource -> {
|
||||
String resourceId = resource.getId();
|
||||
policyStore.findByResource(resourceId).forEach(policy -> {
|
||||
if (policy.getResources().size() == 1) {
|
||||
policyStore.delete(policy.getId());
|
||||
} else {
|
||||
policy.removeResource(resource);
|
||||
}
|
||||
});
|
||||
resourceStore.delete(resourceId);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -28,8 +28,11 @@ import org.keycloak.authorization.store.ResourceStore;
|
|||
import org.keycloak.authorization.store.StoreFactory;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserFederationManager;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.authorization.ResourceOwnerRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.services.ErrorResponse;
|
||||
import org.keycloak.services.resources.admin.RealmAuth;
|
||||
|
@ -76,12 +79,36 @@ public class ResourceSetService {
|
|||
requireManage();
|
||||
StoreFactory storeFactory = this.authorization.getStoreFactory();
|
||||
Resource existingResource = storeFactory.getResourceStore().findByName(resource.getName(), this.resourceServer.getId());
|
||||
ResourceOwnerRepresentation owner = resource.getOwner();
|
||||
|
||||
if (existingResource != null && existingResource.getResourceServer().getId().equals(this.resourceServer.getId())
|
||||
&& existingResource.getOwner().equals(resource.getOwner())) {
|
||||
&& existingResource.getOwner().equals(owner)) {
|
||||
return ErrorResponse.exists("Resource with name [" + resource.getName() + "] already exists.");
|
||||
}
|
||||
|
||||
if (owner != null) {
|
||||
String ownerId = owner.getId();
|
||||
|
||||
if (ownerId != null) {
|
||||
if (!resourceServer.getClientId().equals(ownerId)) {
|
||||
RealmModel realm = authorization.getRealm();
|
||||
KeycloakSession keycloakSession = authorization.getKeycloakSession();
|
||||
UserFederationManager users = keycloakSession.users();
|
||||
UserModel ownerModel = users.getUserById(ownerId, realm);
|
||||
|
||||
if (ownerModel == null) {
|
||||
ownerModel = users.getUserByUsername(ownerId, realm);
|
||||
}
|
||||
|
||||
if (ownerModel == null) {
|
||||
return ErrorResponse.error("Owner must be a valid username or user identifier. If the resource server, the client id or null.", Status.BAD_REQUEST);
|
||||
}
|
||||
|
||||
owner.setId(ownerModel.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Resource model = toModel(resource, this.resourceServer, authorization);
|
||||
|
||||
ResourceRepresentation representation = new ResourceRepresentation();
|
||||
|
@ -129,7 +156,7 @@ public class ResourceSetService {
|
|||
if (policyModel.getResources().size() == 1) {
|
||||
policyStore.delete(policyModel.getId());
|
||||
} else {
|
||||
policyModel.addResource(resource);
|
||||
policyModel.removeResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
||||
|
||||
<xsl:variable name="keycloakNamespace" select="'urn:jboss:domain:keycloak:'"/>
|
||||
|
||||
<xsl:template match="@* | node()">
|
||||
<xsl:copy>
|
||||
<xsl:apply-templates select="@* | node()"/>
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="//*[local-name()='system-properties']">
|
||||
<system-properties>
|
||||
<property name="hawtio.authenticationEnabled" value="true" />
|
||||
<property name="hawtio.realm" value="hawtio" />
|
||||
<property name="hawtio.roles" value="admin,viewer" />
|
||||
<property name="hawtio.rolePrincipalClasses" value="org.keycloak.adapters.jaas.RolePrincipal" />
|
||||
<property name="hawtio.keycloakEnabled" value="true" />
|
||||
<property name="hawtio.keycloakClientConfig" value="${{jboss.server.config.dir}}/keycloak-hawtio-client.json" />
|
||||
<property name="hawtio.keycloakServerConfig" value="${{jboss.server.config.dir}}/keycloak-hawtio.json" />
|
||||
</system-properties>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="//*[local-name()='security-domain' and @name = 'hawtio-domain']">
|
||||
<security-domain name="hawtio" cache-type="default">
|
||||
<authentication>
|
||||
<login-module code="org.keycloak.adapters.jaas.BearerTokenLoginModule" flag="required">
|
||||
<module-option name="keycloak-config-file" value="${{hawtio.keycloakServerConfig}}"/>
|
||||
</login-module>
|
||||
</authentication>
|
||||
</security-domain>
|
||||
</xsl:template>
|
||||
|
||||
<xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $keycloakNamespace)]">
|
||||
<xsl:copy>
|
||||
<secure-deployment name="hawtio.war" />
|
||||
</xsl:copy>
|
||||
</xsl:template>
|
||||
|
||||
|
||||
</xsl:stylesheet>
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
echo "FUSE_INSTALLER=$JBOSS_HOME/$FUSE_INSTALLER_NAME"
|
||||
if [ ! -f "$JBOSS_HOME/$FUSE_INSTALLER_NAME" ] ; then
|
||||
>&2 echo "JBOSS_HOME/$FUSE_INSTALLER_NAME doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd $JBOSS_HOME
|
||||
java -jar $FUSE_INSTALLER_NAME
|
||||
rm $FUSE_INSTALLER_NAME
|
||||
|
||||
exit 0
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"realm" : "demo",
|
||||
"resource" : "hawtio-client",
|
||||
"auth-server-url" : "http://localhost:8180/auth",
|
||||
"ssl-required" : "external",
|
||||
"public-client" : true
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"realm" : "demo",
|
||||
"resource" : "jaas",
|
||||
"bearer-only" : true,
|
||||
"auth-server-url" : "http://localhost:8180/auth",
|
||||
"ssl-required" : "external",
|
||||
"use-resource-role-mappings": false,
|
||||
"principal-attribute": "preferred_username"
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>integration-arquillian-servers-app-server-jboss</artifactId>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<version>2.4.0.CR1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>integration-arquillian-servers-app-server-eap6-fuse</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>App Server - JBoss - EAP 6 + Fuse integration</name>
|
||||
|
||||
<properties>
|
||||
<app.server.jboss>eap6-fuse</app.server.jboss>
|
||||
|
||||
<app.server.jboss.groupId>org.jboss.as</app.server.jboss.groupId>
|
||||
<app.server.jboss.artifactId>jboss-as-dist</app.server.jboss.artifactId>
|
||||
<app.server.jboss.version>${eap6.version}</app.server.jboss.version>
|
||||
<app.server.jboss.unpacked.folder.name>jboss-eap-6.4</app.server.jboss.unpacked.folder.name>
|
||||
|
||||
<fuse.installer.groupId>com.redhat.fuse.eap</fuse.installer.groupId>
|
||||
<fuse.installer.artifactId>fuse-eap-installer</fuse.installer.artifactId>
|
||||
<fuse.installer.version>6.3.0.redhat-187</fuse.installer.version>
|
||||
|
||||
<app.server.oidc.adapter.artifactId>keycloak-eap6-adapter-dist</app.server.oidc.adapter.artifactId>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-fuse-installer</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>${fuse.installer.groupId}</groupId>
|
||||
<artifactId>${fuse.installer.artifactId}</artifactId>
|
||||
<version>${fuse.installer.version}</version>
|
||||
<type>jar</type>
|
||||
<outputDirectory>${app.server.jboss.home}</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>install-fuse</id>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>${common.resources}/install-fuse.${script.suffix}</executable>
|
||||
<workingDirectory>${app.server.jboss.home}</workingDirectory>
|
||||
<environmentVariables>
|
||||
<JAVA_HOME>${app.server.java.home}</JAVA_HOME>
|
||||
<JBOSS_HOME>${app.server.jboss.home}</JBOSS_HOME>
|
||||
<FUSE_INSTALLER_NAME>${fuse.installer.artifactId}-${fuse.installer.version}.jar</FUSE_INSTALLER_NAME>
|
||||
</environmentVariables>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-hawtio-jsons</id>
|
||||
<phase>process-test-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${app.server.jboss.home}/standalone/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${common.resources}</directory>
|
||||
<includes>
|
||||
<include>keycloak-hawtio.json</include>
|
||||
<include>keycloak-hawtio-client.json</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>xml-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-hawtio-to-standalone</id>
|
||||
<phase>process-test-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<transformationSets>
|
||||
<transformationSet>
|
||||
<dir>${app.server.jboss.home}/standalone/configuration</dir>
|
||||
<includes>
|
||||
<include>standalone.xml</include>
|
||||
</includes>
|
||||
<stylesheet>${common.resources}/add-hawtio.xsl</stylesheet>
|
||||
<outputDir>${app.server.jboss.home}/standalone/configuration</outputDir>
|
||||
</transformationSet>
|
||||
</transformationSets>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
|
@ -108,7 +108,7 @@
|
|||
<executions>
|
||||
<execution>
|
||||
<id>configure-adapter-debug-log</id>
|
||||
<phase>process-resources</phase>
|
||||
<phase>process-test-resources</phase>
|
||||
<goals>
|
||||
<goal>transform</goal>
|
||||
</goals>
|
||||
|
@ -189,7 +189,7 @@
|
|||
<executions>
|
||||
<execution>
|
||||
<id>install-adapters</id>
|
||||
<phase>process-test-resources</phase>
|
||||
<phase>process-resources</phase>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
|
@ -438,6 +438,12 @@
|
|||
<module>relative</module>
|
||||
</modules>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>app-server-eap6-fuse</id>
|
||||
<modules>
|
||||
<module>eap6-fuse</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<name>Test apps distribution</name>
|
||||
|
||||
<build>
|
||||
<finalName>${product.name}-${product.version}-test-apps</finalName>
|
||||
<finalName>test-apps-dist</finalName>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package org.keycloak.testsuite.adapter.page;
|
||||
|
||||
import org.keycloak.testsuite.page.AbstractPage;
|
||||
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class HawtioPage extends AbstractPage {
|
||||
|
||||
public String getUrl() {
|
||||
if (Boolean.parseBoolean(System.getProperty("app.server.ssl.required"))) {
|
||||
return "https://localhost:" + System.getProperty("app.server.https.port", "8543") + "/hawtio";
|
||||
}
|
||||
return "http://localhost:" + System.getProperty("app.server.http.port", "8180") + "/hawtio";
|
||||
}
|
||||
|
||||
@Override
|
||||
public UriBuilder createUriBuilder() {
|
||||
return UriBuilder.fromUri(getUrl());
|
||||
}
|
||||
}
|
|
@ -74,7 +74,7 @@ public class URLProvider extends URLResourceProvider {
|
|||
}
|
||||
|
||||
try {
|
||||
if ("eap6".equals(System.getProperty("app.server"))) {
|
||||
if (System.getProperty("app.server","").startsWith("eap6")) {
|
||||
if (url == null) {
|
||||
url = new URL("http://localhost:8080/");
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ public abstract class AbstractPageWithInjectedUrl extends AbstractPage {
|
|||
|
||||
//EAP6 URL fix
|
||||
protected URL createInjectedURL(String url) {
|
||||
if (!System.getProperty("app.server").equals("eap6")) {
|
||||
if (!System.getProperty("app.server","").startsWith("eap6")) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -68,6 +68,7 @@ public abstract class AbstractAdapterTest extends AbstractAuthTest {
|
|||
} else {
|
||||
modifyClientRedirectUris(tr, "^(/.*/\\*)", appServerContextRootPage.toString() + "$1");
|
||||
modifyClientUrls(tr, "^(/.*)", appServerContextRootPage.toString() + "$1");
|
||||
modifyClientWebOrigins(tr, "8080", System.getProperty("app.server.http.port", null));
|
||||
modifySamlMasterURLs(tr, "8080", System.getProperty("auth.server.http.port", null));
|
||||
modifySAMLClientsAttributes(tr, "8080", System.getProperty("app.server.http.port", "8280"));
|
||||
modifyClientJWKSUrl(tr, "^(/.*)", appServerContextRootPage.toString() + "$1");
|
||||
|
|
|
@ -51,13 +51,7 @@ public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
|
|||
System.out.println(EXAMPLES_VERSION_SUFFIX);
|
||||
|
||||
EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/example-realms";
|
||||
|
||||
if (!System.getProperty("unpacked.container.folder.name","").isEmpty()) {
|
||||
TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/" + System.getProperty("unpacked.container.folder.name","") + "-test-apps";
|
||||
} else {
|
||||
TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/Keycloak-" + EXAMPLES_VERSION_SUFFIX + "-test-apps";
|
||||
}
|
||||
|
||||
TEST_APPS_HOME_DIR = EXAMPLES_HOME + "/test-apps-dist";
|
||||
EXAMPLES_WEB_XML = EXAMPLES_HOME + "/web.xml";
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.page.HawtioPage;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
|
||||
import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class AbstractHawtioAdapterTest extends AbstractExampleAdapterTest {
|
||||
|
||||
@Page
|
||||
private HawtioPage hawtioPage;
|
||||
|
||||
@Override
|
||||
public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
|
||||
testRealms.add(loadRealm("/adapter-test/hawtio-realm/demorealm.json"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore //Waiting for PATCH-1446
|
||||
public void hawtioTest() {
|
||||
testRealmLoginPage.setAuthRealm(DEMO);
|
||||
hawtioPage.navigateTo();
|
||||
assertCurrentUrlStartsWith(testRealmLoginPage);
|
||||
testRealmLoginPage.form().login("root", "password");
|
||||
|
||||
assertCurrentUrlStartsWith(hawtioPage.getDriver(), hawtioPage.toString() + "/welcome");
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ import org.jboss.arquillian.container.test.api.Deployer;
|
|||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
|
@ -27,6 +28,7 @@ import org.keycloak.admin.client.resource.ClientsResource;
|
|||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -47,6 +49,9 @@ public abstract class AbstractDefaultAuthzConfigAdapterTest extends AbstractExam
|
|||
private static final String REALM_NAME = "hello-world-authz";
|
||||
private static final String RESOURCE_SERVER_ID = "hello-world-authz-service";
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import org.jboss.arquillian.graphene.page.Page;
|
|||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
|
@ -37,6 +38,7 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.adapter.page.PhotozClientAuthzTestApp;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -79,6 +81,9 @@ public abstract class AbstractPhotozExampleAdapterTest extends AbstractExampleAd
|
|||
testRealmPage.setAuthRealm(REALM_NAME);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@Before
|
||||
public void beforePhotozExampleAdapterTest() throws FileNotFoundException {
|
||||
deleteAllCookiesForClientPage();
|
||||
|
|
|
@ -20,6 +20,7 @@ import org.jboss.arquillian.container.test.api.Deployer;
|
|||
import org.jboss.arquillian.container.test.api.Deployment;
|
||||
import org.jboss.arquillian.test.api.ArquillianResource;
|
||||
import org.jboss.shrinkwrap.api.spec.WebArchive;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.AuthorizationResource;
|
||||
import org.keycloak.admin.client.resource.ClientResource;
|
||||
|
@ -33,6 +34,7 @@ import org.keycloak.representations.idm.RoleRepresentation;
|
|||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
|
||||
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
|
||||
import org.keycloak.testsuite.util.WaitUtils;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
|
@ -63,6 +65,9 @@ public abstract class AbstractServletAuthzAdapterTest extends AbstractExampleAda
|
|||
private static final String REALM_NAME = "servlet-authz";
|
||||
private static final String RESOURCE_SERVER_ID = "servlet-authz-app";
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@ArquillianResource
|
||||
private Deployer deployer;
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ import static org.junit.Assert.assertNull;
|
|||
import static org.junit.Assert.fail;
|
||||
import static org.keycloak.services.resources.admin.RealmAuth.Resource.AUTHORIZATION;
|
||||
import static org.keycloak.services.resources.admin.RealmAuth.Resource.CLIENT;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -782,6 +783,7 @@ public class PermissionsTest extends AbstractKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void clientAuthorization() {
|
||||
ProfileAssume.assumePreview();
|
||||
invoke(new InvocationWithResponse() {
|
||||
public void invoke(RealmResource realm, AtomicReference<Response> response) {
|
||||
realm.clients().create(ClientBuilder.create().clientId("foo-authz").build());
|
||||
|
|
|
@ -32,6 +32,8 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.keycloak.common.Profile.isPreviewEnabled;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
|
||||
*/
|
||||
|
@ -135,7 +137,9 @@ public class ProvidersTest extends AbstractAuthenticationTest {
|
|||
"Validates a OTP on a separate OTP form. Only shown if required based on the configured conditions.");
|
||||
addProviderInfo(result, "auth-cookie", "Cookie", "Validates the SSO cookie set by the auth server.");
|
||||
addProviderInfo(result, "auth-otp-form", "OTP Form", "Validates a OTP on a separate OTP form.");
|
||||
if (isPreviewEnabled()) {
|
||||
addProviderInfo(result, "auth-script-based", "Script", "Script based authentication. Allows to define custom authentication logic via JavaScript.");
|
||||
}
|
||||
addProviderInfo(result, "auth-spnego", "Kerberos", "Initiates the SPNEGO protocol. Most often used with Kerberos.");
|
||||
addProviderInfo(result, "auth-username-password-form", "Username Password Form",
|
||||
"Validates a username and password from login form.");
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package org.keycloak.testsuite.admin.authentication;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
|
||||
import javax.ws.rs.BadRequestException;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
public class ScriptBasedAuthenticatorTest extends AbstractAuthenticationTest {
|
||||
|
||||
@Test
|
||||
public void checkIfTurnedOffWithProductProfile() throws InterruptedException {
|
||||
ProfileAssume.assumePreviewDisabled();
|
||||
|
||||
HashMap<String, String> params = new HashMap<>();
|
||||
params.put("newName", "Copy-of-browser");
|
||||
authMgmtResource.copy("browser", params);
|
||||
|
||||
params.put("provider", "auth-script-based");
|
||||
try {
|
||||
authMgmtResource.addExecution("Copy-of-browser", params);
|
||||
Assert.fail("Adding script based authenticator should fail with product profile");
|
||||
} catch (BadRequestException ex) {
|
||||
//Expected
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,8 +14,9 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.testsuite.adapter.example.authorization;
|
||||
package org.keycloak.testsuite.admin.client.authorization;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.adapters.KeycloakDeployment;
|
||||
import org.keycloak.adapters.KeycloakDeploymentBuilder;
|
||||
|
@ -23,6 +24,7 @@ import org.keycloak.adapters.authorization.PolicyEnforcer;
|
|||
import org.keycloak.representations.adapters.config.PolicyEnforcerConfig;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.AbstractKeycloakTest;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -34,6 +36,9 @@ import static org.keycloak.testsuite.util.IOUtil.loadRealm;
|
|||
*/
|
||||
public class EnforcerConfigTest extends AbstractKeycloakTest {
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation realm = loadRealm(getClass().getResourceAsStream("/authorization-test/test-authz-realm.json"));
|
|
@ -28,8 +28,15 @@ import java.net.URLEncoder;
|
|||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
|
||||
import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword;
|
||||
import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL;
|
||||
import org.keycloak.testsuite.pages.IdpConfirmLinkPage;
|
||||
import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
|
||||
import org.keycloak.testsuite.util.MailServer;
|
||||
import org.keycloak.testsuite.util.MailServerConfiguration;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
|
||||
public abstract class AbstractBrokerTest extends AbstractKeycloakTest {
|
||||
|
||||
|
@ -62,6 +69,9 @@ public abstract class AbstractBrokerTest extends AbstractKeycloakTest {
|
|||
@Page
|
||||
protected ErrorPage errorPage;
|
||||
|
||||
@Page
|
||||
protected IdpConfirmLinkPage idpConfirmLinkPage;
|
||||
|
||||
@Override
|
||||
public void addTestRealms(List<RealmRepresentation> testRealms) {
|
||||
RealmRepresentation providerRealm = createProviderRealm();
|
||||
|
@ -200,6 +210,68 @@ public abstract class AbstractBrokerTest extends AbstractKeycloakTest {
|
|||
assertEquals(userCount, adminClient.realm(consumerRealmName()).users().count());
|
||||
}
|
||||
|
||||
// KEYCLOAK-2957
|
||||
@Test
|
||||
public void testLinkAccountWithEmailVerified() {
|
||||
//start mail server
|
||||
MailServer.start();
|
||||
MailServer.createEmailAccount(USER_EMAIL, "password");
|
||||
|
||||
try {
|
||||
//configure smpt server in the realm
|
||||
RealmRepresentation master = adminClient.realm(consumerRealmName()).toRepresentation();
|
||||
master.setSmtpServer(suiteContext.getSmtpServer());
|
||||
adminClient.realm(consumerRealmName()).update(master);
|
||||
|
||||
//create user on consumer's site who should be linked later
|
||||
UserRepresentation newUser = UserBuilder.create().username("consumer").email(USER_EMAIL).enabled(true).build();
|
||||
String userId = createUserWithAdminClient(adminClient.realm(consumerRealmName()), newUser);
|
||||
resetUserPassword(adminClient.realm(consumerRealmName()).users().get(userId), "password", false);
|
||||
|
||||
//test
|
||||
driver.navigate().to(getAccountUrl(consumerRealmName()));
|
||||
|
||||
log.debug("Clicking social " + getIDPAlias());
|
||||
accountLoginPage.clickSocial(getIDPAlias());
|
||||
|
||||
waitForPage("log in to");
|
||||
|
||||
Assert.assertTrue("Driver should be on the provider realm page right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + providerRealmName() + "/"));
|
||||
|
||||
log.debug("Logging in");
|
||||
accountLoginPage.login(getUserLogin(), getUserPassword());
|
||||
|
||||
waitForPage("update account information");
|
||||
|
||||
Assert.assertTrue(updateAccountInformationPage.isCurrent());
|
||||
Assert.assertTrue("We must be on correct realm right now",
|
||||
driver.getCurrentUrl().contains("/auth/realms/" + consumerRealmName() + "/"));
|
||||
|
||||
log.debug("Updating info on updateAccount page");
|
||||
updateAccountInformationPage.updateAccountInformation("Firstname", "Lastname");
|
||||
|
||||
//link account by email
|
||||
waitForPage("account already exists");
|
||||
idpConfirmLinkPage.clickLinkAccount();
|
||||
|
||||
String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, USER_EMAIL,
|
||||
"Someone wants to link your ", false);
|
||||
|
||||
log.info("navigating to url from email: " + url);
|
||||
driver.navigate().to(url);
|
||||
|
||||
//test if user is logged in
|
||||
assertEquals(accountPage.buildUri().toASCIIString().replace("master", "consumer") + "/", driver.getCurrentUrl());
|
||||
|
||||
//test if the user has verified email
|
||||
assertTrue(adminClient.realm(consumerRealmName()).users().get(userId).toRepresentation().isEmailVerified());
|
||||
} finally {
|
||||
// stop mail server
|
||||
MailServer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
// KEYCLOAK-3267
|
||||
@Test
|
||||
public void loginWithExistingUserWithBruteForceEnabled() {
|
||||
|
|
|
@ -61,6 +61,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
import org.keycloak.common.Profile;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -375,8 +376,10 @@ public class ExportImportUtil {
|
|||
Assert.assertNotNull(linked);
|
||||
Assert.assertEquals("my-service-user", linked.getUsername());
|
||||
|
||||
if (Profile.isPreviewEnabled()) {
|
||||
assertAuthorizationSettings(realmRsc);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isProtocolMapperGranted(Map<String, Object> consent, ProtocolMapperRepresentation mapperRep) {
|
||||
Map<String, List> grantedMappers = (Map<String, List>)consent.get("grantedProtocolMappers");
|
||||
|
|
|
@ -18,10 +18,7 @@ package org.keycloak.testsuite.forms;
|
|||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.*;
|
||||
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.events.Details;
|
||||
|
@ -34,6 +31,7 @@ import org.keycloak.representations.idm.AuthenticatorConfigRepresentation;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.util.ExecutionBuilder;
|
||||
import org.keycloak.testsuite.util.FlowBuilder;
|
||||
|
@ -62,6 +60,9 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
|
|||
|
||||
private AuthenticationFlowRepresentation flow;
|
||||
|
||||
@BeforeClass
|
||||
public static void enabled() { ProfileAssume.assumePreview(); }
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
||||
|
|
|
@ -30,10 +30,9 @@ import org.keycloak.testsuite.util.GreenMailRule;
|
|||
import javax.mail.MessagingException;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
|
||||
|
@ -84,6 +83,8 @@ public class EmailTest extends AbstractI18NTest {
|
|||
|
||||
@Test
|
||||
public void restPasswordEmailGerman() throws IOException, MessagingException {
|
||||
ProfileAssume.assumePreview();
|
||||
|
||||
changeUserLocale("de");
|
||||
|
||||
loginPage.open();
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.keycloak.testsuite.i18n;
|
||||
|
||||
import org.apache.http.impl.client.DefaultHttpClient;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClient;
|
||||
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
|
||||
import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
|
||||
|
@ -29,6 +28,7 @@ import org.keycloak.testsuite.pages.LoginPage;
|
|||
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.jboss.arquillian.graphene.page.Page;
|
||||
import org.keycloak.testsuite.ProfileAssume;
|
||||
import org.keycloak.testsuite.util.IdentityProviderBuilder;
|
||||
|
||||
/**
|
||||
|
@ -97,6 +97,8 @@ public class LoginPageTest extends AbstractI18NTest {
|
|||
|
||||
@Test
|
||||
public void acceptLanguageHeader() {
|
||||
ProfileAssume.assumePreview();
|
||||
|
||||
DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
|
||||
ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
|
||||
ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();
|
||||
|
|
|
@ -41,7 +41,8 @@ public class MailAssert {
|
|||
message= SslMailServer.getLastReceivedMessage();
|
||||
} else {
|
||||
message = MailServer.getLastReceivedMessage();
|
||||
} assertNotNull("There is no received email.", message);
|
||||
}
|
||||
assertNotNull("There is no received email.", message);
|
||||
assertEquals(recipient, message.getRecipients(RecipientType.TO)[0].toString());
|
||||
assertEquals(from, message.getFrom()[0].toString());
|
||||
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
{
|
||||
"realm": "demo",
|
||||
"enabled": true,
|
||||
"accessTokenLifespan": 60,
|
||||
"accessCodeLifespan": 60,
|
||||
"accessCodeLifespanUserAction": 300,
|
||||
"ssoSessionIdleTimeout": 600,
|
||||
"ssoSessionMaxLifespan": 36000,
|
||||
"sslRequired": "external",
|
||||
"registrationAllowed": false,
|
||||
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
|
||||
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
|
||||
"requiredCredentials": [ "password" ],
|
||||
"users" : [
|
||||
{
|
||||
"username" : "bburke@redhat.com",
|
||||
"enabled": true,
|
||||
"email" : "bburke@redhat.com",
|
||||
"firstName": "Bill",
|
||||
"lastName": "Burke",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": [ "user" ],
|
||||
"clientRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "stian",
|
||||
"enabled": true,
|
||||
"email" : "stian@redhat.com",
|
||||
"firstName": "Stian",
|
||||
"lastName": "Thorgersen",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": [ "user" ],
|
||||
"clientRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "mposolda@redhat.com",
|
||||
"enabled": true,
|
||||
"email" : "mposolda@redhat.com",
|
||||
"firstName": "Marek",
|
||||
"lastName": "Posolda",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": [ "user" ],
|
||||
"clientRoles": {
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "admin",
|
||||
"enabled": true,
|
||||
"email" : "admin@admin.com",
|
||||
"firstName": "Admin",
|
||||
"lastName": "Burke",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": [ "user","admin" ],
|
||||
"clientRoles": {
|
||||
"realm-management": [ "realm-admin" ],
|
||||
"account": [ "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "jmxadmin",
|
||||
"enabled": true,
|
||||
"email" : "jmxadmin@admin.com",
|
||||
"firstName": "JmxAdmin",
|
||||
"lastName": "Burke",
|
||||
"credentials" : [
|
||||
{ "type" : "password",
|
||||
"value" : "password" }
|
||||
],
|
||||
"realmRoles": [ "user", "jmxAdmin" ],
|
||||
"clientRoles": {
|
||||
"account": [ "manage-account" ],
|
||||
"realm-management": [ "realm-admin" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "john",
|
||||
"firstName" : "John",
|
||||
"lastName" : "Anthony",
|
||||
"email" : "john@hawt.io",
|
||||
"enabled" : true,
|
||||
"credentials" : [
|
||||
{
|
||||
"type" : "password",
|
||||
"value" : "password"
|
||||
}
|
||||
],
|
||||
"realmRoles" : [ "viewer" ],
|
||||
"applicationRoles": {
|
||||
"account" : [ "view-profile", "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "mary",
|
||||
"firstName" : "Mary",
|
||||
"lastName" : "Kelly",
|
||||
"email" : "mary@hawt.io",
|
||||
"enabled" : true,
|
||||
"credentials" : [
|
||||
{
|
||||
"type" : "password",
|
||||
"value" : "password"
|
||||
}
|
||||
],
|
||||
"applicationRoles": {
|
||||
"account" : [ "view-profile", "manage-account" ]
|
||||
}
|
||||
},
|
||||
{
|
||||
"username" : "root",
|
||||
"firstName" : "Root",
|
||||
"lastName" : "Root",
|
||||
"email" : "root@hawt.io",
|
||||
"enabled" : true,
|
||||
"credentials" : [
|
||||
{
|
||||
"type" : "password",
|
||||
"value" : "password"
|
||||
}
|
||||
],
|
||||
"realmRoles" : [ "jmxAdmin" ],
|
||||
"applicationRoles": {
|
||||
"account" : [ "view-profile", "manage-account" ],
|
||||
"realm-management" : [ "realm-admin" ]
|
||||
}
|
||||
}
|
||||
],
|
||||
"roles" : {
|
||||
"realm" : [
|
||||
{
|
||||
"name": "user",
|
||||
"description": "User privileges"
|
||||
},
|
||||
{
|
||||
"name": "admin",
|
||||
"description": "Administrator privileges"
|
||||
},
|
||||
{
|
||||
"name": "manager"
|
||||
},
|
||||
{
|
||||
"name": "viewer"
|
||||
},
|
||||
{
|
||||
"name": "Operator"
|
||||
},
|
||||
{
|
||||
"name": "Maintainer"
|
||||
},
|
||||
{
|
||||
"name": "Deployer"
|
||||
},
|
||||
{
|
||||
"name": "Auditor"
|
||||
},
|
||||
{
|
||||
"name": "Administrator"
|
||||
},
|
||||
{
|
||||
"name": "SuperUser"
|
||||
},
|
||||
{
|
||||
"name": "jmxAdmin",
|
||||
"description": "Admin role with all privileges to SSH and JMX access",
|
||||
"composite": true,
|
||||
"composites": {
|
||||
"realm": [ "admin", "manager", "viewer", "Operator", "Maintainer", "Deployer", "Auditor", "Administrator", "SuperUser" ]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"clients": [
|
||||
{
|
||||
"clientId": "customer-portal",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/customer-portal",
|
||||
"baseUrl": "http://localhost:8181/customer-portal",
|
||||
"redirectUris": [
|
||||
"http://localhost:8181/customer-portal/*"
|
||||
],
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId": "example-camel-cdi",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8080/example-camel-cdi",
|
||||
"baseUrl": "http://localhost:8080/example-camel-cdi",
|
||||
"redirectUris": [
|
||||
"http://localhost:8080/example-camel-cdi/*"
|
||||
],
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId": "product-portal",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/product-portal",
|
||||
"baseUrl": "http://localhost:8181/product-portal",
|
||||
"redirectUris": [
|
||||
"http://localhost:8181/product-portal/*"
|
||||
],
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId": "builtin-cxf-app",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/cxf",
|
||||
"baseUrl": "http://localhost:8181/cxf",
|
||||
"redirectUris": [
|
||||
"http://localhost:8181/cxf/*"
|
||||
],
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId": "custom-cxf-endpoint",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8282/PersonServiceCF",
|
||||
"baseUrl": "http://localhost:8282/PersonServiceCF",
|
||||
"bearerOnly": true
|
||||
},
|
||||
{
|
||||
"clientId": "admin-camel-endpoint",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8383/admin-camel-endpoint",
|
||||
"baseUrl": "http://localhost:8383/admin-camel-endpoint",
|
||||
"bearerOnly": true
|
||||
},
|
||||
{
|
||||
"clientId": "ssh-jmx-admin-client",
|
||||
"enabled": true,
|
||||
"publicClient": false,
|
||||
"standardFlowEnabled": false,
|
||||
"directAccessGrantsEnabled": true,
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId": "external-config",
|
||||
"enabled": true,
|
||||
"adminUrl": "http://localhost:8181/external-config",
|
||||
"baseUrl": "http://localhost:8181/external-config",
|
||||
"redirectUris": [
|
||||
"http://localhost:8181/external-config",
|
||||
"http://localhost:8181/external-config/*"
|
||||
],
|
||||
"secret": "password"
|
||||
},
|
||||
{
|
||||
"clientId" : "hawtio-client",
|
||||
"surrogateAuthRequired" : false,
|
||||
"fullScopeAllowed" : false,
|
||||
"enabled" : true,
|
||||
"redirectUris" : ["http://localhost:8080/hawtio/*" ],
|
||||
"webOrigins" : [ "http://localhost:8080"],
|
||||
"bearerOnly" : false,
|
||||
"publicClient" : true,
|
||||
"protocol" : "openid-connect"
|
||||
}
|
||||
|
||||
],
|
||||
"scopeMappings": [
|
||||
{
|
||||
"client": "ssh-jmx-admin-client",
|
||||
"roles": [ "admin", "jmxAdmin" ]
|
||||
},
|
||||
{
|
||||
"client": "hawtio-client",
|
||||
"roles": [ "viewer", "jmxAdmin" ]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -67,6 +67,7 @@
|
|||
-Djboss.bind.address=0.0.0.0
|
||||
${adapter.test.props}
|
||||
${migration.import.properties}
|
||||
${auth.server.profile}
|
||||
</property>
|
||||
<property name="javaVmArguments">
|
||||
${auth.server.memory.settings}
|
||||
|
@ -89,6 +90,7 @@
|
|||
-Djboss.socket.binding.port-offset=${auth.server.backend1.port.offset}
|
||||
-Djboss.node.name=node1
|
||||
${adapter.test.props}
|
||||
${auth.server.profile}
|
||||
</property>
|
||||
<property name="javaVmArguments">
|
||||
${auth.server.memory.settings}
|
||||
|
@ -109,6 +111,7 @@
|
|||
-Djboss.socket.binding.port-offset=${auth.server.backend2.port.offset}
|
||||
-Djboss.node.name=node2
|
||||
${adapter.test.props}
|
||||
${auth.server.profile}
|
||||
</property>
|
||||
<property name="javaVmArguments">
|
||||
${auth.server.memory.settings}
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.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.
|
||||
-->
|
||||
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.keycloak.testsuite</groupId>
|
||||
<artifactId>integration-arquillian-tests-adapters-jboss</artifactId>
|
||||
<version>2.4.0.CR1-SNAPSHOT</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>integration-arquillian-tests-adapters-eap6-fuse</artifactId>
|
||||
|
||||
<name>Adapter Tests - JBoss - EAP 6</name>
|
||||
|
||||
<properties>
|
||||
<app.server>eap6-fuse</app.server>
|
||||
|
||||
<app.server.management.protocol>remote</app.server.management.protocol>
|
||||
<app.server.management.port>${app.server.management.port.jmx}</app.server.management.port>
|
||||
</properties>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,10 @@
|
|||
package org.keycloak.testsuite.adapter.example;
|
||||
|
||||
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
|
||||
|
||||
/**
|
||||
* @author mhajas
|
||||
*/
|
||||
@AppServerContainer("app-server-eap6-fuse")
|
||||
public class EAP6FUSEHawtioAdapterTest extends AbstractHawtioAdapterTest {
|
||||
}
|
|
@ -177,7 +177,12 @@
|
|||
<module>relative</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>app-server-eap6-fuse</id>
|
||||
<modules>
|
||||
<module>eap6-fuse</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -88,6 +88,7 @@ public class AdminEventsTest extends AbstractConsoleTest {
|
|||
assertEquals(1, resultList.size());
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='CREATE']"));
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='clients/" + id + "']"));
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='CLIENT']"));
|
||||
|
||||
adminEventsPage.table().reset();
|
||||
adminEventsPage.table().filterForm().addOperationType("UPDATE");
|
||||
|
@ -97,6 +98,7 @@ public class AdminEventsTest extends AbstractConsoleTest {
|
|||
assertEquals(1, resultList.size());
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='UPDATE']"));
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='clients/" + id + "']"));
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='CLIENT']"));
|
||||
|
||||
adminEventsPage.table().reset();
|
||||
adminEventsPage.table().filterForm().addOperationType("DELETE");
|
||||
|
@ -106,6 +108,7 @@ public class AdminEventsTest extends AbstractConsoleTest {
|
|||
assertEquals(1, resultList.size());
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='DELETE']"));
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='clients/" + id + "']"));
|
||||
resultList.get(0).findElement(By.xpath("//td[text()='CLIENT']"));
|
||||
}
|
||||
|
||||
public ClientsResource clientsResource() {
|
||||
|
|
|
@ -63,6 +63,7 @@
|
|||
<auth.server.jboss.startup.timeout>300</auth.server.jboss.startup.timeout>
|
||||
|
||||
<auth.server.remote>false</auth.server.remote>
|
||||
<auth.server.profile/>
|
||||
|
||||
<adapter.test.props/>
|
||||
<migration.import.properties/>
|
||||
|
@ -163,6 +164,8 @@
|
|||
<auth.server.config.property.value>${auth.server.config.property.value}</auth.server.config.property.value>
|
||||
<auth.server.adapter.impl.class>${auth.server.adapter.impl.class}</auth.server.adapter.impl.class>
|
||||
|
||||
<auth.server.profile>${auth.server.profile}</auth.server.profile>
|
||||
|
||||
<frontend.console.output>${frontend.console.output}</frontend.console.output>
|
||||
<backends.console.output>${backend.console.output}</backends.console.output>
|
||||
|
||||
|
@ -246,6 +249,18 @@
|
|||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-profile</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>keycloak.profile</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<auth.server.profile>-Dkeycloak.profile=${keycloak.profile}</auth.server.profile>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>auth-server-cluster</id>
|
||||
<properties>
|
||||
|
@ -412,16 +427,16 @@
|
|||
<profile>
|
||||
<id>auth-server-migration</id>
|
||||
<properties>
|
||||
<migration.import.file>src/test/resources/migration-test/migration-realm-${migrated.auth.server.version}.json</migration.import.file>
|
||||
<migration.import.props.previous>
|
||||
-Dkeycloak.migration.action=import
|
||||
-Dkeycloak.migration.provider=singleFile
|
||||
-Dkeycloak.migration.file=src/test/resources/migration-test/migration-realm-${migrated.auth.server.version}.json
|
||||
-Dkeycloak.migration.file=${migration.import.file}
|
||||
-Dkeycloak.migration.strategy=OVERWRITE_EXISTING
|
||||
</migration.import.props.previous>
|
||||
<skip.add.user.json>true</skip.add.user.json>
|
||||
</properties>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
|
@ -479,7 +494,6 @@
|
|||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
|
@ -492,16 +506,16 @@
|
|||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<migration.import.file>src/test/resources/migration-test/migration-realm-${migrated.auth.server.version}.json</migration.import.file>
|
||||
<migration.import.properties>
|
||||
-Dkeycloak.migration.action=import
|
||||
-Dkeycloak.migration.provider=singleFile
|
||||
-Dkeycloak.migration.file=src/test/resources/migration-test/migration-realm-${migrated.auth.server.version}.json
|
||||
-Dkeycloak.migration.file=${migration.import.file}
|
||||
-Dkeycloak.migration.strategy=OVERWRITE_EXISTING
|
||||
</migration.import.properties>
|
||||
<skip.add.user.json>true</skip.add.user.json>
|
||||
</properties>
|
||||
<build>
|
||||
<pluginManagement>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-enforcer-plugin</artifactId>
|
||||
|
@ -530,10 +544,21 @@
|
|||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>migration-productized</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>migrated.version.import.file.suffix</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<migration.import.file>src/test/resources/migration-test/migration-realm-${migrated.version.import.file.suffix}.json</migration.import.file>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>no-account</id>
|
||||
<properties>
|
||||
|
|
|
@ -956,7 +956,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
|
|||
id: $scope.identityProvider.internalId
|
||||
}, $scope.identityProvider, function () {
|
||||
$route.reload();
|
||||
Notifications.success("The " + $scope.identityProvider.alias + " provider has been update.");
|
||||
Notifications.success("The " + $scope.identityProvider.alias + " provider has been updated.");
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
|
@ -222,8 +222,11 @@
|
|||
<input class="form-control" id="fromUrl" type="text" ng-model="fromUrl.data">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'identity-provider.import-from-url.tooltip' | translate}}</kc-tooltip>
|
||||
<div class="col-md-6" data-ng-show="importUrl">
|
||||
<button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="importFrom"></label>
|
||||
<div class="col-md-6">
|
||||
<button id="importFrom" type="button" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="newIdentityProvider">
|
||||
|
@ -238,8 +241,11 @@
|
|||
{{files[0].name}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="importFile"></label>
|
||||
<div class="col-md-6" data-ng-show="importFile">
|
||||
<button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
<button id="importFile" type="button" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -191,8 +191,11 @@
|
|||
<input class="form-control" id="fromUrl" type="text" ng-model="fromUrl.data">
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'saml.import-from-url.tooltip' | translate}}</kc-tooltip>
|
||||
<div class="col-sm-4" data-ng-show="importUrl">
|
||||
<button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="importFrom"></label>
|
||||
<div class="col-md-6">
|
||||
<button id="importFrom" type="button" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" data-ng-show="newIdentityProvider">
|
||||
|
@ -206,8 +209,11 @@
|
|||
{{files[0].name}}
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-sm-4" data-ng-show="importFile">
|
||||
<button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="importFile"></label>
|
||||
<div class="col-sm-6" data-ng-show="importFile">
|
||||
<button id="importFile" type="button" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">{{:: 'import' | translate}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
|
@ -244,12 +244,9 @@ ol#kc-totp-settings li:first-of-type {
|
|||
.zocial.microsoft {background-color: #0052a4; color: #fff;}
|
||||
.zocial.microsoft:before { content: "\f15d"; }
|
||||
|
||||
|
||||
@media (min-width: 768px) {
|
||||
@media (min-width: 1281px) {
|
||||
#kc-container-wrapper {
|
||||
bottom: 13%;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#kc-logo-wrapper {
|
||||
|
@ -257,6 +254,17 @@ ol#kc-totp-settings li:first-of-type {
|
|||
top: 50px;
|
||||
right: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
#kc-container-wrapper {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#kc-logo-wrapper {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.login-pf .container {
|
||||
padding-right: 80px;
|
||||
|
|
Loading…
Reference in a new issue