/*
* Copyright 2016 Red Hat, Inc.
*
* 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.jboss.as.security;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.CODE;
import static org.jboss.as.security.Constants.*;
import static org.jboss.as.security.elytron.ElytronIntegrationResourceDefinitions.LEGACY_JAAS_CONFIG;
import static org.jboss.as.security.elytron.ElytronIntegrationResourceDefinitions.LEGACY_JSSE_CONFIG;
import java.util.List;
import java.util.Set;
import javax.xml.stream.XMLStreamException;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
/**
* A {@link XMLElementWriter} that is responsible for writing the configuration of the legacy security subsystem. The
* subsystem is written according to the current (latest) version of the schema.
*
* @author <a href="mailto:sguilhen@redhat.com">Stefan Guilhen</a>
*/
public class SecuritySubsystemPersister implements XMLElementWriter<SubsystemMarshallingContext> {
public static final SecuritySubsystemPersister INSTANCE = new SecuritySubsystemPersister();
protected SecuritySubsystemPersister() {
}
@Override
public void writeContent(XMLExtendedStreamWriter writer, SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(Namespace.CURRENT.getUriString(), false);
ModelNode node = context.getModelNode();
if (SecuritySubsystemRootResourceDefinition.DEEP_COPY_SUBJECT_MODE.isMarshallable(node) ||
SecuritySubsystemRootResourceDefinition.INITIALIZE_JACC.isMarshallable(node)) {
writer.writeEmptyElement(Element.SECURITY_MANAGEMENT.getLocalName());
if (SecuritySubsystemRootResourceDefinition.DEEP_COPY_SUBJECT_MODE.isMarshallable(node)) {
SecuritySubsystemRootResourceDefinition.DEEP_COPY_SUBJECT_MODE.marshallAsAttribute(node, writer);
}
if (SecuritySubsystemRootResourceDefinition.INITIALIZE_JACC.isMarshallable(node)) {
SecuritySubsystemRootResourceDefinition.INITIALIZE_JACC.marshallAsAttribute(node, writer);
}
}
if (node.hasDefined(SECURITY_DOMAIN) && node.get(SECURITY_DOMAIN).asInt() > 0) {
writer.writeStartElement(Element.SECURITY_DOMAINS.getLocalName());
ModelNode securityDomains = node.get(SECURITY_DOMAIN);
for (String policy : securityDomains.keys()) {
writer.writeStartElement(Element.SECURITY_DOMAIN.getLocalName());
writer.writeAttribute(Attribute.NAME.getLocalName(), policy);
ModelNode policyDetails = securityDomains.get(policy);
SecurityDomainResourceDefinition.CACHE_TYPE.marshallAsAttribute(policyDetails, writer);
writeSecurityDomainContent(writer, policyDetails);
writer.writeEndElement();
}
writer.writeEndElement();
}
if (node.hasDefined(Constants.VAULT)) {
ModelNode vault = node.get(Constants.VAULT, Constants.CLASSIC);
writer.writeStartElement(Element.VAULT.getLocalName());
VaultResourceDefinition.CODE.marshallAsAttribute(vault, writer);
if (vault.hasDefined(Constants.VAULT_OPTIONS)) {
ModelNode properties = vault.get(Constants.VAULT_OPTIONS);
for (Property prop : properties.asPropertyList()) {
writer.writeEmptyElement(Element.VAULT_OPTION.getLocalName());
writer.writeAttribute(Attribute.NAME.getLocalName(), prop.getName());
writer.writeAttribute(Attribute.VALUE.getLocalName(), prop.getValue().asString());
}
}
writer.writeEndElement();
}
writeElytronIntegration(writer, node);
writer.writeEndElement();
}
private void writeSecurityDomainContent(XMLExtendedStreamWriter writer, ModelNode policyDetails) throws XMLStreamException {
Set<String> keys = policyDetails.keys();
keys.remove(NAME);
keys.remove(CACHE_TYPE);
for (String key : keys) {
Element element = Element.forName(key);
switch (element) {
case AUTHENTICATION: {
ModelNode kind = policyDetails.get(AUTHENTICATION);
for (Property prop : kind.asPropertyList()) {
if (CLASSIC.equals(prop.getName())) {
writeAuthentication(writer, prop.getValue());
} else if (JASPI.equals(prop.getName())) {
writeAuthenticationJaspi(writer, prop.getValue());
}
}
break;
}
case AUTHORIZATION: {
writeAuthorization(writer, policyDetails.get(AUTHORIZATION, CLASSIC));
break;
}
case ACL: {
writeACL(writer, policyDetails.get(ACL, CLASSIC));
break;
}
case AUDIT: {
writeAudit(writer, policyDetails.get(AUDIT, CLASSIC));
break;
}
case IDENTITY_TRUST: {
writeIdentityTrust(writer, policyDetails.get(IDENTITY_TRUST, CLASSIC));
break;
}
case MAPPING: {
writeMapping(writer, policyDetails.get(MAPPING, CLASSIC));
break;
}
case JSSE: {
writeJSSE(writer, policyDetails.get(JSSE, CLASSIC));
break;
}
}
}
}
private void writeAuthentication(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.AUTHENTICATION.getLocalName());
writeLoginModule(writer, modelNode, Constants.LOGIN_MODULE);
writer.writeEndElement();
}
}
private void writeAuthorization(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.AUTHORIZATION.getLocalName());
writeLoginModule(writer, modelNode, Constants.POLICY_MODULE, Element.POLICY_MODULE.getLocalName());
writer.writeEndElement();
}
}
private void writeACL(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.ACL.getLocalName());
writeLoginModule(writer, modelNode, Constants.ACL_MODULE, Element.ACL_MODULE.getLocalName());
writer.writeEndElement();
}
}
private void writeAudit(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.AUDIT.getLocalName());
writeLoginModule(writer, modelNode, Constants.PROVIDER_MODULE, Element.PROVIDER_MODULE.getLocalName());
writer.writeEndElement();
}
}
private void writeIdentityTrust(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.IDENTITY_TRUST.getLocalName());
writeLoginModule(writer, modelNode, Constants.TRUST_MODULE, Element.TRUST_MODULE.getLocalName());
writer.writeEndElement();
}
}
private void writeMapping(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.MAPPING.getLocalName());
writeLoginModule(writer, modelNode, Constants.MAPPING_MODULE, Constants.MAPPING_MODULE);
writer.writeEndElement();
}
}
private void writeAuthenticationJaspi(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.AUTHENTICATION_JASPI.getLocalName());
ModelNode moduleStack = modelNode.get(LOGIN_MODULE_STACK);
writeLoginModuleStack(writer, moduleStack);
writeLoginModule(writer, modelNode, Constants.AUTH_MODULE, Element.AUTH_MODULE.getLocalName());
writer.writeEndElement();
}
}
private void writeLoginModuleStack(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
List<Property> stacks = modelNode.asPropertyList();
for (Property stack : stacks) {
writer.writeStartElement(Element.LOGIN_MODULE_STACK.getLocalName());
writer.writeAttribute(Attribute.NAME.getLocalName(), stack.getName());
writeLoginModule(writer, stack.getValue(), Constants.LOGIN_MODULE);
writer.writeEndElement();
}
}
}
private void writeLoginModule(XMLExtendedStreamWriter writer, ModelNode modelNode, String key) throws XMLStreamException {
writeLoginModule(writer, modelNode, key, Element.LOGIN_MODULE.getLocalName());
}
private void writeLoginModule(XMLExtendedStreamWriter writer, ModelNode modelNode, String key, final String elementName) throws XMLStreamException {
if (!modelNode.hasDefined(key)){
return;
}
final ModelNode modules = modelNode.get(key);
for (Property moduleProp : modules.asPropertyList()) {
ModelNode module = moduleProp.getValue();
writer.writeStartElement(elementName);
if (!moduleProp.getName().equals(module.get(CODE).asString())) {
writer.writeAttribute(NAME, moduleProp.getName());
}
LoginModuleResourceDefinition.CODE.marshallAsAttribute(module, writer);
LoginModuleResourceDefinition.FLAG.marshallAsAttribute(module, writer);
MappingModuleDefinition.TYPE.marshallAsAttribute(module, writer);
JASPIMappingModuleDefinition.LOGIN_MODULE_STACK_REF.marshallAsAttribute(module, writer);
LoginModuleResourceDefinition.MODULE.marshallAsAttribute(module, false, writer);
if (module.hasDefined(Constants.MODULE_OPTIONS)) {
for (ModelNode option : module.get(Constants.MODULE_OPTIONS).asList()) {
writer.writeEmptyElement(Element.MODULE_OPTION.getLocalName());
writer.writeAttribute(Attribute.NAME.getLocalName(), option.asProperty().getName());
writer.writeAttribute(Attribute.VALUE.getLocalName(), option.asProperty().getValue().asString());
}
}
writer.writeEndElement();
}
}
private void writeJSSE(XMLExtendedStreamWriter writer, ModelNode modelNode) throws XMLStreamException {
if (modelNode.isDefined() && modelNode.asInt() > 0) {
writer.writeStartElement(Element.JSSE.getLocalName());
JSSEResourceDefinition.KEYSTORE.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.TRUSTSTORE.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.KEYMANAGER.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.TRUSTMANAGER.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.CIPHER_SUITES.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.SERVER_ALIAS.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.SERVICE_AUTH_TOKEN.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.CLIENT_ALIAS.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.CLIENT_AUTH.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.PROTOCOLS.marshallAsAttribute(modelNode, false, writer);
JSSEResourceDefinition.ADDITIONAL_PROPERTIES.marshallAsElement(modelNode, writer);
writer.writeEndElement();
}
}
private void writeElytronIntegration(final XMLExtendedStreamWriter writer, final ModelNode modelNode) throws XMLStreamException {
boolean integrationStarted = false;
integrationStarted = integrationStarted | writeSecurityRealms(writer, modelNode, integrationStarted);
integrationStarted = integrationStarted | writeTLS(writer, modelNode, integrationStarted);
if (integrationStarted) {
writer.writeEndElement();
}
}
private boolean writeSecurityRealms(final XMLExtendedStreamWriter writer, final ModelNode modelNode,
final boolean integrationStarted) throws XMLStreamException {
if (modelNode.hasDefined(ELYTRON_REALM)) {
if (integrationStarted == false) {
writer.writeStartElement(ELYTRON_INTEGRATION);
}
writer.writeStartElement(SECURITY_REALMS);
ModelNode elytronRealms = modelNode.require(ELYTRON_REALM);
for (String realmName : elytronRealms.keys()) {
writer.writeStartElement(ELYTRON_REALM);
writer.writeAttribute(NAME, realmName);
LEGACY_JAAS_CONFIG.marshallAsAttribute(elytronRealms.require(realmName), writer);
writer.writeEndElement();
}
writer.writeEndElement();
return true;
}
return false;
}
private boolean writeTLS(final XMLExtendedStreamWriter writer, final ModelNode modelNode,
final boolean integrationStarted) throws XMLStreamException {
if (modelNode.hasDefined(ELYTRON_KEY_STORE) || modelNode.hasDefined(ELYTRON_TRUST_STORE) ||
modelNode.hasDefined(ELYTRON_KEY_MANAGER) || modelNode.hasDefined(ELYTRON_TRUST_MANAGER)) {
if (integrationStarted == false) {
writer.writeStartElement(ELYTRON_INTEGRATION);
}
writer.writeStartElement(TLS);
writeTLSEntity(writer, modelNode, ELYTRON_KEY_STORE);
writeTLSEntity(writer, modelNode, ELYTRON_TRUST_STORE);
writeTLSEntity(writer, modelNode, ELYTRON_KEY_MANAGER);
writeTLSEntity(writer, modelNode, ELYTRON_TRUST_MANAGER);
writer.writeEndElement();
return true;
}
return false;
}
private void writeTLSEntity(final XMLExtendedStreamWriter writer, final ModelNode modelNode,
final String tlsEntityName) throws XMLStreamException {
if (modelNode.hasDefined(tlsEntityName)) {
ModelNode tlsEntities = modelNode.require(tlsEntityName);
for (String entityName : tlsEntities.keys()) {
writer.writeStartElement(tlsEntityName);
writer.writeAttribute(NAME, entityName);
LEGACY_JSSE_CONFIG.marshallAsAttribute(tlsEntities.require(entityName), writer);
writer.writeEndElement();
}
}
}
}