Browse Source
Groovy has more extensive support for Xml parsing via XmlSlurper. To replace it, this conversion also introduces a SAX wrapper, NicerXmlParser, and a companion Node wrapper, NicerNode, that allowed for less modification of the converted tests. Issue: gh-4939pull/5182/merge
12 changed files with 951 additions and 522 deletions
@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2011 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc |
||||
|
||||
/** |
||||
* Represents a Spring Security XSD Attribute. It is created when parsing the current xsd to compare to the documented appendix. |
||||
* |
||||
* @author Rob Winch |
||||
* @see SpringSecurityXsdParser |
||||
* @see XsdDocumentedSpec |
||||
*/ |
||||
class Attribute { |
||||
def name |
||||
def desc |
||||
def elmt |
||||
|
||||
def getId() { |
||||
return "${elmt.id}-${name}".toString() |
||||
} |
||||
} |
||||
@ -1,90 +0,0 @@
@@ -1,90 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2011 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc |
||||
|
||||
/** |
||||
* Represents a Spring Security XSD Element. It is created when parsing the current xsd to compare to the documented appendix. |
||||
* |
||||
* @author Rob Winch |
||||
* @see SpringSecurityXsdParser |
||||
* @see XsdDocumentedSpec |
||||
*/ |
||||
class Element { |
||||
def name |
||||
def desc |
||||
def attrs |
||||
/** |
||||
* Contains the elements that extend this element (i.e. any-user-service contains ldap-user-service) |
||||
*/ |
||||
def subGrps = [] |
||||
def childElmts = [:] |
||||
def parentElmts = [:] |
||||
|
||||
def getId() { |
||||
return "nsa-${name}".toString() |
||||
} |
||||
|
||||
/** |
||||
* Gets all the ids related to this Element including attributes, parent elements, and child elements. |
||||
* |
||||
* <p> |
||||
* The expected ids to be found are documented below. |
||||
* <ul> |
||||
* <li>Elements - any xml element will have the nsa-<element>. For example the http element will have the id |
||||
* nsa-http</li> |
||||
* <li>Parent Section - Any element with a parent other than beans will have a section named |
||||
* nsa-<element>-parents. For example, authentication-provider would have a section id of |
||||
* nsa-authentication-provider-parents. The section would then contain a list of links pointing to the |
||||
* documentation for each parent element.</li> |
||||
* <li>Attributes Section - Any element with attributes will have a section with the id |
||||
* nsa-<element>-attributes. For example the http element would require a section with the id |
||||
* http-attributes.</li> |
||||
* <li>Attribute - Each attribute of an element would have an id of nsa-<element>-<attributeName>. For |
||||
* example the attribute create-session for the http attribute would have the id http-create-session.</li> |
||||
* <li>Child Section - Any element with a child element will have a section named nsa-<element>-children. |
||||
* For example, authentication-provider would have a section id of nsa-authentication-provider-children. The |
||||
* section would then contain a list of links pointing to the documentation for each child element.</li> |
||||
* </ul> |
||||
* @return |
||||
*/ |
||||
def getIds() { |
||||
def ids = [id] |
||||
childElmts.values()*.ids.each { ids.addAll it } |
||||
attrs*.id.each { ids.add it } |
||||
if(childElmts) { |
||||
ids.add id+'-children' |
||||
} |
||||
if(attrs) { |
||||
ids.add id+'-attributes' |
||||
} |
||||
if(parentElmts) { |
||||
ids.add id+'-parents' |
||||
} |
||||
ids |
||||
} |
||||
|
||||
def getAllChildElmts() { |
||||
def result = [:] |
||||
childElmts.values()*.subGrps*.each { elmt -> result.put(elmt.name,elmt) } |
||||
result + childElmts |
||||
} |
||||
|
||||
def getAllParentElmts() { |
||||
def result = [:] |
||||
parentElmts.values()*.subGrps*.each { elmt -> result.put(elmt.name,elmt) } |
||||
result + parentElmts |
||||
} |
||||
} |
||||
@ -1,177 +0,0 @@
@@ -1,177 +0,0 @@
|
||||
/* |
||||
* Copyright 2002-2011 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc |
||||
|
||||
import groovy.xml.Namespace |
||||
|
||||
/** |
||||
* Parses the Spring Security Xsd Document |
||||
* |
||||
* @author Rob Winch |
||||
*/ |
||||
class SpringSecurityXsdParser { |
||||
private def rootElement |
||||
|
||||
private def xs = new Namespace("http://www.w3.org/2001/XMLSchema", 'xs') |
||||
private def attrElmts = [] as Set |
||||
private def elementNameToElement = [:] as Map |
||||
|
||||
/** |
||||
* Returns a map of the element name to the {@link Element}. |
||||
* @return |
||||
*/ |
||||
Map<String,Element> parse() { |
||||
elements(rootElement) |
||||
elementNameToElement |
||||
} |
||||
|
||||
/** |
||||
* Creates a Map of the name to an Element object of all the children of element. |
||||
* |
||||
* @param element |
||||
* @return |
||||
*/ |
||||
private def elements(element) { |
||||
def elementNameToElement = [:] as Map |
||||
element.children().each { c-> |
||||
if(c.name() == 'element') { |
||||
def e = elmt(c) |
||||
elementNameToElement.put(e.name,e) |
||||
} else { |
||||
elementNameToElement.putAll(elements(c)) |
||||
} |
||||
} |
||||
elementNameToElement |
||||
} |
||||
|
||||
/** |
||||
* Any children that are attribute will be returned as an Attribute object. |
||||
* @param element |
||||
* @return a collection of Attribute objects that are children of element. |
||||
*/ |
||||
private def attrs(element) { |
||||
def r = [] |
||||
element.children().each { c-> |
||||
if(c.name() == 'attribute') { |
||||
r.add(attr(c)) |
||||
}else if(c.name() == 'element') { |
||||
}else { |
||||
r.addAll(attrs(c)) |
||||
} |
||||
} |
||||
r |
||||
} |
||||
|
||||
/** |
||||
* Any children will be searched for an attributeGroup, each of it's children will be returned as an Attribute |
||||
* @param element |
||||
* @return |
||||
*/ |
||||
private def attrgrps(element) { |
||||
def r = [] |
||||
element.children().each { c-> |
||||
if(c.name() == 'element') { |
||||
}else if (c.name() == 'attributeGroup') { |
||||
if(c.attributes().get('name')) { |
||||
r.addAll(attrgrp(c)) |
||||
} else { |
||||
def n = c.attributes().get('ref').split(':')[1] |
||||
def attrGrp = findNode(element,n) |
||||
r.addAll(attrgrp(attrGrp)) |
||||
} |
||||
} else { |
||||
r.addAll(attrgrps(c)) |
||||
} |
||||
} |
||||
r |
||||
} |
||||
|
||||
private def findNode(c,name) { |
||||
def root = c |
||||
while(root.name() != 'schema') { |
||||
root = root.parent() |
||||
} |
||||
def result = root.breadthFirst().find { child-> name == child.@name?.text() } |
||||
assert result?.@name?.text() == name |
||||
result |
||||
} |
||||
|
||||
/** |
||||
* Processes an individual attributeGroup by obtaining all the attributes and then looking for more attributeGroup elements and prcessing them. |
||||
* @param e |
||||
* @return all the attributes for a specific attributeGroup and any child attributeGroups |
||||
*/ |
||||
private def attrgrp(e) { |
||||
def attrs = attrs(e) |
||||
attrs.addAll(attrgrps(e)) |
||||
attrs |
||||
} |
||||
|
||||
/** |
||||
* Obtains the description for a specific element |
||||
* @param element |
||||
* @return |
||||
*/ |
||||
private def desc(element) { |
||||
return element['annotation']['documentation'] |
||||
} |
||||
|
||||
/** |
||||
* Given an element creates an attribute from it. |
||||
* @param n |
||||
* @return |
||||
*/ |
||||
private def attr(n) { |
||||
new Attribute(desc: desc(n), name: n.@name.text()) |
||||
} |
||||
|
||||
/** |
||||
* Given an element creates an Element out of it by collecting all its attributes and child elements. |
||||
* |
||||
* @param n |
||||
* @return |
||||
*/ |
||||
private def elmt(n) { |
||||
def name = n.@ref.text() |
||||
if(name) { |
||||
name = name.split(':')[1] |
||||
n = findNode(n,name) |
||||
} else { |
||||
name = n.@name.text() |
||||
} |
||||
if(elementNameToElement.containsKey(name)) { |
||||
return elementNameToElement.get(name) |
||||
} |
||||
attrElmts.add(name) |
||||
def e = new Element() |
||||
e.name = n.@name.text() |
||||
e.desc = desc(n) |
||||
e.childElmts = elements(n) |
||||
e.attrs = attrs(n) |
||||
e.attrs.addAll(attrgrps(n)) |
||||
e.attrs*.elmt = e |
||||
e.childElmts.values()*.each { it.parentElmts.put(e.name,e) } |
||||
|
||||
def subGrpName = n.@substitutionGroup.text() |
||||
if(subGrpName) { |
||||
def subGrp = elmt(findNode(n,subGrpName.split(":")[1])) |
||||
subGrp.subGrps.add(e) |
||||
} |
||||
|
||||
elementNameToElement.put(name,e) |
||||
e |
||||
} |
||||
} |
||||
@ -1,222 +0,0 @@
@@ -1,222 +0,0 @@
|
||||
/* |
||||
* Copyright 2011-2016 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc |
||||
|
||||
import groovy.util.slurpersupport.GPathResult |
||||
import spock.lang.* |
||||
|
||||
import org.springframework.security.config.http.SecurityFilters |
||||
|
||||
/** |
||||
* Tests to ensure that the xsd is properly documented. |
||||
* |
||||
* @author Rob Winch |
||||
*/ |
||||
class XsdDocumentedTests extends Specification { |
||||
|
||||
def ignoredIds = [ |
||||
'nsa-any-user-service', |
||||
'nsa-any-user-service-parents', |
||||
'nsa-authentication', |
||||
'nsa-websocket-security', |
||||
'nsa-ldap', |
||||
'nsa-method-security', |
||||
'nsa-web' |
||||
] |
||||
@Shared def reference = new File('../docs/manual/src/docs/asciidoc/_includes/appendix/namespace.adoc') |
||||
|
||||
@Shared File schema31xDocument = new File('src/main/resources/org/springframework/security/config/spring-security-3.1.xsd') |
||||
@Shared File schemaDocument = new File('src/main/resources/org/springframework/security/config/spring-security-5.0.xsd') |
||||
@Shared Map<String,Element> elementNameToElement |
||||
@Shared GPathResult schemaRootElement |
||||
|
||||
def setupSpec() { |
||||
schemaRootElement = new XmlSlurper().parse(schemaDocument) |
||||
elementNameToElement = new SpringSecurityXsdParser(rootElement: schemaRootElement).parse() |
||||
} |
||||
|
||||
def cleanupSpec() { |
||||
reference = null |
||||
schema31xDocument = null |
||||
schemaDocument = null |
||||
elementNameToElement = null |
||||
schemaRootElement = null |
||||
} |
||||
|
||||
def 'SEC-2139: named-security-filter are all defined and ordered properly'() { |
||||
setup: |
||||
def expectedFilters = (EnumSet.allOf(SecurityFilters) as List).sort { it.order } |
||||
when: |
||||
def nsf = schemaRootElement.simpleType.find { it.@name == 'named-security-filter' } |
||||
def nsfValues = nsf.children().children().collect { c -> |
||||
Enum.valueOf(SecurityFilters, c.@value.toString()) |
||||
} |
||||
then: |
||||
expectedFilters == nsfValues |
||||
} |
||||
|
||||
def 'SEC-2139: 3.1.x named-security-filter are all defined and ordered properly'() { |
||||
setup: |
||||
def expectedFilters = [ |
||||
"FIRST", |
||||
"CHANNEL_FILTER", |
||||
"SECURITY_CONTEXT_FILTER", |
||||
"CONCURRENT_SESSION_FILTER", |
||||
"LOGOUT_FILTER", |
||||
"X509_FILTER", |
||||
"PRE_AUTH_FILTER", |
||||
"CAS_FILTER", |
||||
"FORM_LOGIN_FILTER", |
||||
"OPENID_FILTER", |
||||
"LOGIN_PAGE_FILTER", |
||||
"DIGEST_AUTH_FILTER", |
||||
"BASIC_AUTH_FILTER", |
||||
"REQUEST_CACHE_FILTER", |
||||
"SERVLET_API_SUPPORT_FILTER", |
||||
"JAAS_API_SUPPORT_FILTER", |
||||
"REMEMBER_ME_FILTER", |
||||
"ANONYMOUS_FILTER", |
||||
"SESSION_MANAGEMENT_FILTER", |
||||
"EXCEPTION_TRANSLATION_FILTER", |
||||
"FILTER_SECURITY_INTERCEPTOR", |
||||
"SWITCH_USER_FILTER", |
||||
"LAST" |
||||
].collect { |
||||
Enum.valueOf(SecurityFilters, it) |
||||
} |
||||
def schema31xRootElement = new XmlSlurper().parse(schema31xDocument) |
||||
when: |
||||
def nsf = schema31xRootElement.simpleType.find { it.@name == 'named-security-filter' } |
||||
def nsfValues = nsf.children().children().collect { c -> |
||||
Enum.valueOf(SecurityFilters, c.@value.toString()) |
||||
} |
||||
then: |
||||
expectedFilters == nsfValues |
||||
} |
||||
|
||||
/** |
||||
* This will check to ensure that the expected number of xsd documents are found to ensure that we are validating |
||||
* against the current xsd document. If this test fails, all that is needed is to update the schemaDocument |
||||
* and the expected size for this test. |
||||
* @return |
||||
*/ |
||||
def 'the latest schema is being validated'() { |
||||
when: 'all the schemas are found' |
||||
def schemas = schemaDocument.getParentFile().list().findAll { it.endsWith('.xsd') } |
||||
then: 'the count is equal to 12, if not then schemaDocument needs updated' |
||||
schemas.size() == 12 |
||||
} |
||||
|
||||
/** |
||||
* This uses a naming convention for the ids of the appendix to ensure that the entire appendix is documented. |
||||
* The naming convention for the ids is documented in {@link Element#getIds()}. |
||||
* @return |
||||
*/ |
||||
def 'the entire schema is included in the appendix documentation'() { |
||||
setup: 'get all the documented ids and the expected ids' |
||||
def documentedIds = [] |
||||
reference.eachLine { line -> |
||||
if(line.matches("\\[\\[(nsa-.*)\\]\\]")) { |
||||
documentedIds.add(line.substring(2,line.length() - 2)) |
||||
} |
||||
} |
||||
when: 'the schema is compared to the appendix documentation' |
||||
def expectedIds = [] as Set |
||||
elementNameToElement*.value*.ids*.each { expectedIds.addAll it } |
||||
documentedIds.removeAll ignoredIds |
||||
expectedIds.removeAll ignoredIds |
||||
def undocumentedIds = (expectedIds - documentedIds) |
||||
def shouldNotBeDocumented = (documentedIds - expectedIds) |
||||
then: 'all the elements and attributes are documented' |
||||
shouldNotBeDocumented.empty |
||||
undocumentedIds.empty |
||||
} |
||||
|
||||
/** |
||||
* This test ensures that any element that has children or parents contains a section that has links pointing to that |
||||
* documentation. |
||||
* @return |
||||
*/ |
||||
def 'validate parents and children are linked in the appendix documentation'() { |
||||
when: "get all the links for each element's children and parents" |
||||
def docAttrNameToChildren = [:] |
||||
def docAttrNameToParents = [:] |
||||
|
||||
def currentDocAttrNameToElmt |
||||
def docAttrName |
||||
|
||||
reference.eachLine { line -> |
||||
if(line.matches('^\\[\\[.*\\]\\]$')) { |
||||
def id = line.substring(2,line.length() - 2) |
||||
if(id.endsWith("-children")) { |
||||
docAttrName = id.substring(0,id.length() - 9) |
||||
currentDocAttrNameToElmt = docAttrNameToChildren |
||||
} else if(id.endsWith("-parents")) { |
||||
docAttrName = id.substring(0,id.length() - 8) |
||||
currentDocAttrNameToElmt = docAttrNameToParents |
||||
} else if(docAttrName && !id.startsWith(docAttrName)) { |
||||
currentDocAttrNameToElmt = null |
||||
docAttrName = null |
||||
} |
||||
} |
||||
|
||||
if(docAttrName) { |
||||
def expression = '^\\* <<(nsa-.*),.*>>$' |
||||
if(line.matches(expression)) { |
||||
String elmtId = line.replaceAll(expression, '$1') |
||||
currentDocAttrNameToElmt.get(docAttrName, []).add(elmtId) |
||||
} |
||||
} |
||||
} |
||||
|
||||
def schemaAttrNameToParents = [:] |
||||
def schemaAttrNameToChildren = [:] |
||||
elementNameToElement.each { entry -> |
||||
def key = 'nsa-'+entry.key |
||||
if(ignoredIds.contains(key)) { |
||||
return |
||||
} |
||||
def parentIds = entry.value.allParentElmts.values()*.id.findAll { !ignoredIds.contains(it) }.sort() |
||||
if(parentIds) { |
||||
schemaAttrNameToParents.put(key,parentIds) |
||||
} |
||||
def childIds = entry.value.allChildElmts.values()*.id.findAll { !ignoredIds.contains(it) }.sort() |
||||
if(childIds) { |
||||
schemaAttrNameToChildren.put(key,childIds) |
||||
} |
||||
} |
||||
then: "the expected parents and children are all documented" |
||||
schemaAttrNameToChildren.sort() == docAttrNameToChildren.sort() |
||||
schemaAttrNameToParents.sort() == docAttrNameToParents.sort() |
||||
} |
||||
|
||||
/** |
||||
* This test checks each xsd element and ensures there is documentation for it. |
||||
* @return |
||||
*/ |
||||
def 'entire xsd is documented'() { |
||||
when: "validate that the entire xsd contains documentation" |
||||
def notDocElmtIds = elementNameToElement.values().findAll { |
||||
!it.desc.text() && !ignoredIds.contains(it.id) |
||||
}*.id.sort().join("\n") |
||||
def notDocAttrIds = elementNameToElement.values()*.attrs.flatten().findAll { |
||||
!it.desc.text() && !ignoredIds.contains(it.id) |
||||
}*.id.sort().join("\n") |
||||
then: "all the elements and attributes have some documentation" |
||||
!notDocElmtIds |
||||
!notDocAttrIds |
||||
} |
||||
} |
||||
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
/** |
||||
* Represents a Spring Security XSD Attribute. It is created when parsing the current xsd to compare to the documented appendix. |
||||
* |
||||
* @author Rob Winch |
||||
* @author Josh Cummings |
||||
* |
||||
* @see SpringSecurityXsdParser |
||||
* @see XsdDocumentedTests |
||||
*/ |
||||
public class Attribute { |
||||
private String name; |
||||
|
||||
private String desc; |
||||
|
||||
private Element elmt; |
||||
|
||||
public Attribute(String desc, String name) { |
||||
this.desc = desc; |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getDesc() { |
||||
return this.desc; |
||||
} |
||||
|
||||
public void setDesc(String desc) { |
||||
this.desc = desc; |
||||
} |
||||
|
||||
public Element getElmt() { |
||||
return this.elmt; |
||||
} |
||||
|
||||
public void setElmt(Element elmt) { |
||||
this.elmt = elmt; |
||||
} |
||||
|
||||
public String getId() { |
||||
return String.format("%s-%s", this.elmt.getId(), this.name); |
||||
} |
||||
} |
||||
@ -0,0 +1,169 @@
@@ -0,0 +1,169 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.HashMap; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* Represents a Spring Security XSD Element. It is created when parsing |
||||
* the current xsd to compare to the documented appendix. |
||||
* |
||||
* @author Rob Winch |
||||
* @author Josh Cummings |
||||
* |
||||
* @see SpringSecurityXsdParser |
||||
* @see XsdDocumentedTests |
||||
*/ |
||||
public class Element { |
||||
private String name; |
||||
private String desc; |
||||
private Collection<Attribute> attrs = new ArrayList<>(); |
||||
|
||||
/** |
||||
* Contains the elements that extend this element (i.e. any-user-service contains ldap-user-service) |
||||
*/ |
||||
private Collection<Element> subGrps = new ArrayList<>(); |
||||
private Map<String, Element> childElmts = new HashMap<>(); |
||||
private Map<String, Element> parentElmts = new HashMap<>(); |
||||
|
||||
public String getId() { |
||||
return String.format("nsa-%s", this.name); |
||||
} |
||||
|
||||
public String getName() { |
||||
return this.name; |
||||
} |
||||
|
||||
public void setName(String name) { |
||||
this.name = name; |
||||
} |
||||
|
||||
public String getDesc() { |
||||
return this.desc; |
||||
} |
||||
|
||||
public void setDesc(String desc) { |
||||
this.desc = desc; |
||||
} |
||||
|
||||
public Collection<Attribute> getAttrs() { |
||||
return this.attrs; |
||||
} |
||||
|
||||
public void setAttrs(Collection<Attribute> attrs) { |
||||
this.attrs = attrs; |
||||
} |
||||
|
||||
public Collection<Element> getSubGrps() { |
||||
return this.subGrps; |
||||
} |
||||
|
||||
public void setSubGrps(Collection<Element> subGrps) { |
||||
this.subGrps = subGrps; |
||||
} |
||||
|
||||
public Map<String, Element> getChildElmts() { |
||||
return this.childElmts; |
||||
} |
||||
|
||||
public void setChildElmts(Map<String, Element> childElmts) { |
||||
this.childElmts = childElmts; |
||||
} |
||||
|
||||
public Map<String, Element> getParentElmts() { |
||||
return this.parentElmts; |
||||
} |
||||
|
||||
public void setParentElmts(Map<String, Element> parentElmts) { |
||||
this.parentElmts = parentElmts; |
||||
} |
||||
|
||||
/** |
||||
* Gets all the ids related to this Element including attributes, parent elements, and child elements. |
||||
* |
||||
* <p> |
||||
* The expected ids to be found are documented below. |
||||
* <ul> |
||||
* <li>Elements - any xml element will have the nsa-<element>. For example the http element will have the id |
||||
* nsa-http</li> |
||||
* <li>Parent Section - Any element with a parent other than beans will have a section named |
||||
* nsa-<element>-parents. For example, authentication-provider would have a section id of |
||||
* nsa-authentication-provider-parents. The section would then contain a list of links pointing to the |
||||
* documentation for each parent element.</li> |
||||
* <li>Attributes Section - Any element with attributes will have a section with the id |
||||
* nsa-<element>-attributes. For example the http element would require a section with the id |
||||
* http-attributes.</li> |
||||
* <li>Attribute - Each attribute of an element would have an id of nsa-<element>-<attributeName>. For |
||||
* example the attribute create-session for the http attribute would have the id http-create-session.</li> |
||||
* <li>Child Section - Any element with a child element will have a section named nsa-<element>-children. |
||||
* For example, authentication-provider would have a section id of nsa-authentication-provider-children. The |
||||
* section would then contain a list of links pointing to the documentation for each child element.</li> |
||||
* </ul> |
||||
* @return |
||||
*/ |
||||
public Collection<String> getIds() { |
||||
Collection<String> ids = new ArrayList<>(); |
||||
ids.add(getId()); |
||||
|
||||
this.childElmts.values() |
||||
.forEach(elmt -> ids.add(elmt.getId())); |
||||
|
||||
this.attrs.forEach(attr -> ids.add(attr.getId())); |
||||
|
||||
if ( !this.childElmts.isEmpty() ) { |
||||
ids.add(getId() + "-children"); |
||||
} |
||||
|
||||
if ( !this.attrs.isEmpty() ) { |
||||
ids.add(getId() + "-attributes"); |
||||
} |
||||
|
||||
if ( !this.parentElmts.isEmpty() ) { |
||||
ids.add(getId() + "-parents"); |
||||
} |
||||
|
||||
return ids; |
||||
} |
||||
|
||||
public Map<String, Element> getAllChildElmts() { |
||||
Map<String, Element> result = new HashMap<>(); |
||||
|
||||
this.childElmts.values() |
||||
.forEach(elmt -> |
||||
elmt.subGrps.forEach( |
||||
subElmt -> result.put(subElmt.name, subElmt))); |
||||
|
||||
result.putAll(this.childElmts); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
public Map<String, Element> getAllParentElmts() { |
||||
Map<String, Element> result = new HashMap<>(); |
||||
|
||||
this.parentElmts.values() |
||||
.forEach(elmt -> |
||||
elmt.subGrps.forEach( |
||||
subElmt -> result.put(subElmt.name, subElmt))); |
||||
|
||||
result.putAll(this.parentElmts); |
||||
|
||||
return result; |
||||
} |
||||
} |
||||
@ -0,0 +1,73 @@
@@ -0,0 +1,73 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
import org.w3c.dom.Node; |
||||
import org.w3c.dom.NodeList; |
||||
|
||||
import java.util.Optional; |
||||
import java.util.stream.IntStream; |
||||
import java.util.stream.Stream; |
||||
|
||||
/** |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class NicerNode { |
||||
private final Node node; |
||||
|
||||
public NicerNode(Node node) { |
||||
this.node = node; |
||||
} |
||||
|
||||
public String simpleName() { |
||||
String[] parts = this.node.getNodeName().split(":"); |
||||
return parts[parts.length-1]; |
||||
} |
||||
|
||||
public String text() { |
||||
return this.node.getTextContent(); |
||||
} |
||||
|
||||
public Stream<NicerNode> children() { |
||||
NodeList children = this.node.getChildNodes(); |
||||
|
||||
return IntStream.range(0, children.getLength()) |
||||
.mapToObj(children::item) |
||||
.map(NicerNode::new); |
||||
} |
||||
|
||||
public Optional<NicerNode> child(String name) { |
||||
return this.children() |
||||
.filter(child -> name.equals(child.simpleName())) |
||||
.findFirst(); |
||||
} |
||||
|
||||
public Optional<NicerNode> parent() { |
||||
return Optional.ofNullable(this.node.getParentNode()) |
||||
.map(parent -> new NicerNode(parent)); |
||||
} |
||||
|
||||
public String attribute(String name) { |
||||
return Optional.ofNullable(this.node.getAttributes()) |
||||
.map(attrs -> attrs.getNamedItem(name)) |
||||
.map(attr -> attr.getTextContent()) |
||||
.orElse(null); |
||||
} |
||||
|
||||
public Node node() { |
||||
return this.node; |
||||
} |
||||
} |
||||
@ -0,0 +1,51 @@
@@ -0,0 +1,51 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
import org.xml.sax.SAXException; |
||||
|
||||
import javax.xml.parsers.DocumentBuilder; |
||||
import javax.xml.parsers.DocumentBuilderFactory; |
||||
import javax.xml.parsers.ParserConfigurationException; |
||||
import java.io.IOException; |
||||
import java.io.InputStream; |
||||
|
||||
/** |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class NicerXmlParser implements AutoCloseable { |
||||
private InputStream xml; |
||||
|
||||
public NicerXmlParser(InputStream xml) { |
||||
this.xml = xml; |
||||
} |
||||
|
||||
public NicerNode parse() { |
||||
try { |
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); |
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); |
||||
|
||||
return new NicerNode(dBuilder.parse(this.xml)); |
||||
} catch ( IOException | ParserConfigurationException | SAXException e ) { |
||||
throw new IllegalStateException(e); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void close() throws IOException { |
||||
this.xml.close(); |
||||
} |
||||
} |
||||
@ -0,0 +1,48 @@
@@ -0,0 +1,48 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
import org.springframework.core.io.ClassPathResource; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Map; |
||||
|
||||
/** |
||||
* Support for ensuring preparing the givens in {@link XsdDocumentedTests} |
||||
* |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class NicerXmlSupport { |
||||
private NicerXmlParser parser; |
||||
|
||||
public NicerNode parse(String location) throws IOException { |
||||
ClassPathResource resource = new ClassPathResource(location); |
||||
this.parser = new NicerXmlParser(resource.getInputStream()); |
||||
|
||||
return this.parser.parse(); |
||||
} |
||||
|
||||
public Map<String, Element> elementsByElementName(String location) throws IOException { |
||||
NicerNode node = parse(location); |
||||
return new SpringSecurityXsdParser(node).parse(); |
||||
} |
||||
|
||||
public void close() throws IOException { |
||||
if ( this.parser != null ) { |
||||
this.parser.close(); |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,211 @@
@@ -0,0 +1,211 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
import org.springframework.util.StringUtils; |
||||
|
||||
import java.util.*; |
||||
import java.util.stream.Stream; |
||||
|
||||
/** |
||||
* Parses the Spring Security Xsd Document |
||||
* |
||||
* @author Rob Winch |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class SpringSecurityXsdParser { |
||||
private NicerNode rootElement; |
||||
|
||||
private Set<String> attrElmts = new LinkedHashSet<>(); |
||||
private Map<String, Element> elementNameToElement = new HashMap<>(); |
||||
|
||||
public SpringSecurityXsdParser(NicerNode rootElement) { |
||||
this.rootElement = rootElement; |
||||
} |
||||
|
||||
/** |
||||
* Returns a map of the element name to the {@link Element}. |
||||
* |
||||
* @return |
||||
*/ |
||||
public Map<String, Element> parse() { |
||||
elements(this.rootElement); |
||||
return this.elementNameToElement; |
||||
} |
||||
|
||||
/** |
||||
* Creates a Map of the name to an Element object of all the children of element. |
||||
* |
||||
* @param node |
||||
* @return |
||||
*/ |
||||
private Map<String, Element> elements(NicerNode node) { |
||||
Map<String, Element> elementNameToElement = new HashMap<>(); |
||||
|
||||
node.children().forEach(child -> { |
||||
if ("element".equals(child.simpleName())) { |
||||
Element e = elmt(child); |
||||
elementNameToElement.put(e.getName(), e); |
||||
} else { |
||||
elementNameToElement.putAll(elements(child)); |
||||
} |
||||
}); |
||||
|
||||
return elementNameToElement; |
||||
} |
||||
|
||||
/** |
||||
* Any children that are attribute will be returned as an Attribute object. |
||||
* |
||||
* @param element |
||||
* @return a collection of Attribute objects that are children of element. |
||||
*/ |
||||
private Collection<Attribute> attrs(NicerNode element) { |
||||
Collection<Attribute> attrs = new ArrayList<>(); |
||||
element.children().forEach(c -> { |
||||
String name = c.simpleName(); |
||||
if ("attribute".equals(name)) { |
||||
attrs.add(attr(c)); |
||||
} else if ("element".equals(name)) { |
||||
} else { |
||||
attrs.addAll(attrs(c)); |
||||
} |
||||
}); |
||||
|
||||
return attrs; |
||||
} |
||||
|
||||
/** |
||||
* Any children will be searched for an attributeGroup, each of its children will be returned as an Attribute |
||||
* |
||||
* @param element |
||||
* @return |
||||
*/ |
||||
private Collection<Attribute> attrgrps(NicerNode element) { |
||||
Collection<Attribute> attrgrp = new ArrayList<>(); |
||||
|
||||
element.children().forEach(c -> { |
||||
if ("element".equals(c.simpleName())) { |
||||
|
||||
} else if ("attributeGroup".equals(c.simpleName())) { |
||||
if (c.attribute("name") != null) { |
||||
attrgrp.addAll(attrgrp(c)); |
||||
} else { |
||||
String name = c.attribute("ref").split(":")[1]; |
||||
NicerNode attrGrp = findNode(element, name); |
||||
attrgrp.addAll(attrgrp(attrGrp)); |
||||
} |
||||
} else { |
||||
attrgrp.addAll(attrgrps(c)); |
||||
} |
||||
}); |
||||
|
||||
return attrgrp; |
||||
} |
||||
|
||||
private NicerNode findNode(NicerNode c, String name) { |
||||
NicerNode root = c; |
||||
while (!"schema".equals(root.simpleName())) { |
||||
root = root.parent().get(); |
||||
} |
||||
|
||||
return expand(root) |
||||
.filter(node -> name.equals(node.attribute("name"))) |
||||
.findFirst().orElseThrow(IllegalArgumentException::new); |
||||
} |
||||
|
||||
private Stream<NicerNode> expand(NicerNode root) { |
||||
return Stream.concat( |
||||
Stream.of(root), |
||||
root.children().flatMap(this::expand)); |
||||
} |
||||
|
||||
/** |
||||
* Processes an individual attributeGroup by obtaining all the attributes and then looking for more attributeGroup elements and prcessing them. |
||||
* |
||||
* @param e |
||||
* @return all the attributes for a specific attributeGroup and any child attributeGroups |
||||
*/ |
||||
private Collection<Attribute> attrgrp(NicerNode e) { |
||||
Collection<Attribute> attrs = attrs(e); |
||||
attrs.addAll(attrgrps(e)); |
||||
return attrs; |
||||
} |
||||
|
||||
/** |
||||
* Obtains the description for a specific element |
||||
* |
||||
* @param element |
||||
* @return |
||||
*/ |
||||
private String desc(NicerNode element) { |
||||
return element.child("annotation") |
||||
.flatMap(annotation -> annotation.child("documentation")) |
||||
.map(documentation -> documentation.text()) |
||||
.orElse(null); |
||||
} |
||||
|
||||
/** |
||||
* Given an element creates an attribute from it. |
||||
* |
||||
* @param n |
||||
* @return |
||||
*/ |
||||
private Attribute attr(NicerNode n) { |
||||
return new Attribute(desc(n), n.attribute("name")); |
||||
} |
||||
|
||||
/** |
||||
* Given an element creates an Element out of it by collecting all its attributes and child elements. |
||||
* |
||||
* @param n |
||||
* @return |
||||
*/ |
||||
private Element elmt(NicerNode n) { |
||||
String name = n.attribute("ref"); |
||||
if (StringUtils.isEmpty(name)) { |
||||
name = n.attribute("name"); |
||||
} else { |
||||
name = name.split(":")[1]; |
||||
n = findNode(n, name); |
||||
} |
||||
|
||||
if (this.elementNameToElement.containsKey(name)) { |
||||
return this.elementNameToElement.get(name); |
||||
} |
||||
this.attrElmts.add(name); |
||||
|
||||
Element e = new Element(); |
||||
e.setName(n.attribute("name")); |
||||
e.setDesc(desc(n)); |
||||
e.setChildElmts(elements(n)); |
||||
e.setAttrs(attrs(n)); |
||||
e.getAttrs().addAll(attrgrps(n)); |
||||
e.getAttrs().forEach(attr -> attr.setElmt(e)); |
||||
e.getChildElmts().values().forEach(element -> |
||||
element.getParentElmts().put(e.getName(), e)); |
||||
|
||||
String subGrpName = n.attribute("substitutionGroup"); |
||||
if (!StringUtils.isEmpty(subGrpName)) { |
||||
Element subGrp = elmt(findNode(n, subGrpName.split(":")[1])); |
||||
subGrp.getSubGrps().add(e); |
||||
} |
||||
|
||||
this.elementNameToElement.put(name, e); |
||||
|
||||
return e; |
||||
} |
||||
} |
||||
@ -0,0 +1,293 @@
@@ -0,0 +1,293 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.doc; |
||||
|
||||
import org.apache.commons.lang.StringUtils; |
||||
import org.junit.After; |
||||
import org.junit.Test; |
||||
import org.springframework.core.io.ClassPathResource; |
||||
import org.springframework.security.config.http.SecurityFiltersAssertions; |
||||
|
||||
import java.io.IOException; |
||||
import java.nio.file.Files; |
||||
import java.nio.file.Paths; |
||||
import java.util.*; |
||||
import java.util.stream.Collectors; |
||||
import java.util.stream.Stream; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Tests to ensure that the xsd is properly documented. |
||||
* |
||||
* @author Rob Winch |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class XsdDocumentedTests { |
||||
|
||||
Collection<String> ignoredIds = Arrays.asList( |
||||
"nsa-any-user-service", |
||||
"nsa-any-user-service-parents", |
||||
"nsa-authentication", |
||||
"nsa-websocket-security", |
||||
"nsa-ldap", |
||||
"nsa-method-security", |
||||
"nsa-web"); |
||||
|
||||
String referenceLocation = "../docs/manual/src/docs/asciidoc/_includes/appendix/namespace.adoc"; |
||||
|
||||
String schema31xDocumentLocation = "org/springframework/security/config/spring-security-3.1.xsd"; |
||||
String schemaDocumentLocation = "org/springframework/security/config/spring-security-5.0.xsd"; |
||||
|
||||
NicerXmlSupport xml = new NicerXmlSupport(); |
||||
|
||||
@After |
||||
public void close() throws IOException { |
||||
this.xml.close(); |
||||
} |
||||
|
||||
@Test |
||||
public void parseWhenLatestXsdThenAllNamedSecurityFiltersAreDefinedAndOrderedProperly() |
||||
throws IOException { |
||||
NicerNode root = this.xml.parse(this.schemaDocumentLocation); |
||||
|
||||
List<String> nodes = |
||||
root.child("schema") |
||||
.map(NicerNode::children) |
||||
.orElse(Stream.empty()) |
||||
.filter(node -> |
||||
"simpleType".equals(node.simpleName()) && |
||||
"named-security-filter".equals(node.attribute("name"))) |
||||
.flatMap(NicerNode::children) |
||||
.flatMap(NicerNode::children) |
||||
.map(node -> node.attribute("value")) |
||||
.filter(StringUtils::isNotEmpty) |
||||
.collect(Collectors.toList()); |
||||
|
||||
SecurityFiltersAssertions.assertEquals(nodes); |
||||
} |
||||
|
||||
@Test |
||||
public void parseWhen31XsdThenAllNamedSecurityFiltersAreDefinedAndOrderedProperly() |
||||
throws IOException { |
||||
|
||||
List<String> expected = Arrays.asList( |
||||
"FIRST", |
||||
"CHANNEL_FILTER", |
||||
"SECURITY_CONTEXT_FILTER", |
||||
"CONCURRENT_SESSION_FILTER", |
||||
"LOGOUT_FILTER", |
||||
"X509_FILTER", |
||||
"PRE_AUTH_FILTER", |
||||
"CAS_FILTER", |
||||
"FORM_LOGIN_FILTER", |
||||
"OPENID_FILTER", |
||||
"LOGIN_PAGE_FILTER", |
||||
"DIGEST_AUTH_FILTER", |
||||
"BASIC_AUTH_FILTER", |
||||
"REQUEST_CACHE_FILTER", |
||||
"SERVLET_API_SUPPORT_FILTER", |
||||
"JAAS_API_SUPPORT_FILTER", |
||||
"REMEMBER_ME_FILTER", |
||||
"ANONYMOUS_FILTER", |
||||
"SESSION_MANAGEMENT_FILTER", |
||||
"EXCEPTION_TRANSLATION_FILTER", |
||||
"FILTER_SECURITY_INTERCEPTOR", |
||||
"SWITCH_USER_FILTER", |
||||
"LAST" |
||||
); |
||||
|
||||
NicerNode root = this.xml.parse(this.schema31xDocumentLocation); |
||||
|
||||
List<String> nodes = |
||||
root.child("schema") |
||||
.map(NicerNode::children) |
||||
.orElse(Stream.empty()) |
||||
.filter(node -> |
||||
"simpleType".equals(node.simpleName()) && |
||||
"named-security-filter".equals(node.attribute("name"))) |
||||
.flatMap(NicerNode::children) |
||||
.flatMap(NicerNode::children) |
||||
.map(node -> node.attribute("value")) |
||||
.filter(StringUtils::isNotEmpty) |
||||
.collect(Collectors.toList()); |
||||
|
||||
assertThat(nodes).isEqualTo(expected); |
||||
} |
||||
|
||||
/** |
||||
* This will check to ensure that the expected number of xsd documents are found to ensure that we are validating |
||||
* against the current xsd document. If this test fails, all that is needed is to update the schemaDocument |
||||
* and the expected size for this test. |
||||
* @return |
||||
*/ |
||||
@Test |
||||
public void sizeWhenReadingFilesystemThenIsCorrectNumberOfSchemaFiles() |
||||
throws IOException { |
||||
|
||||
ClassPathResource resource = new ClassPathResource(this.schemaDocumentLocation); |
||||
|
||||
String[] schemas = resource.getFile().getParentFile().list((dir, name) -> name.endsWith(".xsd")); |
||||
|
||||
assertThat(schemas.length).isEqualTo(12) |
||||
.withFailMessage("the count is equal to 12, if not then schemaDocument needs updating"); |
||||
} |
||||
|
||||
/** |
||||
* This uses a naming convention for the ids of the appendix to ensure that the entire appendix is documented. |
||||
* The naming convention for the ids is documented in {@link Element#getIds()}. |
||||
* @return |
||||
*/ |
||||
@Test |
||||
public void countReferencesWhenReviewingDocumentationThenEntireSchemaIsIncluded() |
||||
throws IOException { |
||||
|
||||
Map<String, Element> elementsByElementName = |
||||
this.xml.elementsByElementName(this.schemaDocumentLocation); |
||||
|
||||
List<String> documentIds = |
||||
Files.lines(Paths.get(this.referenceLocation)) |
||||
.filter(line -> line.matches("\\[\\[(nsa-.*)\\]\\]")) |
||||
.map(line -> line.substring(2, line.length() - 2)) |
||||
.collect(Collectors.toList()); |
||||
|
||||
Set<String> expectedIds = |
||||
elementsByElementName.values().stream() |
||||
.flatMap(element -> element.getIds().stream()) |
||||
.collect(Collectors.toSet()); |
||||
|
||||
documentIds.removeAll(this.ignoredIds); |
||||
expectedIds.removeAll(this.ignoredIds); |
||||
|
||||
assertThat(documentIds).containsAll(expectedIds); |
||||
assertThat(expectedIds).containsAll(documentIds); |
||||
} |
||||
|
||||
/** |
||||
* This test ensures that any element that has children or parents contains a section that has links pointing to that |
||||
* documentation. |
||||
* @return |
||||
*/ |
||||
@Test |
||||
public void countLinksWhenReviewingDocumentationThenParentsAndChildrenAreCorrectlyLinked() |
||||
throws IOException { |
||||
|
||||
Map<String, List<String>> docAttrNameToChildren = new HashMap<>(); |
||||
Map<String, List<String>> docAttrNameToParents = new HashMap<>(); |
||||
|
||||
String docAttrName = null; |
||||
Map<String, List<String>> currentDocAttrNameToElmt = null; |
||||
|
||||
List<String> lines = Files.readAllLines(Paths.get(this.referenceLocation)); |
||||
for ( String line : lines ) { |
||||
if(line.matches("^\\[\\[.*\\]\\]$")) { |
||||
String id = line.substring(2, line.length() - 2); |
||||
|
||||
if(id.endsWith("-children")) { |
||||
docAttrName = id.substring(0, id.length() - 9); |
||||
currentDocAttrNameToElmt = docAttrNameToChildren; |
||||
} else if(id.endsWith("-parents")) { |
||||
docAttrName = id.substring(0, id.length() - 8); |
||||
currentDocAttrNameToElmt = docAttrNameToParents; |
||||
} else if(docAttrName != null && !id.startsWith(docAttrName)) { |
||||
currentDocAttrNameToElmt = null; |
||||
docAttrName = null; |
||||
} |
||||
} |
||||
|
||||
if(docAttrName != null && currentDocAttrNameToElmt != null) { |
||||
String expression = "^\\* <<(nsa-.*),.*>>$"; |
||||
if(line.matches(expression)) { |
||||
String elmtId = line.replaceAll(expression, "$1"); |
||||
currentDocAttrNameToElmt |
||||
.computeIfAbsent(docAttrName, key -> new ArrayList<>()) |
||||
.add(elmtId); |
||||
} |
||||
} |
||||
} |
||||
|
||||
Map<String, Element> elementNameToElement = this.xml.elementsByElementName(this.schemaDocumentLocation); |
||||
|
||||
Map<String, List<String>> schemaAttrNameToChildren = new HashMap<>(); |
||||
Map<String, List<String>> schemaAttrNameToParents = new HashMap<>(); |
||||
|
||||
elementNameToElement.entrySet().stream() |
||||
.forEach(entry -> { |
||||
String key = "nsa-" + entry.getKey(); |
||||
if (this.ignoredIds.contains(key) ) { |
||||
return; |
||||
} |
||||
|
||||
List<String> parentIds = |
||||
entry.getValue().getAllParentElmts().values().stream() |
||||
.filter(element -> !this.ignoredIds.contains(element.getId())) |
||||
.map(element -> element.getId()) |
||||
.sorted() |
||||
.collect(Collectors.toList()); |
||||
if ( !parentIds.isEmpty() ) { |
||||
schemaAttrNameToParents.put(key, parentIds); |
||||
} |
||||
|
||||
List<String> childIds = |
||||
entry.getValue().getAllChildElmts().values().stream() |
||||
.filter(element -> !this.ignoredIds.contains(element.getId())) |
||||
.map(element -> element.getId()) |
||||
.sorted() |
||||
.collect(Collectors.toList()); |
||||
if ( !childIds.isEmpty() ) { |
||||
schemaAttrNameToChildren.put(key, childIds); |
||||
} |
||||
}); |
||||
|
||||
assertThat(docAttrNameToChildren).isEqualTo(schemaAttrNameToChildren); |
||||
assertThat(docAttrNameToParents).isEqualTo(schemaAttrNameToParents); |
||||
} |
||||
|
||||
|
||||
/** |
||||
* This test checks each xsd element and ensures there is documentation for it. |
||||
* @return |
||||
*/ |
||||
@Test |
||||
public void countWhenReviewingDocumentationThenAllElementsDocumented() |
||||
throws IOException { |
||||
|
||||
Map<String, Element> elementNameToElement = |
||||
this.xml.elementsByElementName(this.schemaDocumentLocation); |
||||
|
||||
String notDocElmtIds = |
||||
elementNameToElement.values().stream() |
||||
.filter(element -> |
||||
StringUtils.isEmpty(element.getDesc()) && |
||||
!this.ignoredIds.contains(element.getId())) |
||||
.map(element -> element.getId()) |
||||
.sorted() |
||||
.collect(Collectors.joining("\n")); |
||||
|
||||
String notDocAttrIds = |
||||
elementNameToElement.values().stream() |
||||
.flatMap(element -> element.getAttrs().stream()) |
||||
.filter(element -> |
||||
StringUtils.isEmpty(element.getDesc()) && |
||||
!this.ignoredIds.contains(element.getId())) |
||||
.map(element -> element.getId()) |
||||
.sorted() |
||||
.collect(Collectors.joining("\n")); |
||||
|
||||
assertThat(notDocElmtIds).isEmpty(); |
||||
assertThat(notDocAttrIds).isEmpty(); |
||||
} |
||||
} |
||||
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
/* |
||||
* Copyright 2002-2018 the original author or authors. |
||||
* |
||||
* 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.springframework.security.config.http; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
/** |
||||
* Assertions for tests that rely on confirming behavior of the package-private SecurityFilters enum |
||||
* |
||||
* @author Josh Cummings |
||||
*/ |
||||
public class SecurityFiltersAssertions { |
||||
private static Collection<SecurityFilters> ordered = Arrays.asList(SecurityFilters.values()); |
||||
|
||||
public static void assertEquals(List<String> filters) { |
||||
List<String> expected = ordered.stream() |
||||
.map(SecurityFilters::name) |
||||
.collect(Collectors.toList()); |
||||
|
||||
assertThat(filters).isEqualTo(expected); |
||||
} |
||||
} |
||||
Loading…
Reference in new issue