Skip to content

Commit

Permalink
[ELY-2241] Bump to a new version 1.1 namespace for the fileysystem re…
Browse files Browse the repository at this point in the history
…alm.

This new schema drops all elements and types specific to authentication
client.

Additionally prepare for support for new items to be added to this
namespace.
  • Loading branch information
darranl authored and Ashpan committed Mar 17, 2022
1 parent 0bc5a2f commit 310dc3c
Show file tree
Hide file tree
Showing 2 changed files with 222 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;
Expand Down Expand Up @@ -113,11 +115,47 @@
*/
public final class FileSystemSecurityRealm implements ModifiableSecurityRealm, CacheableSecurityRealm {

static final String ELYTRON_1_0 = "urn:elytron:1.0";
static final String ELYTRON_1_0_1 = "urn:elytron:1.0.1";
static final ElytronPermission CREATE_SECURITY_REALM = ElytronPermission.forName("createSecurityRealm");

private final Supplier<Provider[]> providers;
static final Map<String, Version> KNOWN_NAMESPACES;

private enum Version {

VERSION_1_0("urn:elytron:1.0", null),
VERSION_1_0_1("urn:elytron:1.0.1", VERSION_1_0),
VERSION_1_1("urn:elytron:identity:1.1", VERSION_1_0_1);

final String namespace;

/*
* In the future we could support multiple parents but wait until that becomes a reality before adding it.
*/
final Version parent;

Version(String namespace, Version parent) {
this.namespace = namespace;
this.parent = parent;
}

String getNamespace() {
return namespace;
}

boolean isAtLeast(Version version) {
return this.equals(version) || (parent != null ? parent.isAtLeast(version) : false);
}

}

static {
Map<String, Version> knownNamespaces = new HashMap<>();
for (Version version : Version.values()) {
knownNamespaces.put(version.namespace, version);
}
KNOWN_NAMESPACES = Collections.unmodifiableMap(knownNamespaces);
}

private final Path root;
private final NameRewriter nameRewriter;
private final int levels;
Expand Down Expand Up @@ -640,7 +678,7 @@ private Void createPrivileged() throws RealmUnavailableException {
streamWriter.writeCharacters("\n");
streamWriter.writeStartElement("identity");
// Continue to write using 1.0 as not using any features added in 1.0.1
streamWriter.writeDefaultNamespace(ELYTRON_1_0);
streamWriter.writeDefaultNamespace(Version.VERSION_1_0.getNamespace());
streamWriter.writeEndElement();
streamWriter.writeEndDocument();
} catch (XMLStreamException e) {
Expand Down Expand Up @@ -774,12 +812,26 @@ private Void replaceIdentityPrivileged(final LoadedIdentity newIdentity) throws
}
}

private Version requiredVersion(final LoadedIdentity identityToWrite) {
// As new functionality is added we will identify if we need to use a later version
// if new functionality is used then use the required schema version otherwise fallback
// to an older version.

// Do we require version 1.1?
// return Version.VERSION_1_1;

// We would not require 1.0.1 as no realm specific changed were made.
//return Version.VERSION_1_0_1;

return Version.VERSION_1_0;
}

private void writeIdentity(final XMLStreamWriter streamWriter, final LoadedIdentity newIdentity) throws XMLStreamException, InvalidKeySpecException, NoSuchAlgorithmException, CertificateEncodingException {
streamWriter.writeStartDocument();
streamWriter.writeCharacters("\n");
streamWriter.writeStartElement("identity");
// Continue to write using 1.0 as not using any features added in 1.0.1
streamWriter.writeDefaultNamespace(ELYTRON_1_0);

streamWriter.writeDefaultNamespace(requiredVersion(newIdentity).getNamespace());

if (newIdentity.getCredentials().size() > 0) {
streamWriter.writeCharacters("\n ");
Expand Down Expand Up @@ -898,13 +950,18 @@ private LoadedIdentity loadIdentityPrivileged(final boolean skipCredentials, fin

private LoadedIdentity parseIdentity(final XMLStreamReader streamReader, final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException, XMLStreamException {
final int tag = streamReader.nextTag();
if (tag != START_ELEMENT || ! validNamespace(streamReader.getNamespaceURI()) || ! "identity".equals(streamReader.getLocalName())) {
Version version;
if (tag != START_ELEMENT || ((version = identifyVersion(streamReader)) == null) || ! "identity".equals(streamReader.getLocalName())) {
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
}
return parseIdentityContents(streamReader, skipCredentials, skipAttributes);
return parseIdentityContents(streamReader, version, skipCredentials, skipAttributes);
}

private LoadedIdentity parseIdentityContents(final XMLStreamReader streamReader, final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException, XMLStreamException {
private Version identifyVersion(final XMLStreamReader streamReader) {
return KNOWN_NAMESPACES.get(streamReader.getNamespaceURI());
}

private LoadedIdentity parseIdentityContents(final XMLStreamReader streamReader, final Version version, final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException, XMLStreamException {
final int attributeCount = streamReader.getAttributeCount();
if (attributeCount > 0) {
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
Expand All @@ -922,29 +979,30 @@ private LoadedIdentity parseIdentityContents(final XMLStreamReader streamReader,
}
return new LoadedIdentity(name, credentials, attributes, hashEncoding);
}
if (! validNamespace(streamReader.getNamespaceURI())) {
if (! version.getNamespace().equals(streamReader.getNamespaceURI())) {
// Mixed versions unsupported.
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
}
if (! gotCredentials && "credentials".equals(streamReader.getLocalName())) {
gotCredentials = true;
if (skipCredentials) {
consumeContent(streamReader);
} else {
credentials = parseCredentials(streamReader);
credentials = parseCredentials(streamReader, version);
}
} else if (! gotAttributes && "attributes".equals(streamReader.getLocalName())) {
gotAttributes = true;
if (skipAttributes) {
consumeContent(streamReader);
} else {
attributes = parseAttributes(streamReader);
attributes = parseAttributes(streamReader, version);
}
}
streamReader.nextTag();
}
}

private List<Credential> parseCredentials(final XMLStreamReader streamReader) throws RealmUnavailableException, XMLStreamException {
private List<Credential> parseCredentials(final XMLStreamReader streamReader, final Version version) throws RealmUnavailableException, XMLStreamException {
final int attributeCount = streamReader.getAttributeCount();
if (attributeCount > 0) {
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
Expand All @@ -954,7 +1012,8 @@ private List<Credential> parseCredentials(final XMLStreamReader streamReader) th
}
List<Credential> credentials = new ArrayList<>();
do {
if (! validNamespace(streamReader.getNamespaceURI())) {
if (! version.getNamespace().equals(streamReader.getNamespaceURI())) {
// Mixed versions unsupported.
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
}
if ("password".equals(streamReader.getLocalName())) {
Expand Down Expand Up @@ -1111,7 +1170,7 @@ private void parseOtp(final List<Credential> credentials, final XMLStreamReader
}
}

private Attributes parseAttributes(final XMLStreamReader streamReader) throws RealmUnavailableException, XMLStreamException {
private Attributes parseAttributes(final XMLStreamReader streamReader, final Version version) throws RealmUnavailableException, XMLStreamException {
final int attributeCount = streamReader.getAttributeCount();
if (attributeCount > 0) {
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
Expand All @@ -1122,7 +1181,8 @@ private Attributes parseAttributes(final XMLStreamReader streamReader) throws Re
}
Attributes attributes = new MapAttributes();
do {
if (! validNamespace(streamReader.getNamespaceURI())) {
if (! version.getNamespace().equals(streamReader.getNamespaceURI())) {
// Mixed versions unsupported.
throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name);
}
if ("attribute".equals(streamReader.getLocalName())) {
Expand Down Expand Up @@ -1177,9 +1237,6 @@ private void consumeContent(final XMLStreamReader reader) throws XMLStreamExcept
}
}

private boolean validNamespace(final String namespace) {
return ELYTRON_1_0.equals(namespace) || ELYTRON_1_0_1.equals(namespace);
}
}

static final class LoadedIdentity {
Expand Down
147 changes: 147 additions & 0 deletions auth/realm/base/src/main/resources/schema/elytron-identity-1_1.xsd
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
~ JBoss, Home of Professional Open Source.
~ Copyright 2021 Red Hat, Inc., and individual 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.
-->

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:elytron:identity:1.1.0"
xmlns="urn:elytron:identity:1.1.0"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
version="1.0">

<!-- File-backed realm elements -->

<xsd:element name="identity" type="identity-type"/>

<xsd:complexType name="identity-type">
<xsd:all minOccurs="1" maxOccurs="1">
<xsd:element name="credentials" type="credentials-type" minOccurs="0" maxOccurs="1"/>
<xsd:element name="attributes" type="attributes-type" minOccurs="0" maxOccurs="1"/>
</xsd:all>
</xsd:complexType>

<xsd:complexType name="credentials-type">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="password" type="credential-type"/>
<xsd:element name="otp" type="otp-credential-type"/>
<xsd:element name="public-key" type="credential-type"/>
<xsd:element name="certificate" type="credential-type"/>
</xsd:choice>
</xsd:complexType>

<xsd:complexType name="attributes-type">
<xsd:choice minOccurs="0" maxOccurs="unbounded">
<xsd:element name="attribute" type="attribute-type"/>
</xsd:choice>
</xsd:complexType>

<xsd:complexType name="credential-type">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="algorithm" type="xsd:string" use="optional"/>
<xsd:attribute name="format" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>

<xsd:complexType name="otp-credential-type">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="algorithm" type="xsd:string" use="optional"/>
<xsd:attribute name="hash" type="xsd:string" use="optional"/>
<xsd:attribute name="seed" type="xsd:string" use="optional"/>
<xsd:attribute name="sequence" type="xsd:string" use="optional"/>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>

<xsd:complexType name="attribute-type">
<xsd:attribute name="name" type="xsd:string" use="required"/>
<xsd:attribute name="value" type="xsd:string" use="required"/>
</xsd:complexType>

<!-- Common types -->

<xsd:complexType name="empty-type"/>

<xsd:complexType name="name-type">
<xsd:attribute name="name" type="xsd:string" use="required"/>
</xsd:complexType>

<xsd:complexType name="abstract-type-type">
<xsd:attribute name="name" type="xsd:string" use="optional"/>
<xsd:attribute name="authority" type="xsd:string" use="optional"/>
</xsd:complexType>

<xsd:complexType name="optional-name-type">
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>

<xsd:complexType name="selector-type">
<xsd:attribute name="selector" type="xsd:string" use="required"/>
</xsd:complexType>

<xsd:complexType name="module-ref-type">
<xsd:attribute name="module-name" type="xsd:string" use="optional"/>
</xsd:complexType>

<xsd:complexType name="port-number-type">
<xsd:attribute name="number" type="port-number-simple-type" use="required"/>
</xsd:complexType>

<xsd:simpleType name="port-number-simple-type">
<xsd:restriction base="xsd:positiveInteger">
<xsd:minInclusive value="1"/>
<xsd:maxInclusive value="65535"/>
</xsd:restriction>
</xsd:simpleType>

<xsd:complexType name="regex-substitution-type">
<xsd:attribute name="pattern" type="xsd:string" use="required"/>
<xsd:attribute name="replacement" type="xsd:string" use="required"/>
</xsd:complexType>

<xsd:complexType name="names-list-type">
<xsd:attribute name="names" type="names-list-simple-type" use="required"/>
</xsd:complexType>

<xsd:simpleType name="names-list-simple-type">
<xsd:list itemType="xsd:string"/>
</xsd:simpleType>

<xsd:complexType name="uri-type">
<xsd:attribute name="uri" type="xsd:anyURI" use="required"/>
</xsd:complexType>

<xsd:complexType name="clear-password-type">
<xsd:attribute name="password" type="xsd:string" use="required"/>
</xsd:complexType>

<xsd:complexType name="properties-type">
<xsd:sequence>
<xsd:element name="property" maxOccurs="unbounded">
<xsd:complexType>
<xsd:attribute name="key" type="xsd:string" use="required" />
<xsd:attribute name="value" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:sequence>
</xsd:complexType>

</xsd:schema>

0 comments on commit 310dc3c

Please sign in to comment.