Skip to content

Commit

Permalink
ISPN-13019 Distributed security realm
Browse files Browse the repository at this point in the history
  • Loading branch information
tristantarrant committed May 10, 2021
1 parent 24ba82a commit 8966502
Show file tree
Hide file tree
Showing 16 changed files with 387 additions and 36 deletions.
Expand Up @@ -10,6 +10,7 @@ include::{topics}/ref_server_realm_ldap.adoc[leveloffset=+1]
include::{topics}/ref_server_realm_ldap_rewrite.adoc[leveloffset=+2]
include::{topics}/ref_server_realm_trust.adoc[leveloffset=+1]
include::{topics}/ref_server_realm_token.adoc[leveloffset=+1]
include::{topics}/ref_server_realm_distributed.adoc[leveloffset=+1]

// Restore the parent context.
ifdef::parent-context[:context: {parent-context}]
Expand Down
@@ -0,0 +1,15 @@
<security xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:infinispan:server:{schemaversion} https://infinispan.org/schemas/infinispan-server-{schemaversion}.xsd"
xmlns="urn:infinispan:server:{schemaversion}">
<security-realms>
<security-realm name="default">
<ldap-realm>
<!-- ... -->
</ldap-realm>
<properties-realm>
<!-- ... -->
</properties-realm>
<distributed-realm realms="ldap properties"/>
</security-realm>
</security-realms>
</security>
@@ -0,0 +1,14 @@
[id='distributed_realm-{context}']
= Distributed Realms
A security realm definition for authentication and authorization identities distributed between multiple security realms.

.Distributed realm configuration

[source,xml,options="nowrap",subs=attributes+]
----
include::config_examples/server_distributed_realm.xml[]
----

.Supported authentication mechanisms

Distributed realms support the authentication mechanisms of the underlying realms.
Expand Up @@ -14,6 +14,8 @@ public enum Attribute {
AUDIENCE,
AUTH_SERVER_URL,
CACHE_CONTAINER,
CACHE_LIFESPAN,
CACHE_MAX_SIZE,
CLIENT_ID,
CLIENT_SECRET,
CLIENT_SSL_CONTEXT,
Expand All @@ -22,6 +24,7 @@ public enum Attribute {
CREDENTIAL,
DEBUG,
DEFAULT_INTERFACE,
DEFAULT_REALM,
DIGEST_REALM_NAME,
DIRECT_VERIFICATION,
ENABLED_CIPHERSUITES,
Expand Down Expand Up @@ -63,6 +66,7 @@ public enum Attribute {
PUBLIC_KEY,
RDN_IDENTIFIER,
READ_TIMEOUT,
REALMS,
RECEIVE_BUFFER_SIZE,
REFERENCE,
REFERRAL_MODE,
Expand Down
Expand Up @@ -14,6 +14,10 @@ public enum Element {
ATTRIBUTE,
ATTRIBUTE_MAPPING,
ATTRIBUTE_REFERENCE,
BINDINGS,
CONNECTION_PROPERTIES,
CONNECTORS,
DISTRIBUTED_REALM,
ENDPOINTS,
ENGINE,
FILESYSTEM_REALM,
Expand Down Expand Up @@ -69,9 +73,7 @@ public enum Element {
IP_FILTER,
ACCEPT,
REJECT,
BINDINGS,
CONNECTION_PROPERTIES,
CONNECTORS;
;

private static final Map<String, Element> ELEMENTS;

Expand Down
Expand Up @@ -20,6 +20,7 @@
import org.infinispan.server.configuration.endpoint.EndpointConfigurationBuilder;
import org.infinispan.server.configuration.security.CredentialStoreConfigurationBuilder;
import org.infinispan.server.configuration.security.CredentialStoresConfigurationBuilder;
import org.infinispan.server.configuration.security.DistributedRealmConfigurationBuilder;
import org.infinispan.server.configuration.security.FileSystemRealmConfigurationBuilder;
import org.infinispan.server.configuration.security.GroupsPropertiesConfigurationBuilder;
import org.infinispan.server.configuration.security.JwtConfigurationBuilder;
Expand Down Expand Up @@ -101,8 +102,7 @@ public void readElement(ConfigurationReader reader, ConfigurationBuilderHolder h
}
}

private void parseServerElements(ConfigurationReader reader, ConfigurationBuilderHolder holder, ServerConfigurationBuilder builder)
{
private void parseServerElements(ConfigurationReader reader, ConfigurationBuilderHolder holder, ServerConfigurationBuilder builder) {
Element element = nextElement(reader);
if (element == Element.INTERFACES) {
parseInterfaces(reader, builder);
Expand Down Expand Up @@ -346,7 +346,7 @@ private void parseCredentialStore(ConfigurationReader reader, ServerConfiguratio
}

private String parseCredentialReference(ConfigurationReader reader, ServerConfigurationBuilder builder) {
switch(Element.forName(reader.getLocalName())) {
switch (Element.forName(reader.getLocalName())) {
case CREDENTIAL_REFERENCE: {
String store = null;
String alias = null;
Expand Down Expand Up @@ -396,6 +396,25 @@ private void parseSecurityRealms(ConfigurationReader reader, ServerConfiguration
private void parseSecurityRealm(ConfigurationReader reader, ServerConfigurationBuilder builder, RealmsConfigurationBuilder realms) {
String name = ParseUtils.requireAttributes(reader, Attribute.NAME)[0];
RealmConfigurationBuilder securityRealmBuilder = realms.addSecurityRealm(name);
for(int i = 0; i < reader.getAttributeCount(); i++) {
ParseUtils.requireNoNamespaceAttribute(reader, i);
String value = reader.getAttributeValue(i);
Attribute attribute = Attribute.forName(reader.getAttributeName(i));
switch (attribute) {
case NAME:
// Already seen
break;
case DEFAULT_REALM:
securityRealmBuilder.defaultRealm(value);
break;
case CACHE_MAX_SIZE:
securityRealmBuilder.cacheMaxSize(Integer.parseInt(value));
break;
case CACHE_LIFESPAN:
securityRealmBuilder.cacheLifespan(Long.parseLong(value));
break;
}
}
Element element = nextElement(reader);
if (element == Element.SERVER_IDENTITIES) {
parseServerIdentities(reader, builder, securityRealmBuilder);
Expand Down Expand Up @@ -429,6 +448,10 @@ private void parseSecurityRealm(ConfigurationReader reader, ServerConfigurationB
}
element = nextElement(reader);
}
if (element == Element.DISTRIBUTED_REALM) {
parseDistributedRealm(reader, securityRealmBuilder.distributedConfiguration());
element = nextElement(reader);
}
if (element != null) {
throw ParseUtils.unexpectedElement(reader, element);
}
Expand Down Expand Up @@ -899,6 +922,28 @@ private void parsePropertiesRealm(ConfigurationReader reader, PropertiesRealmCon
propertiesBuilder.build();
}

private void parseDistributedRealm(ConfigurationReader reader, DistributedRealmConfigurationBuilder distributedRealmBuilder) {
String name = "distributed";
String[] realms = ParseUtils.requireAttributes(reader, Attribute.REALMS)[0].split("\\s+");
for (int i = 0; i < reader.getAttributeCount(); i++) {
ParseUtils.requireNoNamespaceAttribute(reader, i);
String value = reader.getAttributeValue(i);
Attribute attribute = Attribute.forName(reader.getAttributeName(i));
switch (attribute) {
case NAME:
name = value;
break;
case REALMS:
// Already seen
break;
default:
throw ParseUtils.unexpectedAttribute(reader, i);
}
}
ParseUtils.requireNoContent(reader);
distributedRealmBuilder.name(name).realms(realms).build();
}

private void parseServerIdentities(ConfigurationReader reader, ServerConfigurationBuilder builder, RealmConfigurationBuilder securityRealmBuilder) {
ServerIdentitiesConfigurationBuilder identitiesBuilder = securityRealmBuilder.serverIdentitiesConfiguration();
Element element = nextElement(reader);
Expand Down
@@ -0,0 +1,40 @@
package org.infinispan.server.configuration.security;

import java.util.ArrayList;
import java.util.List;

import org.infinispan.commons.configuration.ConfigurationInfo;
import org.infinispan.commons.configuration.attributes.AttributeDefinition;
import org.infinispan.commons.configuration.attributes.AttributeSet;
import org.infinispan.commons.configuration.elements.DefaultElementDefinition;
import org.infinispan.commons.configuration.elements.ElementDefinition;
import org.infinispan.server.configuration.Element;

/**
* @author Tristan Tarrant &lt;tristan@infinispan.org&gt;
* @since 13.0
**/
public class DistributedRealmConfiguration implements ConfigurationInfo {
static final AttributeDefinition<String> NAME = AttributeDefinition.builder("name", null, String.class).build();
static final AttributeDefinition<List<String>> REALMS = AttributeDefinition.builder("realms", new ArrayList<>(), (Class<List<String>>) (Class<?>) List.class)
.initializer(ArrayList::new).immutable().build();

static AttributeSet attributeDefinitionSet() {
return new AttributeSet(DistributedRealmConfiguration.class, NAME, REALMS);
}

private static ElementDefinition ELEMENT_DEFINITION = new DefaultElementDefinition(Element.TOKEN_REALM.toString());
private final AttributeSet attributes;

DistributedRealmConfiguration(AttributeSet attributes) {
this.attributes = attributes.checkProtection();
}

public String name() {
return attributes.attribute(NAME).get();
}

public List<String> realms() {
return attributes.attribute(REALMS).get();
}
}
@@ -0,0 +1,65 @@
package org.infinispan.server.configuration.security;

import static org.infinispan.server.configuration.security.DistributedRealmConfiguration.NAME;
import static org.infinispan.server.configuration.security.DistributedRealmConfiguration.REALMS;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.infinispan.commons.configuration.Builder;
import org.infinispan.commons.configuration.attributes.AttributeSet;
import org.wildfly.security.auth.realm.DistributedSecurityRealm;
import org.wildfly.security.auth.server.SecurityRealm;

/**
* @author Tristan Tarrant &lt;tristan@infinispan.org&gt;
* @since 13.0
**/
public class DistributedRealmConfigurationBuilder implements Builder<DistributedRealmConfiguration> {

private final RealmConfigurationBuilder realmBuilder;
private final AttributeSet attributes;
private DistributedSecurityRealm securityRealm;


public DistributedRealmConfigurationBuilder(RealmConfigurationBuilder realmConfigurationBuilder) {
this.realmBuilder = realmConfigurationBuilder;
this.attributes = DistributedRealmConfiguration.attributeDefinitionSet();
}

public DistributedRealmConfigurationBuilder name(String name) {
attributes.attribute(NAME).set(name);
return this;
}

public DistributedRealmConfigurationBuilder realms(String[] realms) {
attributes.attribute(REALMS).set(Arrays.asList(realms));
return this;
}

@Override
public void validate() {

}

@Override
public DistributedRealmConfiguration create() {
return new DistributedRealmConfiguration(attributes.protect());
}

@Override
public Builder<?> read(DistributedRealmConfiguration template) {
attributes.read(template.attributes());
return this;
}

public void build() {
if (securityRealm == null) {
SecurityRealm realms[] = attributes.attribute(REALMS).get()
.stream().map(name -> realmBuilder.getRealm(name)).toArray(SecurityRealm[]::new);
securityRealm = new DistributedSecurityRealm(realms);
realmBuilder.addRealm(attributes.attribute(NAME).get(), securityRealm);
}
}
}
Expand Up @@ -14,12 +14,13 @@
* @since 10.0
*/
public class RealmConfiguration implements ConfigurationInfo {
static final AttributeDefinition<String> NAME = AttributeDefinition.builder("name", null, String.class).build();
static final AttributeDefinition<Integer> CACHE_MAX_SIZE = AttributeDefinition.builder("cacheMaxSize", 256).build();
static final AttributeDefinition<Long> CACHE_LIFESPAN = AttributeDefinition.builder("lifespan", -1l).build();
static final AttributeDefinition<String> NAME = AttributeDefinition.builder("name", null, String.class).immutable().build();
static final AttributeDefinition<String> DEFAULT_REALM = AttributeDefinition.builder("defaultRealm", null, String.class).immutable().build();
static final AttributeDefinition<Integer> CACHE_MAX_SIZE = AttributeDefinition.builder("cacheMaxSize", 256).immutable().build();
static final AttributeDefinition<Long> CACHE_LIFESPAN = AttributeDefinition.builder("lifespan", -1l).immutable().build();

static AttributeSet attributeDefinitionSet() {
return new AttributeSet(RealmConfiguration.class, NAME, CACHE_MAX_SIZE, CACHE_LIFESPAN);
return new AttributeSet(RealmConfiguration.class, NAME, DEFAULT_REALM, CACHE_MAX_SIZE, CACHE_LIFESPAN);
}

private static ElementDefinition ELEMENT_DEFINITION = new DefaultElementDefinition(Element.SECURITY_REALM.toString());
Expand Down
Expand Up @@ -2,9 +2,12 @@

import static org.infinispan.server.configuration.security.RealmConfiguration.CACHE_LIFESPAN;
import static org.infinispan.server.configuration.security.RealmConfiguration.CACHE_MAX_SIZE;
import static org.infinispan.server.configuration.security.RealmConfiguration.DEFAULT_REALM;

import java.security.GeneralSecurityException;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;

Expand Down Expand Up @@ -43,6 +46,8 @@ public class RealmConfigurationBuilder implements Builder<RealmConfiguration> {
private final TokenRealmConfigurationBuilder tokenConfiguration = new TokenRealmConfigurationBuilder(this);
private final TrustStoreRealmConfigurationBuilder trustStoreConfiguration = new TrustStoreRealmConfigurationBuilder(this);
private final PropertiesRealmConfigurationBuilder propertiesRealmConfiguration = new PropertiesRealmConfigurationBuilder(this);
private final DistributedRealmConfigurationBuilder distributedConfiguration = new DistributedRealmConfigurationBuilder(this);
private final Map<String, SecurityRealm> realms = new HashMap<>();

private SSLContext sslContext = null;
private SSLContextBuilder sslContextBuilder = null;
Expand Down Expand Up @@ -73,6 +78,12 @@ SecurityDomain.Builder domainBuilder() {
return domainBuilder;
}

public RealmConfigurationBuilder defaultRealm(String defaultRealm) {
this.attributes.attribute(DEFAULT_REALM).set(defaultRealm);
domainBuilder.setDefaultRealmName(defaultRealm);
return this;
}

public RealmConfigurationBuilder cacheMaxSize(int size) {
this.attributes.attribute(CACHE_MAX_SIZE).set(size);
return this;
Expand Down Expand Up @@ -111,6 +122,10 @@ public PropertiesRealmConfigurationBuilder propertiesRealm() {
return propertiesRealmConfiguration;
}

public DistributedRealmConfigurationBuilder distributedConfiguration() {
return distributedConfiguration;
}

void setHttpChallengeReadiness(Supplier<Boolean> readiness) {
this.httpChallengeReadiness = readiness;
}
Expand Down Expand Up @@ -206,7 +221,9 @@ public void addRealm(String realmName, SecurityRealm realm) {
}

public void addRealm(String realmName, SecurityRealm realm, Consumer<SecurityDomain.RealmBuilder> realmBuilderConsumer) {
SecurityDomain.RealmBuilder realmBuilder = domainBuilder.addRealm(realmName, cacheable(realm));
SecurityRealm securityRealm = cacheable(realm);
realms.put(realmName, securityRealm);
SecurityDomain.RealmBuilder realmBuilder = domainBuilder.addRealm(realmName, securityRealm);
if (realmBuilderConsumer != null) {
realmBuilderConsumer.accept(realmBuilder);
}
Expand All @@ -216,4 +233,8 @@ public void addRealm(String realmName, SecurityRealm realm, Consumer<SecurityDom
domainBuilder.setDefaultRealmName(realmName);
}
}

public SecurityRealm getRealm(String name) {
return realms.get(name);
}
}

0 comments on commit 8966502

Please sign in to comment.