keycloak-uncached
Changes
connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectMapper.java 2(+1 -1)
examples/fuse/fuse-admin/README.md 42(+17 -25)
examples/fuse/testrealm.json 50(+49 -1)
integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java 26(+25 -1)
Details
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectMapper.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectMapper.java
index 462b064..102cf21 100644
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectMapper.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/impl/types/BasicDBObjectMapper.java
@@ -9,7 +9,7 @@ import org.keycloak.connections.mongo.api.types.MapperRegistry;
import org.keycloak.connections.mongo.impl.EntityInfo;
import org.keycloak.connections.mongo.impl.MongoStoreImpl;
import org.keycloak.models.utils.reflection.Property;
-import org.keycloak.models.utils.reflection.Types;
+import org.keycloak.util.reflections.Types;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
diff --git a/distribution/osgi/jaas/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/distribution/osgi/jaas/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 793c6dd..562fcff 100644
--- a/distribution/osgi/jaas/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/distribution/osgi/jaas/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -10,7 +10,10 @@
<cm:property-placeholder persistent-id="org.keycloak" update-strategy="reload">
<cm:default-properties>
<cm:property name="jaasBearerKeycloakConfigFile" value="$[karaf.base]/etc/keycloak-hawtio.json"/>
+ <cm:property name="jaasBearerRolePrincipalClass" value=""/>
+
<cm:property name="jaasDirectAccessKeycloakConfigFile" value="$[karaf.base]/etc/keycloak-direct-access.json"/>
+ <cm:property name="jaasDirectAccessRolePrincipalClass" value="org.apache.karaf.jaas.boot.principal.RolePrincipal"/>
</cm:default-properties>
</cm:property-placeholder>
@@ -20,12 +23,14 @@
<jaas:module className="org.keycloak.adapters.jaas.BearerTokenLoginModule"
flags="sufficient">
keycloak-config-file = ${jaasBearerKeycloakConfigFile}
+ role-principal-class = ${jaasBearerRolePrincipalClass}
</jaas:module>
<!-- Used for direct username/password authentication for non-web scenarios (like ssh) -->
<jaas:module className="org.keycloak.adapters.jaas.DirectAccessGrantsLoginModule"
flags="sufficient">
keycloak-config-file = ${jaasDirectAccessKeycloakConfigFile}
+ role-principal-class = ${jaasDirectAccessRolePrincipalClass}
</jaas:module>
</jaas:config>
diff --git a/docbook/reference/en/en-US/modules/fuse-adapter.xml b/docbook/reference/en/en-US/modules/fuse-adapter.xml
index 22224c1..8127477 100644
--- a/docbook/reference/en/en-US/modules/fuse-adapter.xml
+++ b/docbook/reference/en/en-US/modules/fuse-adapter.xml
@@ -35,6 +35,16 @@
Security for <ulink url="http://cxf.apache.org/">Apache CXF</ulink> endpoints running on default engine provided by CXF servlet.
</para>
</listitem>
+ <listitem>
+ <para>
+ Security for SSH and JMX admin access.
+ </para>
+ </listitem>
+ <listitem>
+ <para>
+ Security for <ulink url="http://hawt.io/">Hawt.io admin console</ulink> .
+ </para>
+ </listitem>
</itemizedlist>
</para>
<para>The best place to start is look at Fuse demo bundled as part of Keycloak examples in directory <literal>examples/fuse</literal> .</para>
diff --git a/docbook/reference/en/en-US/modules/jaas.xml b/docbook/reference/en/en-US/modules/jaas.xml
index 802dfcb..c07f4c6 100644
--- a/docbook/reference/en/en-US/modules/jaas.xml
+++ b/docbook/reference/en/en-US/modules/jaas.xml
@@ -31,7 +31,12 @@
</variablelist>
</para>
<para>
- Both login modules have single configuration property <literal>keycloak-config-file</literal> where you need to provide location of keycloak.json configuration file.
+ Both login modules have configuration property <literal>keycloak-config-file</literal> where you need to provide location of keycloak.json configuration file.
It could be either provided from filesystem or from classpath (in that case you may need value like <literal>classpath:/folder-on-classpath/keycloak.json</literal> ).
</para>
+ <para>
+ Second property <literal>role-principal-class</literal> allows to specify alternative class for Role principals attached to JAAS Subject. Default
+ value for Role principal is <literal>org.keycloak.adapters.jaas.RolePrincipal</literal> . Note that class should have constructor
+ with single String argument.
+ </para>
</section>
\ No newline at end of file
examples/fuse/fuse-admin/README.md 42(+17 -25)
diff --git a/examples/fuse/fuse-admin/README.md b/examples/fuse/fuse-admin/README.md
index 6c2b275..d22d45d 100644
--- a/examples/fuse/fuse-admin/README.md
+++ b/examples/fuse/fuse-admin/README.md
@@ -15,14 +15,13 @@ allows to remotely connect to Keycloak and verify credentials based on [Direct a
Example steps for enable SSH authentication:
-1) Import 'demo' realm as mentioned in [Base steps](../README.md#base-steps) . It contains client `sh-jmx-admin-client`, which is used for SSH authentication.
+1) Import 'demo' realm as mentioned in [Base steps](../README.md#base-steps) . It contains client `ssh-jmx-admin-client`, which is used for SSH authentication.
Skip this step if you installed demo already.
-2) Then you need to update/specify these 2 properties in file `$FUSE_HOME/etc/org.apache.karaf.shell.cfg`:
+2) Then you need to update/specify this property in file `$FUSE_HOME/etc/org.apache.karaf.shell.cfg`:
```
sshRealm=keycloak
-sshRole=org.keycloak.adapters.jaas.RolePrincipal:admin
```
3) Copy file from Keycloak fuse examples `examples/fuse/fuse-admin/keycloak-direct-access.json` to `$FUSE_HOME/etc/` directory.
@@ -30,7 +29,7 @@ This file contains configuration of the client application, which is used by JAA
4) Start Fuse and install `keycloak` JAAS realm into Fuse. This could be done easily by installing `keycloak-jaas` feature, which has JAAS realm predefined
(you are able to override it by using your own `keycloak` JAAS realm with higher ranking). As long as you already installed `keycloak-fuse-example` feature as mentioned
-in [examples readme](../README.md), you can skip this step as `keycloak-jaas` is installed already. Otherwise use those commands:
+in [examples readme](../README.md), you can skip this step as `keycloak-jaas` is installed already. Otherwise use those commands (replace Keycloak version with current one):
```
features:addurl mvn:org.keycloak/keycloak-osgi-features/1.1.0.Final/xml/features
@@ -49,7 +48,7 @@ And login with password `password` . Note that other users from "demo" realm lik
JMX authentication with keycloak credentials on JBoss Fuse 6.1
--------------------------------------------------------------
-This may be needed just in case if you really want to use jconsole or other external tool to perform remote connection to JMX through RMI. Otherwise it may
+This may be needed in case if you really want to use jconsole or other external tool to perform remote connection to JMX through RMI. Otherwise it may
be better to use just hawt.io/jolokia as jolokia agent is installed in hawt.io by default.
1) In file `$FUSE_HOME/etc/org.apache.karaf.management.cfg` you can change these 2 properties:
@@ -72,30 +71,23 @@ may be still able to access MBeans remotely via HTTP (Hawtio). So make sure to p
really protect JMX mbeans.
-JMX on JBoss Fuse 6.2 and Apache Karaf 3.0.2
+SSH and JMX on JBoss Fuse 6.2 and Apache Karaf 3.0.2
--------------------------------------------
-
-For JMX, the steps are similar like above, however there is more fine grained authorization for JMX access in Fuse 6.2. You still need to configure jmxRealm in
-`$FUSE_HOME/etc/org.apache.karaf.management.cfg`, however jmxRole is not needed. So in config should be just this:
+For SSH steps are very similar to above for 6.1. In JBoss Fuse 6.2 you may need to install `ssh` feature as it doesn't seem to be installed here by default.
```
-jmxRealm=keycloak
+features:install ssh
```
-And then you need to configure authorization for all specific MBeans, which you want to access with authenticated user. For generic access
-you can configure the most generic ACL in file `$FUSE_HOME/etc/jmx.acl.cfg` like this:
-```
-list* = org.keycloak.adapters.jaas.RolePrincipal:admin
-get* = org.keycloak.adapters.jaas.RolePrincipal:admin
-is* = org.keycloak.adapters.jaas.RolePrincipal:admin
-set* = org.keycloak.adapters.jaas.RolePrincipal:admin
-* = org.keycloak.adapters.jaas.RolePrincipal:admin
-```
+For JMX, the steps are similar like for Fuse 6.1, however there is more fine grained authorization for JMX access in Fuse 6.2 and Karaf 3.
+You need to install just jmxRealm in `$FUSE_HOME/etc/org.apache.karaf.management.cfg` . Property `jmxRole` is no longer valid.
-Then admin will have permissions to see all the attributes and trigger all JMX operations, which don't have their own ACL. You can configure
-those specific ACLs too. For example if you want to allow operation `lookupAgents` in MBean `jolokia:type=Discovery` you can configure
-in file `$FUSE_HOME/etc/jmx.acl.jolokia.Discovery.cfg` the property like this:
-
-```
-lookupAgents = org.keycloak.adapters.jaas.RolePrincipal:admin
```
+jmxRealm=keycloak
+```
+
+Actually if you login as user `admin`, you have very limited privileges without possibility to do much JMX operations as this user has just `admin` role, which is not allowed to do much in JMX.
+
+However if you login as user `jmxadmin` with password `password`, you will have all JMX privileges! This user has composite role `jmxAdmin`, which is mapped to
+all possible roles used in JMX authorization files like `etc/jmx.acl.*.cfg` . See karaf documentation for more info about fine grained JMX authorization.
+
examples/fuse/testrealm.json 50(+49 -1)
diff --git a/examples/fuse/testrealm.json b/examples/fuse/testrealm.json
index 97bb3fa..4f7cfea 100644
--- a/examples/fuse/testrealm.json
+++ b/examples/fuse/testrealm.json
@@ -74,6 +74,22 @@
"applicationRoles": {
"realm-management": [ "realm-admin" ]
}
+ },
+ {
+ "username" : "jmxadmin",
+ "enabled": true,
+ "email" : "jmxadmin@admin.com",
+ "firstName": "JmxAdmin",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user", "jmxAdmin" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ],
+ "realm-management": [ "realm-admin" ]
+ }
}
],
"roles" : {
@@ -85,6 +101,38 @@
{
"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" ]
+ }
}
]
},
@@ -151,7 +199,7 @@
"scopeMappings": [
{
"client": "ssh-jmx-admin-client",
- "roles": ["admin"]
+ "roles": [ "admin", "jmxAdmin" ]
}
]
}
\ No newline at end of file
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
index ac3f276..a5b56fe 100644
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/AbstractKeycloakLoginModule.java
@@ -1,6 +1,7 @@
package org.keycloak.adapters.jaas;
import java.io.InputStream;
+import java.lang.reflect.Constructor;
import java.security.Principal;
import java.util.HashSet;
import java.util.Map;
@@ -28,6 +29,7 @@ import org.keycloak.adapters.KeycloakDeployment;
import org.keycloak.adapters.KeycloakDeploymentBuilder;
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
import org.keycloak.representations.AccessToken;
+import org.keycloak.util.reflections.Reflections;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@@ -35,11 +37,13 @@ import org.keycloak.representations.AccessToken;
public abstract class AbstractKeycloakLoginModule implements LoginModule {
public static final String KEYCLOAK_CONFIG_FILE_OPTION = "keycloak-config-file";
+ public static final String ROLE_PRINCIPAL_CLASS_OPTION = "role-principal-class";
protected Subject subject;
protected CallbackHandler callbackHandler;
protected Auth auth;
protected KeycloakDeployment deployment;
+ protected String rolePrincipalClass;
// This is to avoid parsing keycloak.json file in each request. Key is file location, Value is parsed keycloak deployment
private static ConcurrentMap<String, KeycloakDeployment> deployments = new ConcurrentHashMap<String, KeycloakDeployment>();
@@ -50,6 +54,9 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
this.callbackHandler = callbackHandler;
String configFile = (String)options.get(KEYCLOAK_CONFIG_FILE_OPTION);
+ rolePrincipalClass = (String)options.get(ROLE_PRINCIPAL_CLASS_OPTION);
+ getLogger().debug("Declared options: " + KEYCLOAK_CONFIG_FILE_OPTION + "=" + configFile + ", " + ROLE_PRINCIPAL_CLASS_OPTION + "=" + rolePrincipalClass);
+
if (configFile != null) {
deployment = deployments.get(configFile);
if (deployment == null) {
@@ -117,7 +124,7 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
this.subject.getPrivateCredentials().add(auth.getTokenString());
if (auth.getRoles() != null) {
for (String roleName : auth.getRoles()) {
- RolePrincipal rolePrinc = new RolePrincipal(roleName);
+ Principal rolePrinc = createRolePrincipal(roleName);
this.subject.getPrincipals().add(rolePrinc);
}
}
@@ -125,6 +132,23 @@ public abstract class AbstractKeycloakLoginModule implements LoginModule {
return true;
}
+
+ protected Principal createRolePrincipal(String roleName) {
+ if (rolePrincipalClass != null && rolePrincipalClass.length() > 0) {
+ try {
+ Class<Principal> clazz = Reflections.classForName(rolePrincipalClass, getClass().getClassLoader());
+ Constructor<Principal> constructor = clazz.getDeclaredConstructor(String.class);
+ return constructor.newInstance(roleName);
+ } catch (Exception e) {
+ getLogger().warn("Unable to create declared roleClass " + rolePrincipalClass + " due to " + e.getMessage());
+ }
+ }
+
+ // Fallback to default rolePrincipal class
+ return new RolePrincipal(roleName);
+ }
+
+
@Override
public boolean abort() throws LoginException {
return true;
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
index 1d070cf..c731bbb 100755
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
@@ -118,6 +118,7 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
started = true;
}
+ // TODO: Use 'Reflections.classForName'
protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
try {
return (Class<? extends KeycloakConfigResolver>)getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java b/model/api/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java
index 16cc165..6fa8786 100644
--- a/model/api/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/reflection/MethodPropertyImpl.java
@@ -6,6 +6,8 @@ import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
+import org.keycloak.util.reflections.Reflections;
+
/**
* A bean property based on the value represented by a getter/setter method pair
*/