001package gov.nist.secauto.oscal.lib.model;
002
003import gov.nist.secauto.metaschema.core.datatype.adapter.UuidAdapter;
004import gov.nist.secauto.metaschema.core.model.IBoundObject;
005import gov.nist.secauto.metaschema.core.model.IMetaschemaData;
006import gov.nist.secauto.metaschema.core.model.JsonGroupAsBehavior;
007import gov.nist.secauto.metaschema.core.model.constraint.IConstraint;
008import gov.nist.secauto.metaschema.core.util.ObjectUtils;
009import gov.nist.secauto.metaschema.databind.model.annotations.AssemblyConstraints;
010import gov.nist.secauto.metaschema.databind.model.annotations.BoundAssembly;
011import gov.nist.secauto.metaschema.databind.model.annotations.BoundField;
012import gov.nist.secauto.metaschema.databind.model.annotations.BoundFlag;
013import gov.nist.secauto.metaschema.databind.model.annotations.GroupAs;
014import gov.nist.secauto.metaschema.databind.model.annotations.Index;
015import gov.nist.secauto.metaschema.databind.model.annotations.IsUnique;
016import gov.nist.secauto.metaschema.databind.model.annotations.KeyField;
017import gov.nist.secauto.metaschema.databind.model.annotations.Let;
018import gov.nist.secauto.metaschema.databind.model.annotations.MetaschemaAssembly;
019import gov.nist.secauto.metaschema.databind.model.annotations.ValueConstraints;
020import java.lang.Override;
021import java.lang.String;
022import java.util.LinkedList;
023import java.util.List;
024import java.util.UUID;
025import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
026import org.apache.commons.lang3.builder.ToStringStyle;
027
028/**
029 * A plan of action and milestones which identifies initial and residual risks, deviations, and disposition, such as those required by FedRAMP.
030 */
031@MetaschemaAssembly(
032    formalName = "Plan of Action and Milestones (POA&M)",
033    description = "A plan of action and milestones which identifies initial and residual risks, deviations, and disposition, such as those required by FedRAMP.",
034    name = "plan-of-action-and-milestones",
035    moduleClass = OscalPoamModule.class,
036    rootName = "plan-of-action-and-milestones",
037    remarks = "Either an OSCAL-based SSP must be imported, or a unique system-id must be specified. Both may be present.",
038    valueConstraints = @ValueConstraints(lets = @Let(name = "all-imports", target = "recurse-depth('.[import-ap]/doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(import-ap/@href)))/assessment-plan|.[import-ssp]/doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(import-ssp/@href)))/system-security-plan|.[import-profile]/resolve-profile(doc(resolve-uri(Q{http://csrc.nist.gov/ns/oscal/1.0}resolve-reference(import-profile/@href))))/catalog')")),
039    modelConstraints = @AssemblyConstraints(index = {@Index(id = "oscal-poam-index-metadata-scoped-role-id", formalName = "In-Scope Role Identifiers", description = "An index of role identifiers that are in-scope for the plan-of-action-and-milestones model. Roles are collected from imported system-securtity-plans, which in turn includes referenced profiles and catalogs. For a given role @id, a locally declared role takes precedence over a role that is imported, the role that was last imported.", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/role ! map:entry(@id,.))?*", name = "index-imports-metadata-role-id", keyFields = @KeyField(target = "@id")), @Index(id = "oscal-poam-index-metadata-scoped-location-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/location ! map:entry(@uuid,.))?*", name = "index-imports-metadata-location-uuid", keyFields = @KeyField(target = "@uuid")), @Index(id = "oscal-poam-index-metadata-scoped-party-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/party ! map:entry(@uuid,.))?*", name = "index-imports-metadata-party-uuid", keyFields = @KeyField(target = "@uuid")), @Index(id = "oscal-poam-index-metadata-scoped-party-organization-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports/metadata/party[@type='organization'] ! map:entry(@uuid,.))?*", name = "index-imports-metadata-party-organization-uuid", keyFields = @KeyField(target = "@uuid")), @Index(id = "oscal-poam-index-metadata-scoped-property-uuid", level = IConstraint.Level.ERROR, target = "map:merge($all-imports//prop[@uuid] ! map:entry(@uuid,.))?*", name = "index-imports-metadata-property-uuid", keyFields = @KeyField(target = "@uuid"))}, unique = {@IsUnique(id = "oscal-unique-document-id", formalName = "Unique Document Identifier", description = "Ensure all document identifiers have a unique combination of @scheme and value.", level = IConstraint.Level.ERROR, target = "document-id", keyFields = {@KeyField(target = "@scheme"), @KeyField}), @IsUnique(id = "oscal-unique-property-in-context-location", formalName = "Unique Properties", description = "Ensure all properties are unique for a given location using a unique combination of @ns, @name, @class. @group. and @value.", level = IConstraint.Level.ERROR, target = ".//prop", keyFields = {@KeyField(target = "path(..)"), @KeyField(target = "@name"), @KeyField(target = "@ns"), @KeyField(target = "@class"), @KeyField(target = "@group"), @KeyField(target = "@value")}), @IsUnique(id = "oscal-unique-link-in-context-location", formalName = "Unique Links", description = "Ensure all links are unique for a given location using a unique combination of @href, @rel, and @media-type.", level = IConstraint.Level.ERROR, target = ".//link", keyFields = {@KeyField(target = "path(..)"), @KeyField(target = "@href"), @KeyField(target = "@rel"), @KeyField(target = "@media-type")}), @IsUnique(id = "oscal-unique-responsibility-in-context-location", formalName = "Unique Responsibilities", description = "Ensure all responsible-roles and responsible-parties are unique for a given location using a unique combination of @role-id and the combination of @party-uuid values.", level = IConstraint.Level.ERROR, target = ".//(responsible-party|responsible-role)", keyFields = {@KeyField(target = "path(..)"), @KeyField(target = "@role-id"), @KeyField(target = "@party-uuid")}, remarks = "Since `responsible-party` and `responsible-role` associate multiple `party-uuid` entries with a single `role-id`, each role-id must be referenced only once.")})
040)
041public class PlanOfActionAndMilestones extends AbstractOscalInstance implements IBoundObject {
042  private final IMetaschemaData __metaschemaData;
043
044  /**
045   * "A <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#machine-oriented\">machine-oriented</a>, <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#globally-unique\">globally unique</a> identifier with <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#instance\">instance</a>scope that can be used to reference this POA&amp;M instance in <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#poam-identifiers\">this OSCAL instance</a>. This UUID should be assigned <a href=\"https://pages.nist.gov/OSCAL/concepts/identifier-use/#consistency\">per-subject</a>, which means it should be consistently used to identify the same subject across revisions of the document."
046   */
047  @BoundFlag(
048      formalName = "POA&M Universally Unique Identifier",
049      description = "A [machine-oriented](https://pages.nist.gov/OSCAL/concepts/identifier-use/#machine-oriented), [globally unique](https://pages.nist.gov/OSCAL/concepts/identifier-use/#globally-unique) identifier with [instance](https://pages.nist.gov/OSCAL/concepts/identifier-use/#instance)scope that can be used to reference this POA\\&M instance in [this OSCAL instance](https://pages.nist.gov/OSCAL/concepts/identifier-use/#poam-identifiers). This UUID should be assigned [per-subject](https://pages.nist.gov/OSCAL/concepts/identifier-use/#consistency), which means it should be consistently used to identify the same subject across revisions of the document.",
050      name = "uuid",
051      required = true,
052      typeAdapter = UuidAdapter.class
053  )
054  private UUID _uuid;
055
056  @BoundAssembly(
057      formalName = "Document Metadata",
058      description = "Provides information about the containing document, and defines concepts that are shared across the document.",
059      useName = "metadata",
060      minOccurs = 1
061  )
062  private Metadata _metadata;
063
064  @BoundAssembly(
065      formalName = "Import System Security Plan",
066      description = "Used by the assessment plan and POA\\&M to import information about the system.",
067      useName = "import-ssp",
068      remarks = "Used by the POA\\&M to import information about the system."
069  )
070  private ImportSsp _importSsp;
071
072  @BoundField(
073      formalName = "System Identification",
074      description = "A [human-oriented](https://pages.nist.gov/OSCAL/concepts/identifier-use/#human-oriented), [globally unique](https://pages.nist.gov/OSCAL/concepts/identifier-use/#globally-unique) identifier with [cross-instance](https://pages.nist.gov/OSCAL/concepts/identifier-use/#cross-instance) scope that can be used to reference this system identification property elsewhere in [this or other OSCAL instances](https://pages.nist.gov/OSCAL/concepts/identifier-use/#scope). When referencing an externally defined `system identification`, the `system identification` must be used in the context of the external / imported OSCAL instance (e.g., uri-reference). This string should be assigned [per-subject](https://pages.nist.gov/OSCAL/concepts/identifier-use/#consistency), which means it should be consistently used to identify the same system across revisions of the document.",
075      useName = "system-id"
076  )
077  private SystemId _systemId;
078
079  @BoundAssembly(
080      formalName = "Local Definitions",
081      description = "Allows components, and inventory-items to be defined within the POA\\&M for circumstances where no OSCAL-based SSP exists, or is not delivered with the POA\\&M.",
082      useName = "local-definitions"
083  )
084  private LocalDefinitions _localDefinitions;
085
086  @BoundAssembly(
087      formalName = "Observation",
088      description = "Describes an individual observation.",
089      useName = "observation",
090      maxOccurs = -1,
091      groupAs = @GroupAs(name = "observations", inJson = JsonGroupAsBehavior.LIST)
092  )
093  private List<Observation> _observations;
094
095  @BoundAssembly(
096      formalName = "Identified Risk",
097      description = "An identified risk.",
098      useName = "risk",
099      maxOccurs = -1,
100      groupAs = @GroupAs(name = "risks", inJson = JsonGroupAsBehavior.LIST)
101  )
102  private List<Risk> _risks;
103
104  @BoundAssembly(
105      formalName = "Finding",
106      description = "Describes an individual finding.",
107      useName = "finding",
108      maxOccurs = -1,
109      groupAs = @GroupAs(name = "findings", inJson = JsonGroupAsBehavior.LIST)
110  )
111  private List<Finding> _findings;
112
113  @BoundAssembly(
114      formalName = "POA&M Item",
115      description = "Describes an individual POA\\&M item.",
116      useName = "poam-item",
117      minOccurs = 1,
118      maxOccurs = -1,
119      groupAs = @GroupAs(name = "poam-items", inJson = JsonGroupAsBehavior.LIST)
120  )
121  private List<PoamItem> _poamItems;
122
123  @BoundAssembly(
124      formalName = "Back matter",
125      description = "A collection of resources that may be referenced from within the OSCAL document instance.",
126      useName = "back-matter"
127  )
128  private BackMatter _backMatter;
129
130  public PlanOfActionAndMilestones() {
131    this(null);
132  }
133
134  public PlanOfActionAndMilestones(IMetaschemaData data) {
135    this.__metaschemaData = data;
136  }
137
138  @Override
139  public IMetaschemaData getMetaschemaData() {
140    return __metaschemaData;
141  }
142
143  public UUID getUuid() {
144    return _uuid;
145  }
146
147  public void setUuid(UUID value) {
148    _uuid = value;
149  }
150
151  public Metadata getMetadata() {
152    return _metadata;
153  }
154
155  public void setMetadata(Metadata value) {
156    _metadata = value;
157  }
158
159  public ImportSsp getImportSsp() {
160    return _importSsp;
161  }
162
163  public void setImportSsp(ImportSsp value) {
164    _importSsp = value;
165  }
166
167  public SystemId getSystemId() {
168    return _systemId;
169  }
170
171  public void setSystemId(SystemId value) {
172    _systemId = value;
173  }
174
175  public LocalDefinitions getLocalDefinitions() {
176    return _localDefinitions;
177  }
178
179  public void setLocalDefinitions(LocalDefinitions value) {
180    _localDefinitions = value;
181  }
182
183  public List<Observation> getObservations() {
184    return _observations;
185  }
186
187  public void setObservations(List<Observation> value) {
188    _observations = value;
189  }
190
191  /**
192   * Add a new {@link Observation} item to the underlying collection.
193   * @param item the item to add
194   * @return {@code true}
195   */
196  public boolean addObservation(Observation item) {
197    Observation value = ObjectUtils.requireNonNull(item,"item cannot be null");
198    if (_observations == null) {
199      _observations = new LinkedList<>();
200    }
201    return _observations.add(value);
202  }
203
204  /**
205   * Remove the first matching {@link Observation} item from the underlying collection.
206   * @param item the item to remove
207   * @return {@code true} if the item was removed or {@code false} otherwise
208   */
209  public boolean removeObservation(Observation item) {
210    Observation value = ObjectUtils.requireNonNull(item,"item cannot be null");
211    return _observations != null && _observations.remove(value);
212  }
213
214  public List<Risk> getRisks() {
215    return _risks;
216  }
217
218  public void setRisks(List<Risk> value) {
219    _risks = value;
220  }
221
222  /**
223   * Add a new {@link Risk} item to the underlying collection.
224   * @param item the item to add
225   * @return {@code true}
226   */
227  public boolean addRisk(Risk item) {
228    Risk value = ObjectUtils.requireNonNull(item,"item cannot be null");
229    if (_risks == null) {
230      _risks = new LinkedList<>();
231    }
232    return _risks.add(value);
233  }
234
235  /**
236   * Remove the first matching {@link Risk} item from the underlying collection.
237   * @param item the item to remove
238   * @return {@code true} if the item was removed or {@code false} otherwise
239   */
240  public boolean removeRisk(Risk item) {
241    Risk value = ObjectUtils.requireNonNull(item,"item cannot be null");
242    return _risks != null && _risks.remove(value);
243  }
244
245  public List<Finding> getFindings() {
246    return _findings;
247  }
248
249  public void setFindings(List<Finding> value) {
250    _findings = value;
251  }
252
253  /**
254   * Add a new {@link Finding} item to the underlying collection.
255   * @param item the item to add
256   * @return {@code true}
257   */
258  public boolean addFinding(Finding item) {
259    Finding value = ObjectUtils.requireNonNull(item,"item cannot be null");
260    if (_findings == null) {
261      _findings = new LinkedList<>();
262    }
263    return _findings.add(value);
264  }
265
266  /**
267   * Remove the first matching {@link Finding} item from the underlying collection.
268   * @param item the item to remove
269   * @return {@code true} if the item was removed or {@code false} otherwise
270   */
271  public boolean removeFinding(Finding item) {
272    Finding value = ObjectUtils.requireNonNull(item,"item cannot be null");
273    return _findings != null && _findings.remove(value);
274  }
275
276  public List<PoamItem> getPoamItems() {
277    return _poamItems;
278  }
279
280  public void setPoamItems(List<PoamItem> value) {
281    _poamItems = value;
282  }
283
284  /**
285   * Add a new {@link PoamItem} item to the underlying collection.
286   * @param item the item to add
287   * @return {@code true}
288   */
289  public boolean addPoamItem(PoamItem item) {
290    PoamItem value = ObjectUtils.requireNonNull(item,"item cannot be null");
291    if (_poamItems == null) {
292      _poamItems = new LinkedList<>();
293    }
294    return _poamItems.add(value);
295  }
296
297  /**
298   * Remove the first matching {@link PoamItem} item from the underlying collection.
299   * @param item the item to remove
300   * @return {@code true} if the item was removed or {@code false} otherwise
301   */
302  public boolean removePoamItem(PoamItem item) {
303    PoamItem value = ObjectUtils.requireNonNull(item,"item cannot be null");
304    return _poamItems != null && _poamItems.remove(value);
305  }
306
307  public BackMatter getBackMatter() {
308    return _backMatter;
309  }
310
311  public void setBackMatter(BackMatter value) {
312    _backMatter = value;
313  }
314
315  @Override
316  public String toString() {
317    return new ReflectionToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE).toString();
318  }
319}