001package gov.nist.secauto.oscal.lib.model; 002 003import gov.nist.secauto.metaschema.binding.model.annotations.AllowedValue; 004import gov.nist.secauto.metaschema.binding.model.annotations.AllowedValues; 005import gov.nist.secauto.metaschema.binding.model.annotations.BoundAssembly; 006import gov.nist.secauto.metaschema.binding.model.annotations.BoundField; 007import gov.nist.secauto.metaschema.binding.model.annotations.BoundFieldValue; 008import gov.nist.secauto.metaschema.binding.model.annotations.BoundFlag; 009import gov.nist.secauto.metaschema.binding.model.annotations.GroupAs; 010import gov.nist.secauto.metaschema.binding.model.annotations.MetaschemaAssembly; 011import gov.nist.secauto.metaschema.binding.model.annotations.ValueConstraints; 012import gov.nist.secauto.metaschema.model.common.JsonGroupAsBehavior; 013import gov.nist.secauto.metaschema.model.common.constraint.IConstraint; 014import gov.nist.secauto.metaschema.model.common.datatype.adapter.TokenAdapter; 015import gov.nist.secauto.metaschema.model.common.datatype.adapter.UriAdapter; 016import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupLine; 017import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupLineAdapter; 018import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupMultiline; 019import gov.nist.secauto.metaschema.model.common.datatype.markup.MarkupMultilineAdapter; 020import gov.nist.secauto.metaschema.model.common.util.ObjectUtils; 021import gov.nist.secauto.oscal.lib.model.control.AbstractPart; 022import java.lang.Override; 023import java.lang.String; 024import java.net.URI; 025import java.util.LinkedList; 026import java.util.List; 027import org.apache.commons.lang3.builder.MultilineRecursiveToStringStyle; 028import org.apache.commons.lang3.builder.ReflectionToStringBuilder; 029 030/** 031 * An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part. 032 */ 033@MetaschemaAssembly( 034 formalName = "Part", 035 description = "An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.", 036 name = "part", 037 metaschema = OscalControlCommonMetaschema.class, 038 remarks = "A `part` provides for logical partitioning of prose, and can be thought of as a grouping structure (e.g., section). A `part` can have child parts allowing for arbitrary nesting of prose content (e.g., statement hierarchy). A `part` can contain `prop` objects that allow for enriching prose text with structured name/value information.\n" 039 + "\n" 040 + "A `part` can be assigned an optional `id`, which allows references to this part from within a catalog, or within an instance of another OSCAL model that has a need to reference the part. Examples of where part referencing is used in OSCAL include:\n" 041 + "\n" 042 + "- Referencing a part by id to tailor (make modifications to) a control statement in a profile.\n" 043 + "- Referencing a control statement represented by a part in a system security plan implemented-requirement where a statement-level response is desired.\n" 044 + "\n" 045 + "Use of `part` and `prop` provides for a wide degree of extensibility within the OSCAL catalog model. The optional `ns` provides a means to qualify a part's `name`, allowing for organization-specific vocabularies to be defined with clear semantics. Any organization that extends OSCAL in this way should consistently assign a `ns` value that represents the organization, making a given namespace qualified `name` unique to that organization. This allows the combination of `ns` and `name` to always be unique and unambiguous, even when mixed with extensions from other organizations. Each organization is responsible for governance of their own extensions, and is strongly encouraged to publish their extensions as standards to their user community. If no `ns` is provided, the name is expected to be in the \"OSCAL\" namespace.\n" 046 + "\n" 047 + "To ensure a `ns` is unique to an organization and naming conflicts are avoided, a URI containing a DNS or other globally defined organization name should be used. For example, if FedRAMP and DoD both extend OSCAL, FedRAMP will use the `ns` `http://fedramp.gov/ns/oscal`, while DoD might use the `ns` `https://defense.gov` for any organization specific `name`.\n" 048 + "\n" 049 + "Tools that process OSCAL content are not required to interpret unrecognized OSCAL extensions; however, OSCAL compliant tools should not modify or remove unrecognized extensions, unless there is a compelling reason to do so, such as data sensitivity." 050) 051@ValueConstraints( 052 allowedValues = @AllowedValues(level = IConstraint.Level.ERROR, target = "prop[has-oscal-namespace('http://csrc.nist.gov/ns/oscal')]/@name", values = {@AllowedValue(value = "label", description = "A human-readable label for the parent context, which may be rendered in place of the actual identifier for some use cases."), @AllowedValue(value = "sort-id", description = "An alternative identifier, whose value is easily sortable among other such values in the document."), @AllowedValue(value = "alt-identifier", description = "An alternate or aliased identifier for the parent context.")}) 053) 054public class ControlPart extends AbstractPart { 055 @BoundFlag( 056 formalName = "Part Identifier", 057 description = "A unique identifier for the part.", 058 useName = "id", 059 typeAdapter = TokenAdapter.class, 060 remarks = "While a part is not required to have an id, it is often desirable for an identifier to be provided, which allows the part to be referenced elsewhere in OSCAL document instances. For this reason, it is RECOMMENDED to provide a part identifier." 061 ) 062 private String _id; 063 064 @BoundFlag( 065 formalName = "Part Name", 066 description = "A textual label that uniquely identifies the part's semantic type, which exists in a value space qualified by the `ns`.", 067 useName = "name", 068 required = true, 069 typeAdapter = TokenAdapter.class 070 ) 071 private String _name; 072 073 @BoundFlag( 074 formalName = "Part Namespace", 075 description = "An optional namespace qualifying the part's `name`. This allows different organizations to associate distinct semantics with the same name.", 076 useName = "ns", 077 typeAdapter = UriAdapter.class, 078 remarks = "This value must be an [absolute URI](https://pages.nist.gov/OSCAL/concepts/uri-use/#absolute-uri) that serves as a [naming system identifier](https://pages.nist.gov/OSCAL/concepts/uri-use/#use-as-a-naming-system-identifier).\n" 079 + "\n" 080 + "When a `ns` is not provided, its value should be assumed to be `http://csrc.nist.gov/ns/oscal` and the name should be a name defined by the associated OSCAL model." 081 ) 082 private URI _ns; 083 084 @BoundFlag( 085 formalName = "Part Class", 086 description = "An optional textual providing a sub-type or characterization of the part's `name`, or a category to which the part belongs.", 087 useName = "class", 088 typeAdapter = TokenAdapter.class, 089 remarks = "One use of this flag is to distinguish or discriminate between the semantics of multiple parts of the same control with the same `name` and `ns` (since even within a given namespace it can be useful to overload a name).\n" 090 + "\n" 091 + "A `class` can be used in validation rules to express extra constraints over named items of a specific `class` value.\n" 092 + "\n" 093 + "A `class` can also be used in an OSCAL profile as a means to target an alteration to control content." 094 ) 095 private String _clazz; 096 097 /** 098 * "An optional name given to the part, which may be used by a tool for display and navigation." 099 */ 100 @BoundField( 101 formalName = "Part Title", 102 description = "An optional name given to the part, which may be used by a tool for display and navigation.", 103 useName = "title" 104 ) 105 @BoundFieldValue( 106 typeAdapter = MarkupLineAdapter.class 107 ) 108 private MarkupLine _title; 109 110 @BoundAssembly( 111 formalName = "Property", 112 description = "An attribute, characteristic, or quality of the containing object expressed as a namespace qualified name/value pair.", 113 useName = "prop", 114 maxOccurs = -1 115 ) 116 @GroupAs( 117 name = "props", 118 inJson = JsonGroupAsBehavior.LIST 119 ) 120 private List<Property> _props; 121 122 /** 123 * "Permits multiple paragraphs, lists, tables etc." 124 */ 125 @BoundField( 126 formalName = "Part Text", 127 description = "Permits multiple paragraphs, lists, tables etc.", 128 useName = "prose", 129 namespace = "##none", 130 inXmlWrapped = false 131 ) 132 @BoundFieldValue( 133 typeAdapter = MarkupMultilineAdapter.class 134 ) 135 private MarkupMultiline _prose; 136 137 @BoundAssembly( 138 formalName = "Part", 139 description = "An annotated, markup-based textual element of a control's or catalog group's definition, or a child of another part.", 140 useName = "part", 141 maxOccurs = -1 142 ) 143 @GroupAs( 144 name = "parts", 145 inJson = JsonGroupAsBehavior.LIST 146 ) 147 private List<ControlPart> _parts; 148 149 @BoundAssembly( 150 formalName = "Link", 151 description = "A reference to a local or remote resource, that has a specific relation to the containing object.", 152 useName = "link", 153 maxOccurs = -1 154 ) 155 @GroupAs( 156 name = "links", 157 inJson = JsonGroupAsBehavior.LIST 158 ) 159 private List<Link> _links; 160 161 public ControlPart() { 162 } 163 164 public String getId() { 165 return _id; 166 } 167 168 public void setId(String value) { 169 _id = value; 170 } 171 172 public String getName() { 173 return _name; 174 } 175 176 public void setName(String value) { 177 _name = value; 178 } 179 180 public URI getNs() { 181 return _ns; 182 } 183 184 public void setNs(URI value) { 185 _ns = value; 186 } 187 188 public String getClazz() { 189 return _clazz; 190 } 191 192 public void setClazz(String value) { 193 _clazz = value; 194 } 195 196 public MarkupLine getTitle() { 197 return _title; 198 } 199 200 public void setTitle(MarkupLine value) { 201 _title = value; 202 } 203 204 public List<Property> getProps() { 205 return _props; 206 } 207 208 public void setProps(List<Property> value) { 209 _props = value; 210 } 211 212 /** 213 * Add a new {@link Property} item to the underlying collection. 214 * @param item the item to add 215 * @return {@code true} 216 */ 217 public boolean addProp(Property item) { 218 Property value = ObjectUtils.requireNonNull(item,"item cannot be null"); 219 if (_props == null) { 220 _props = new LinkedList<>(); 221 } 222 return _props.add(value); 223 } 224 225 /** 226 * Remove the first matching {@link Property} item from the underlying collection. 227 * @param item the item to remove 228 * @return {@code true} if the item was removed or {@code false} otherwise 229 */ 230 public boolean removeProp(Property item) { 231 Property value = ObjectUtils.requireNonNull(item,"item cannot be null"); 232 return _props == null ? false : _props.remove(value); 233 } 234 235 public MarkupMultiline getProse() { 236 return _prose; 237 } 238 239 public void setProse(MarkupMultiline value) { 240 _prose = value; 241 } 242 243 public List<ControlPart> getParts() { 244 return _parts; 245 } 246 247 public void setParts(List<ControlPart> value) { 248 _parts = value; 249 } 250 251 /** 252 * Add a new {@link ControlPart} item to the underlying collection. 253 * @param item the item to add 254 * @return {@code true} 255 */ 256 public boolean addPart(ControlPart item) { 257 ControlPart value = ObjectUtils.requireNonNull(item,"item cannot be null"); 258 if (_parts == null) { 259 _parts = new LinkedList<>(); 260 } 261 return _parts.add(value); 262 } 263 264 /** 265 * Remove the first matching {@link ControlPart} item from the underlying collection. 266 * @param item the item to remove 267 * @return {@code true} if the item was removed or {@code false} otherwise 268 */ 269 public boolean removePart(ControlPart item) { 270 ControlPart value = ObjectUtils.requireNonNull(item,"item cannot be null"); 271 return _parts == null ? false : _parts.remove(value); 272 } 273 274 public List<Link> getLinks() { 275 return _links; 276 } 277 278 public void setLinks(List<Link> value) { 279 _links = value; 280 } 281 282 /** 283 * Add a new {@link Link} item to the underlying collection. 284 * @param item the item to add 285 * @return {@code true} 286 */ 287 public boolean addLink(Link item) { 288 Link value = ObjectUtils.requireNonNull(item,"item cannot be null"); 289 if (_links == null) { 290 _links = new LinkedList<>(); 291 } 292 return _links.add(value); 293 } 294 295 /** 296 * Remove the first matching {@link Link} item from the underlying collection. 297 * @param item the item to remove 298 * @return {@code true} if the item was removed or {@code false} otherwise 299 */ 300 public boolean removeLink(Link item) { 301 Link value = ObjectUtils.requireNonNull(item,"item cannot be null"); 302 return _links == null ? false : _links.remove(value); 303 } 304 305 @Override 306 public String toString() { 307 return new ReflectionToStringBuilder(this, MultilineRecursiveToStringStyle.MULTI_LINE_STYLE).toString(); 308 } 309}