diff --git a/docling/backend/html_backend.py b/docling/backend/html_backend.py index 7bae346..b802605 100644 --- a/docling/backend/html_backend.py +++ b/docling/backend/html_backend.py @@ -136,7 +136,6 @@ def analyse_element(self, element, idx, doc): def get_direct_text(self, item): """Get the direct text of the
  • element (ignoring nested lists).""" text = item.find(string=True, recursive=False) - if isinstance(text, str): return text.strip() @@ -149,21 +148,20 @@ def extract_text_recursively(self, item): if isinstance(item, str): return [item] - result.append(self.get_direct_text(item)) - - try: - # Iterate over the children (and their text and tails) - for child in item: - try: - # Recursively get the child's text content - result.extend(self.extract_text_recursively(child)) - except: - pass - except: - _log.warn("item has no children") - pass - - return " ".join(result) + if item.name not in ["ul", "ol"]: + try: + # Iterate over the children (and their text and tails) + for child in item: + try: + # Recursively get the child's text content + result.extend(self.extract_text_recursively(child)) + except: + pass + except: + _log.warn("item has no children") + pass + + return "".join(result) + " " def handle_header(self, element, idx, doc): """Handles header tags (h1, h2, etc.).""" @@ -255,7 +253,12 @@ def handle_listitem(self, element, idx, doc): if nested_lists: name = element.name - text = self.get_direct_text(element) + # Text in list item can be hidden within hierarchy, hence + # we need to extract it recursively + text = self.extract_text_recursively(element) + # Flatten text, remove break lines: + text = text.replace("\n", "").replace("\r", "") + text = " ".join(text.split()).strip() marker = "" enumerated = False @@ -263,14 +266,15 @@ def handle_listitem(self, element, idx, doc): marker = str(index_in_list) enumerated = True - # create a list-item - self.parents[self.level + 1] = doc.add_list_item( - text=text, - enumerated=enumerated, - marker=marker, - parent=self.parents[self.level], - ) - self.level += 1 + if len(text) > 0: + # create a list-item + self.parents[self.level + 1] = doc.add_list_item( + text=text, + enumerated=enumerated, + marker=marker, + parent=self.parents[self.level], + ) + self.level += 1 self.walk(element, doc) diff --git a/docling/backend/md_backend.py b/docling/backend/md_backend.py index e2d2675..900319c 100644 --- a/docling/backend/md_backend.py +++ b/docling/backend/md_backend.py @@ -135,11 +135,29 @@ def iterate_elements(self, element, depth=0, doc=None, parent_element=None): doc_label = DocItemLabel.TITLE else: doc_label = DocItemLabel.SECTION_HEADER - snippet_text = element.children[0].children.strip() - parent_element = doc.add_text( - label=doc_label, parent=parent_element, text=snippet_text - ) + # Header could have arbitrary inclusion of bold, italic or emphasis, + # hence we need to traverse the tree to get full text of a header + strings = [] + + # Define a recursive function to traverse the tree + def traverse(node): + # Check if the node has a "children" attribute + if hasattr(node, "children"): + # If "children" is a list, continue traversal + if isinstance(node.children, list): + for child in node.children: + traverse(child) + # If "children" is text, add it to header text + elif isinstance(node.children, str): + strings.append(node.children) + + traverse(element) + snippet_text = "".join(strings) + if len(snippet_text) > 0: + parent_element = doc.add_text( + label=doc_label, parent=parent_element, text=snippet_text + ) elif isinstance(element, marko.block.List): self.close_table(doc) @@ -286,6 +304,7 @@ def convert(self) -> DoclingDocument: parsed_ast = marko_parser.parse(self.markdown) # Start iterating from the root of the AST self.iterate_elements(parsed_ast, 0, doc, None) + self.process_inline_text(None, doc) # handle last hanging inline text else: raise RuntimeError( f"Cannot convert md with {self.document_hash} because the backend failed to init." diff --git a/docs/examples/batch_convert.py b/docs/examples/batch_convert.py index aa06303..2c61336 100644 --- a/docs/examples/batch_convert.py +++ b/docs/examples/batch_convert.py @@ -14,7 +14,7 @@ _log = logging.getLogger(__name__) USE_V2 = True -USE_LEGACY = False +USE_LEGACY = True def export_documents( @@ -78,7 +78,7 @@ def export_documents( with (output_dir / f"{doc_filename}.legacy.doctags.txt").open( "w", encoding="utf-8" ) as fp: - fp.write(conv_res.legacy_document.export_to_doctags()) + fp.write(conv_res.legacy_document.export_to_document_tokens()) elif conv_res.status == ConversionStatus.PARTIAL_SUCCESS: _log.info( diff --git a/poetry.lock b/poetry.lock index 1665aca..b832ed3 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.4 and should not be changed by hand. [[package]] name = "aiohappyeyeballs" @@ -196,8 +196,8 @@ files = [ lazy-object-proxy = ">=1.4.0" typing-extensions = {version = ">=4.0.0", markers = "python_version < \"3.11\""} wrapt = [ - {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, {version = ">=1.11,<2", markers = "python_version < \"3.11\""}, + {version = ">=1.14,<2", markers = "python_version >= \"3.11\""}, ] [[package]] @@ -840,8 +840,8 @@ files = [ docling-core = ">=2.0,<3.0" docutils = "!=0.21" numpy = [ - {version = ">=2.0.2,<3.0.0", markers = "python_version >= \"3.13\""}, {version = ">=1.26.4,<2.0.0", markers = "python_version >= \"3.9\" and python_version < \"3.13\""}, + {version = ">=2.0.2,<3.0.0", markers = "python_version >= \"3.13\""}, ] pandas = {version = ">=2.1.4,<3.0.0", markers = "python_version >= \"3.9\""} python-dotenv = ">=1.0.0,<2.0.0" @@ -894,13 +894,13 @@ files = [ [[package]] name = "docling-core" -version = "2.1.0" +version = "2.2.1" description = "A python library to define and validate data types in Docling." optional = false python-versions = "<4.0,>=3.9" files = [ - {file = "docling_core-2.1.0-py3-none-any.whl", hash = "sha256:4ccf9c44f8d7cf663657283baea4c4a36e1c4d1fba7df6b70ebc2c16b58f11a4"}, - {file = "docling_core-2.1.0.tar.gz", hash = "sha256:76ba3cb0a912db712aa89618746d279f1276b943c259dcf9d0b335a30cf7c99e"}, + {file = "docling_core-2.2.1-py3-none-any.whl", hash = "sha256:65ed05331f387410950e10d7d2347eae770ab7dc4b5a632715aaa7c66c158cb5"}, + {file = "docling_core-2.2.1.tar.gz", hash = "sha256:4893369fe2aac9dff26c85a4ff87990f2e1645d9e16473ac7309e3459a3c4219"}, ] [package.dependencies] @@ -928,8 +928,8 @@ jsonlines = ">=3.1.0,<4.0.0" lxml = ">=4.9.1,<5.0.0" mean_average_precision = ">=2021.4.26.0,<2022.0.0.0" numpy = [ - {version = ">=2.1.0,<3.0.0", markers = "python_version >= \"3.13\""}, {version = ">=1.24.4,<2.0.0", markers = "python_version < \"3.13\""}, + {version = ">=2.1.0,<3.0.0", markers = "python_version >= \"3.13\""}, ] opencv-python-headless = ">=4.6.0.66,<5.0.0.0" Pillow = ">=10.0.0,<11.0.0" @@ -2074,8 +2074,8 @@ jsonpatch = ">=1.33,<2.0" langsmith = ">=0.1.112,<0.2.0" packaging = ">=23.2,<25" pydantic = [ - {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, + {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, ] PyYAML = ">=5.3" tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0" @@ -2143,8 +2143,8 @@ files = [ httpx = ">=0.23.0,<1" orjson = ">=3.9.14,<4.0.0" pydantic = [ - {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, + {version = ">=2.7.4,<3.0.0", markers = "python_full_version >= \"3.12.4\""}, ] requests = ">=2,<3" requests-toolbelt = ">=1.0.0,<2.0.0" @@ -3514,10 +3514,10 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, - {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, {version = ">=1.21.4", markers = "python_version >= \"3.10\" and platform_system == \"Darwin\" and python_version < \"3.11\""}, {version = ">=1.21.2", markers = "platform_system != \"Darwin\" and python_version >= \"3.10\" and python_version < \"3.11\""}, + {version = ">=1.23.5", markers = "python_version >= \"3.11\" and python_version < \"3.12\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] [[package]] @@ -3666,9 +3666,9 @@ files = [ [package.dependencies] numpy = [ - {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, - {version = ">=1.23.2", markers = "python_version == \"3.11\""}, {version = ">=1.22.4", markers = "python_version < \"3.11\""}, + {version = ">=1.23.2", markers = "python_version == \"3.11\""}, + {version = ">=1.26.0", markers = "python_version >= \"3.12\""}, ] python-dateutil = ">=2.8.2" pytz = ">=2020.1" @@ -4265,8 +4265,8 @@ files = [ annotated-types = ">=0.6.0" pydantic-core = "2.23.4" typing-extensions = [ - {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, {version = ">=4.6.1", markers = "python_version < \"3.13\""}, + {version = ">=4.12.2", markers = "python_version >= \"3.13\""}, ] [package.extras] @@ -4434,8 +4434,8 @@ files = [ astroid = ">=2.15.8,<=2.17.0-dev0" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} dill = [ - {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, {version = ">=0.2", markers = "python_version < \"3.11\""}, + {version = ">=0.3.6", markers = "python_version >= \"3.11\""}, ] isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.8" @@ -5596,6 +5596,11 @@ files = [ {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd"}, {file = "scikit_learn-1.5.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6"}, {file = "scikit_learn-1.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1"}, + {file = "scikit_learn-1.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e9a702e2de732bbb20d3bad29ebd77fc05a6b427dc49964300340e4c9328b3f5"}, + {file = "scikit_learn-1.5.2-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:b0768ad641981f5d3a198430a1d31c3e044ed2e8a6f22166b4d546a5116d7908"}, + {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:178ddd0a5cb0044464fc1bfc4cca5b1833bfc7bb022d70b05db8530da4bb3dd3"}, + {file = "scikit_learn-1.5.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f7284ade780084d94505632241bf78c44ab3b6f1e8ccab3d2af58e0e950f9c12"}, + {file = "scikit_learn-1.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:b7b0f9a0b1040830d38c39b91b3a44e1b643f4b36e36567b80b7c6bd2202a27f"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9"}, {file = "scikit_learn-1.5.2-cp39-cp39-macosx_12_0_arm64.whl", hash = "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1"}, {file = "scikit_learn-1.5.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8"}, @@ -7175,4 +7180,4 @@ tesserocr = ["tesserocr"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "ae89a74730fdc66a8af64cca75e8713a63592c30894d5cd5ea950f24839c10d9" +content-hash = "48127a4b7e05f31a1c9f2c6f9b0ac8da61ac67309a8dd020b41e7ec82ecff38e" diff --git a/pyproject.toml b/pyproject.toml index 7beb575..b4ad337 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,7 +37,7 @@ torchvision = [ ###################### python = "^3.10" pydantic = "^2.0.0" -docling-core = "^2.1.0" +docling-core = "^2.2.1" docling-ibm-models = "^2.0.1" deepsearch-glm = "^0.26.1" filetype = "^1.2.0" diff --git a/tests/data/groundtruth/docling_v2/2203.01017v2.md b/tests/data/groundtruth/docling_v2/2203.01017v2.md index dd9eebc..5edc916 100644 --- a/tests/data/groundtruth/docling_v2/2203.01017v2.md +++ b/tests/data/groundtruth/docling_v2/2203.01017v2.md @@ -139,13 +139,13 @@ tention encoding is then multiplied to the encoded image to produce a feature fo The output features for each table cell are then fed into the feed-forward network (FFN). The FFN consists of a Multi-Layer Perceptron (3 layers with ReLU activation function) that predicts the normalized coordinates for the bounding box of each table cell. Finally, the predicted bounding boxes are classified based on whether they are empty or not using a linear layer. -Loss Functions. We formulate a multi-task loss Eq. 2 to train our network. The Cross-Entropy loss (denoted as l$_{s}$ ) is used to train the Structure Decoder which predicts the structure tokens. As for the Cell BBox Decoder it is trained with a combination of losses denoted as l$_{box}$ . l$_{box}$ consists of the generally used l$_{1}$ loss for object detection and the IoU loss ( l$_{iou}$ ) to be scale invariant as explained in [25]. In comparison to DETR, we do not use the Hungarian algorithm [15] to match the predicted bounding boxes with the ground-truth boxes, as we have already achieved a one-toone match through two steps: 1) Our token input sequence is naturally ordered, therefore the hidden states of the table data cells are also in order when they are provided as input to the Cell BBox Decoder , and 2) Our bounding boxes generation mechanism (see Sec. 3) ensures a one-to-one mapping between the cell content and its bounding box for all post-processed datasets. +Loss Functions. We formulate a multi-task loss Eq. 2 to train our network. The Cross-Entropy loss (denoted as l$\_{s}$ ) is used to train the Structure Decoder which predicts the structure tokens. As for the Cell BBox Decoder it is trained with a combination of losses denoted as l$\_{box}$ . l$\_{box}$ consists of the generally used l$\_{1}$ loss for object detection and the IoU loss ( l$\_{iou}$ ) to be scale invariant as explained in [25]. In comparison to DETR, we do not use the Hungarian algorithm [15] to match the predicted bounding boxes with the ground-truth boxes, as we have already achieved a one-toone match through two steps: 1) Our token input sequence is naturally ordered, therefore the hidden states of the table data cells are also in order when they are provided as input to the Cell BBox Decoder , and 2) Our bounding boxes generation mechanism (see Sec. 3) ensures a one-to-one mapping between the cell content and its bounding box for all post-processed datasets. The loss used to train the TableFormer can be defined as following: -l$_{box}$ = λ$_{iou}$l$_{iou}$ + λ$_{l}$$_{1}$ l = λl$_{s}$ + (1 - λ ) l$_{box}$ (1) +l$\_{box}$ = λ$\_{iou}$l$\_{iou}$ + λ$\_{l}$$\_{1}$ l = λl$\_{s}$ + (1 - λ ) l$\_{box}$ (1) -where λ ∈ [0, 1], and λ$_{iou}$, λ$_{l}$$_{1}$ ∈$_{R}$ are hyper-parameters. +where λ ∈ [0, 1], and λ$\_{iou}$, λ$\_{l}$$\_{1}$ ∈$\_{R}$ are hyper-parameters. ## 5. Experimental Results @@ -175,9 +175,9 @@ We also share our baseline results on the challenging SynthTabNet dataset. Throu The Tree-Edit-Distance-Based Similarity (TEDS) metric was introduced in [37]. It represents the prediction, and ground-truth as a tree structure of HTML tags. This similarity is calculated as: -TEDS ( T$_{a}$, T$_{b}$ ) = 1 - EditDist ( T$_{a}$, T$_{b}$ ) max ( | T$_{a}$ | , | T$_{b}$ | ) (3) +TEDS ( T$\_{a}$, T$\_{b}$ ) = 1 - EditDist ( T$\_{a}$, T$\_{b}$ ) max ( | T$\_{a}$ | , | T$\_{b}$ | ) (3) -where T$_{a}$ and T$_{b}$ represent tables in tree structure HTML format. EditDist denotes the tree-edit distance, and | T | represents the number of nodes in T . +where T$\_{a}$ and T$\_{b}$ represent tables in tree structure HTML format. EditDist denotes the tree-edit distance, and | T | represents the number of nodes in T . ## 5.4. Quantitative Analysis @@ -376,9 +376,9 @@ Here is a step-by-step description of the prediction postprocessing: - 3.a. If all IOU scores in a column are below the threshold, discard all predictions (structure and bounding boxes) for that column. - 4. Find the best-fitting content alignment for the predicted cells with good IOU per each column. The alignment of the column can be identified by the following formula: -alignment = arg min c { D$_{c}$ } D$_{c}$ = max { x$_{c}$ } - min { x$_{c}$ } (4) +alignment = arg min c { D$\_{c}$ } D$\_{c}$ = max { x$\_{c}$ } - min { x$\_{c}$ } (4) -where c is one of { left, centroid, right } and x$_{c}$ is the xcoordinate for the corresponding point. +where c is one of { left, centroid, right } and x$\_{c}$ is the xcoordinate for the corresponding point. - 5. Use the alignment computed in step 4, to compute the median x -coordinate for all table columns and the me- diff --git a/tests/data/groundtruth/docling_v2/2305.03393v1.md b/tests/data/groundtruth/docling_v2/2305.03393v1.md index eda24e8..08ec86c 100644 --- a/tests/data/groundtruth/docling_v2/2305.03393v1.md +++ b/tests/data/groundtruth/docling_v2/2305.03393v1.md @@ -153,7 +153,7 @@ Table 2. TSR and cell detection results compared between OTSL and HTML on the Pu To illustrate the qualitative differences between OTSL and HTML, Figure 5 demonstrates less overlap and more accurate bounding boxes with OTSL. In Figure 6, OTSL proves to be more effective in handling tables with longer token sequences, resulting in even more precise structure prediction and bounding boxes. -Fig. 5. The OTSL model produces more accurate bounding boxes with less overlap (E) than the HTML model (D), when predicting the structure of a sparse table (A), at twice the inference speed because of shorter sequence length (B),(C). "PMC2807444_006_00.png" PubTabNet. μ +Fig. 5. The OTSL model produces more accurate bounding boxes with less overlap (E) than the HTML model (D), when predicting the structure of a sparse table (A), at twice the inference speed because of shorter sequence length (B),(C). "PMC2807444\_006\_00.png" PubTabNet. μ @@ -161,7 +161,7 @@ Fig. 5. The OTSL model produces more accurate bounding boxes with less overlap ( ≥ -Fig. 6. Visualization of predicted structure and detected bounding boxes on a complex table with many rows. The OTSL model (B) captured repeating pattern of horizontally merged cells from the GT (A), unlike the HTML model (C). The HTML model also didn't complete the HTML sequence correctly and displayed a lot more of drift and overlap of bounding boxes. "PMC5406406_003_01.png" PubTabNet. +Fig. 6. Visualization of predicted structure and detected bounding boxes on a complex table with many rows. The OTSL model (B) captured repeating pattern of horizontally merged cells from the GT (A), unlike the HTML model (C). The HTML model also didn't complete the HTML sequence correctly and displayed a lot more of drift and overlap of bounding boxes. "PMC5406406\_003\_01.png" PubTabNet. diff --git a/tests/data/groundtruth/docling_v2/redp5110.md b/tests/data/groundtruth/docling_v2/redp5110.md index 9095fb9..961f6af 100644 --- a/tests/data/groundtruth/docling_v2/redp5110.md +++ b/tests/data/groundtruth/docling_v2/redp5110.md @@ -49,13 +49,13 @@ Note to U.S. Government Users Restricted Rights -- Use, duplication or disclosur | 1.3.2 New controls: Row and Column Access Control. . . . . . . . . . . . . . . . . . . . . . . . . . . | 5 | | Chapter 2. Roles and separation of duties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 7 | | 2.1 Roles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 8 | -| 2.1.1 DDM and DRDA application server access: QIBM_DB_DDMDRDA . . . . . . . . . . . | 8 | -| 2.1.2 Toolbox application server access: QIBM_DB_ZDA. . . . . . . . . . . . . . . . . . . . . . . . | 8 | -| 2.1.3 Database Administrator function: QIBM_DB_SQLADM . . . . . . . . . . . . . . . . . . . . . | 9 | -| 2.1.4 Database Information function: QIBM_DB_SYSMON | . . . . . . . . . . . . . . . . . . . . . . 9 | -| 2.1.5 Security Administrator function: QIBM_DB_SECADM . . . . . . . . . . . . . . . . . . . . . . | 9 | +| 2.1.1 DDM and DRDA application server access: QIBM\_DB\_DDMDRDA . . . . . . . . . . . | 8 | +| 2.1.2 Toolbox application server access: QIBM\_DB\_ZDA. . . . . . . . . . . . . . . . . . . . . . . . | 8 | +| 2.1.3 Database Administrator function: QIBM\_DB\_SQLADM . . . . . . . . . . . . . . . . . . . . . | 9 | +| 2.1.4 Database Information function: QIBM\_DB\_SYSMON | . . . . . . . . . . . . . . . . . . . . . . 9 | +| 2.1.5 Security Administrator function: QIBM\_DB\_SECADM . . . . . . . . . . . . . . . . . . . . . . | 9 | | 2.1.6 Change Function Usage CL command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 10 | -| 2.1.7 Verifying function usage IDs for RCAC with the FUNCTION_USAGE view . . . . . | 10 | +| 2.1.7 Verifying function usage IDs for RCAC with the FUNCTION\_USAGE view . . . . . | 10 | | 2.2 Separation of duties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 | | | Chapter 3. Row and Column Access Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 13 | | 3.1 Explanation of RCAC and the concept of access control . . . . . . . . . . . . . . . . . . . . . . . | 14 | @@ -64,11 +64,11 @@ Note to U.S. Government Users Restricted Rights -- Use, duplication or disclosur | 3.2 Special registers and built-in global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 18 | | 3.2.1 Special registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 18 | | 3.2.2 Built-in global variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 19 | -| 3.3 VERIFY_GROUP_FOR_USER function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 20 | +| 3.3 VERIFY\_GROUP\_FOR\_USER function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 20 | | 3.4 Establishing and controlling accessibility by using the RCAC rule text . . . . . . . . . . . . . | 21 | | | . . . . . . . . . . . . . . . . . . . . . . . . 22 | | 3.5 SELECT, INSERT, and UPDATE behavior with RCAC | | -| 3.6.1 Assigning the QIBM_DB_SECADM function ID to the consultants. . . . . . . . . . . . | 23 | +| 3.6.1 Assigning the QIBM\_DB\_SECADM function ID to the consultants. . . . . . . . . . . . | 23 | | 3.6.2 Creating group profiles for the users and their roles . . . . . . . . . . . . . . . . . . . . . . . | 23 | | 3.6.3 Demonstrating data access without RCAC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 24 | | 3.6.4 Defining and creating row permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 25 | @@ -83,9 +83,9 @@ Note to U.S. Government Users Restricted Rights -- Use, duplication or disclosur | 4.2 Description of the users roles and responsibilities | . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 | | 4.3 Implementation of RCAC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 42 | | 4.3.1 Reviewing the tables that are used in this example | . . . . . . . . . . . . . . . . . . . . . . . 42 | -| 4.3.2 Assigning function ID QIBM_DB_SECADM to the Database Engineers group | . . 47 | +| 4.3.2 Assigning function ID QIBM\_DB\_SECADM to the Database Engineers group | . . 47 | | 4.3.3 Creating group profiles for the users and their roles . . . . . . . . . . . . . . . . . . . . . . . | 50 | -| 4.3.4 Creating the CUSTOMER_LOGIN_ID global variable | . . . . . . . . . . . . . . . . . . . . . 52 | +| 4.3.4 Creating the CUSTOMER\_LOGIN\_ID global variable | . . . . . . . . . . . . . . . . . . . . . 52 | | 4.3.5 Defining and creating row permissions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . | 54 | | 4.3.6 Defining and creating column masks | . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 | | 4.3.7 Restricting the inserting and updating of masked data . . . . . . . . . . . . . . . . . . . . . | 60 | @@ -489,57 +489,57 @@ To assist with defining roles and the separation of duties with appropriate auth Roles are divided among the following DB2 functions and their corresponding function usage IDs: -- GLYPH DDM and IBM DRDAfi application server access: QIBM_DB_DDMDRDA -- GLYPH Toolbox application server access: QIBM_DB_ZDA -- GLYPH Database Administrator function: QIBM_DB_SQLADM -- GLYPH Database Information function: QIBM_DB_SYSMON -- GLYPH Security Administrator function: QIBM_DB_SECADM +- GLYPH DDM and IBM DRDAfi application server access: QIBM\_DB\_DDMDRDA +- GLYPH Toolbox application server access: QIBM\_DB\_ZDA +- GLYPH Database Administrator function: QIBM\_DB\_SQLADM +- GLYPH Database Information function: QIBM\_DB\_SYSMON +- GLYPH Security Administrator function: QIBM\_DB\_SECADM -## 2.1.1 DDM and DRDA application server access: QIBM_DB_DDMDRDA +## 2.1.1 DDM and DRDA application server access: QIBM\_DB\_DDMDRDA -The QIBM_DB_DDMDRDA function usage ID restricts access to the DDM and DRDA application server (QRWTSRVR). This function usage ID provides an easy alternative (rather than writing an exit program) to control access to DDM and DRDA from the server side. The function usage IDs ship with the default authority of *ALLOWED. The security officer can easily deny access to specific users or groups. +The QIBM\_DB\_DDMDRDA function usage ID restricts access to the DDM and DRDA application server (QRWTSRVR). This function usage ID provides an easy alternative (rather than writing an exit program) to control access to DDM and DRDA from the server side. The function usage IDs ship with the default authority of *ALLOWED. The security officer can easily deny access to specific users or groups. This is an alternative to a User Exit Program approach. No coding is required, it is easy to change, and it is auditable. -## 2.1.2 Toolbox application server access: QIBM_DB_ZDA +## 2.1.2 Toolbox application server access: QIBM\_DB\_ZDA -The QIBM_DB_ZDA function usage ID restricts access to the optimized server that handles DB2 requests from clients (QZDASOINIT and QZDASSINIT). Server access is used by the ODBC, OLE DB, and .NET providers that ship with IBM i Access for Windows and JDBC Toolbox, Run SQL scripts, and other parts of System i Navigator and Navigator for i Web console. +The QIBM\_DB\_ZDA function usage ID restricts access to the optimized server that handles DB2 requests from clients (QZDASOINIT and QZDASSINIT). Server access is used by the ODBC, OLE DB, and .NET providers that ship with IBM i Access for Windows and JDBC Toolbox, Run SQL scripts, and other parts of System i Navigator and Navigator for i Web console. This function usage ID provides an easy alternative (rather than writing an exit program) to control access to these functions from the server side. The function usage IDs ship with the default authority of *ALLOWED. The security officer can easily deny access to specific users or groups. This is an alternative to a User Exit Program approach. No coding is required, it is easy to change, and it is auditable. -## 2.1.3 Database Administrator function: QIBM_DB_SQLADM +## 2.1.3 Database Administrator function: QIBM\_DB\_SQLADM -The Database Administrator function (QIBM_DB_SQLADM) is needed whenever a user is analyzing and viewing SQL performance data. Some of the more common database administrator functions include displaying statements from the SQL Plan Cache, analyzing SQL Performance Monitors and SQL Plan Cache Snapshots, and displaying the SQL details of a job other than your own. +The Database Administrator function (QIBM\_DB\_SQLADM) is needed whenever a user is analyzing and viewing SQL performance data. Some of the more common database administrator functions include displaying statements from the SQL Plan Cache, analyzing SQL Performance Monitors and SQL Plan Cache Snapshots, and displaying the SQL details of a job other than your own. The Database Administrator function provides an alternative to granting *JOBCTL, but simply having the Database Administrator authorization does not carry with it all the needed object authorities for every administration task. The default behavior is to deny authorization. -To perform database administrator tasks that are not related to performance analysis, you must refer to the details of the task to determine its specific authorization requirements. For example, to allow a database administrator to reorganize a table, the DBA must have additional object authorities to the table that are not covered by QIBM_DB_SQLADM. +To perform database administrator tasks that are not related to performance analysis, you must refer to the details of the task to determine its specific authorization requirements. For example, to allow a database administrator to reorganize a table, the DBA must have additional object authorities to the table that are not covered by QIBM\_DB\_SQLADM. -## Granting QIBM_DB_SQLADM function usage +## Granting QIBM\_DB\_SQLADM function usage Only the security administrator (*SECADM) is allowed to change the list of users that can perform Database Administration functions. -## 2.1.4 Database Information function: QIBM_DB_SYSMON +## 2.1.4 Database Information function: QIBM\_DB\_SYSMON -The Database Information function (QIBM_DB_SYSMON) provides much less authority than Database Administrator function. Its primary use allows a user to examine high-level database properties. +The Database Information function (QIBM\_DB\_SYSMON) provides much less authority than Database Administrator function. Its primary use allows a user to examine high-level database properties. -For example, a user that does not have *JOBCTL or QIBM_DB_SQLADM can still view the SQL Plan Cache properties if granted authority to QIBM_DB_SYSMON. Without granting this authority, the default behavior is to deny authorization. +For example, a user that does not have *JOBCTL or QIBM\_DB\_SQLADM can still view the SQL Plan Cache properties if granted authority to QIBM\_DB\_SYSMON. Without granting this authority, the default behavior is to deny authorization. -## Granting QIBM_DB_SYSMON function usage +## Granting QIBM\_DB\_SYSMON function usage Only the security administrator (*SECADM) is allowed to change the list of users that can perform Database Information functions. -## 2.1.5 Security Administrator function: QIBM_DB_SECADM +## 2.1.5 Security Administrator function: QIBM\_DB\_SECADM -The Security Administrator function (QIBM_DB_SECADM) grants authorities, revokes authorities, changes ownership, or changes the primary group without giving access to the object or, in the case of a database table, to the data that is in the table or allowing other operations on the table. +The Security Administrator function (QIBM\_DB\_SECADM) grants authorities, revokes authorities, changes ownership, or changes the primary group without giving access to the object or, in the case of a database table, to the data that is in the table or allowing other operations on the table. -Only those users with the QIBM_DB_SECADM function can administer and manage RCAC rules. RCAC can be used to prevent even users with *ALLOBJ authority from freely accessing all the data in a protected database. These users are excluded from data access unless they are specifically authorized by RCAC. Without granting this authority, the default behavior is to deny authorization. +Only those users with the QIBM\_DB\_SECADM function can administer and manage RCAC rules. RCAC can be used to prevent even users with *ALLOBJ authority from freely accessing all the data in a protected database. These users are excluded from data access unless they are specifically authorized by RCAC. Without granting this authority, the default behavior is to deny authorization. -## Granting QIBM_DB_SECADM function usage +## Granting QIBM\_DB\_SECADM function usage -Only QSECOFR or a user with *SECADM special authority can grant the QIBM_DB_SECADM function usage to a user or group. +Only QSECOFR or a user with *SECADM special authority can grant the QIBM\_DB\_SECADM function usage to a user or group. ## 2.1.6 Change Function Usage CL command @@ -551,26 +551,26 @@ The following CL commands can be used to work with, display, or change function For example, the following CHGFCNUSG command shows granting authorization to user HBEDOYA to administer and manage RCAC rules: -CHGFCNUSG FCNID(QIBM_DB_SECADM) USER(HBEDOYA) USAGE(*ALLOWED) +CHGFCNUSG FCNID(QIBM\_DB\_SECADM) USER(HBEDOYA) USAGE(*ALLOWED) -## 2.1.7 Verifying function usage IDs for RCAC with the FUNCTION_USAGE view +## 2.1.7 Verifying function usage IDs for RCAC with the FUNCTION\_USAGE view -The FUNCTION_USAGE view contains function usage configuration details. Table 2-1 describes the columns in the FUNCTION_USAGE view. +The FUNCTION\_USAGE view contains function usage configuration details. Table 2-1 describes the columns in the FUNCTION\_USAGE view. -Table 2-1 FUNCTION_USAGE view +Table 2-1 FUNCTION\_USAGE view | Column name | Data type | Description | |---------------|-------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| FUNCTION_ID | VARCHAR(30) | ID of the function. | -| USER_NAME | VARCHAR(10) | Name of the user profile that has a usage setting for this function. | +| FUNCTION\_ID | VARCHAR(30) | ID of the function. | +| USER\_NAME | VARCHAR(10) | Name of the user profile that has a usage setting for this function. | | USAGE | VARCHAR(7) | Usage setting: GLYPH ALLOWED: The user profile is allowed to use the function. GLYPH DENIED: The user profile is not allowed to use the function. | -| USER_TYPE | VARCHAR(5) | Type of user profile: GLYPH USER: The user profile is a user. GLYPH GROUP: The user profile is a group. | +| USER\_TYPE | VARCHAR(5) | Type of user profile: GLYPH USER: The user profile is a user. GLYPH GROUP: The user profile is a group. | To discover who has authorization to define and manage RCAC, you can use the query that is shown in Example 2-1. Example 2-1 Query to determine who has authority to define and manage RCAC -SELECT function_id, user_name, usage, user_type FROM function_usage WHERE function_id='QIBM_DB_SECADM' ORDER BY user_name; +SELECT function\_id, user\_name, usage, user\_type FROM function\_usage WHERE function\_id='QIBM\_DB\_SECADM' ORDER BY user\_name; ## 2.2 Separation of duties @@ -578,19 +578,19 @@ Separation of duties helps businesses comply with industry regulations or organi For example, assume that a business has assigned the duty to manage security on IBM i to Theresa. Before release IBM i 7.2, to grant privileges, Theresa had to have the same privileges Theresa was granting to others. Therefore, to grant *USE privileges to the PAYROLL table, Theresa had to have *OBJMGT and *USE authority (or a higher level of authority, such as *ALLOBJ). This requirement allowed Theresa to access the data in the PAYROLL table even though Theresa's job description was only to manage its security. -In IBM i 7.2, the QIBM_DB_SECADM function usage grants authorities, revokes authorities, changes ownership, or changes the primary group without giving access to the object or, in the case of a database table, to the data that is in the table or allowing other operations on the table. +In IBM i 7.2, the QIBM\_DB\_SECADM function usage grants authorities, revokes authorities, changes ownership, or changes the primary group without giving access to the object or, in the case of a database table, to the data that is in the table or allowing other operations on the table. -QIBM_DB_SECADM function usage can be granted only by a user with *SECADM special authority and can be given to a user or a group. +QIBM\_DB\_SECADM function usage can be granted only by a user with *SECADM special authority and can be given to a user or a group. -QIBM_DB_SECADM also is responsible for administering RCAC, which restricts which rows a user is allowed to access in a table and whether a user is allowed to see information in certain columns of a table. +QIBM\_DB\_SECADM also is responsible for administering RCAC, which restricts which rows a user is allowed to access in a table and whether a user is allowed to see information in certain columns of a table. -A preferred practice is that the RCAC administrator has the QIBM_DB_SECADM function usage ID, but absolutely no other data privileges. The result is that the RCAC administrator can deploy and maintain the RCAC constructs, but cannot grant themselves unauthorized access to data itself. +A preferred practice is that the RCAC administrator has the QIBM\_DB\_SECADM function usage ID, but absolutely no other data privileges. The result is that the RCAC administrator can deploy and maintain the RCAC constructs, but cannot grant themselves unauthorized access to data itself. Table 2-2 shows a comparison of the different function usage IDs and *JOBCTL authority to the different CL commands and DB2 for i tools. Table 2-2 Comparison of the different function usage IDs and *JOBCTL authority -| User action | *JOBCTL | QIBM_DB_SECADM | QIBM_DB_SQLADM | QIBM_DB_SYSMON | No Authority | +| User action | *JOBCTL | QIBM\_DB\_SECADM | QIBM\_DB\_SQLADM | QIBM\_DB\_SYSMON | No Authority | |--------------------------------------------------------------------------------|-----------|------------------|------------------|------------------|----------------| | SET CURRENT DEGREE (SQL statement) | X | | X | | | | CHGQRYA command targeting a different user's job | X | | X | | | @@ -605,7 +605,7 @@ Table 2-2 Comparison of the different function usage IDs and *JOBCTL authority | MODIFY PLAN CACHE PROPERTIES procedure (currently does not check authority) | X | | X | | | | CHANGE PLAN CACHE SIZE procedure (currently does not check authority) | X | | X | | | -| User action | *JOBCTL | QIBM_DB_SECADM | QIBM_DB_SQLADM | QIBM_DB_SYSMON | No Authority | +| User action | *JOBCTL | QIBM\_DB\_SECADM | QIBM\_DB\_SQLADM | QIBM\_DB\_SYSMON | No Authority | |--------------------------------------------------------------|-----------|------------------|------------------|------------------|----------------| | START PLAN CACHE EVENT MONITOR procedure | X | | X | | | | END PLAN CACHE EVENT MONITOR procedure | X | | X | | | @@ -643,7 +643,7 @@ The following topics are covered in this chapter: - GLYPH Explanation of RCAC and the concept of access control - GLYPH Special registers and built-in global variables -- GLYPH VERIFY_GROUP_FOR_USER function +- GLYPH VERIFY\_GROUP\_FOR\_USER function - GLYPH Establishing and controlling accessibility by using the RCAC rule text - GLYPH SELECT, INSERT, and UPDATE behavior with RCAC - GLYPH Human resources example @@ -707,7 +707,7 @@ Figure 3-4 ALTER TABLE SQL statement -When row access control is activated on a table, a default permission is established for that table. The name of this permission is QIBM_DEFAULT_ _. This default permission contains a simple piece of logic (0=1) which is never true. The default permission effectively denies access to every user unless there is a permission defined that allows access explicitly. If row access control is activated on a table, and there is no permission that is defined, no one has permission to any rows. All queries against the table produce an empty set. +When row access control is activated on a table, a default permission is established for that table. The name of this permission is QIBM\_DEFAULT\_ \_. This default permission contains a simple piece of logic (0=1) which is never true. The default permission effectively denies access to every user unless there is a permission defined that allows access explicitly. If row access control is activated on a table, and there is no permission that is defined, no one has permission to any rows. All queries against the table produce an empty set. It is possible to define, create, and enable multiple permissions on a table. Logically, all of the permissions are ORed together to form a comprehensive test of the user's ability to access the data. A column can have only one mask that is defined over it. From an implementation standpoint, it does not matter if you create the column masks first or the row permissions first. @@ -724,9 +724,9 @@ A special register is a storage area that is defined for an application process IBM DB2 for i supports four different special registers that can be used to identify what user profiles are relevant to determining object authorities in the current connection to the server. SQL uses the term runtime authorization ID , which corresponds to a user profile on DB2 for i. Here are the four special registers: - GLYPH USER is the runtime user profile that determines the object authorities for the current connection to the server. It has a data type of VARCHAR(18). This value can be changed by the SQL statement SET SESSION AUTHORIZATION . -- GLYPH SESSION_USER is the same as the USER register, except that it has a data type of VARCHAR(128). -- GLYPH CURRENT USER was added in IBM i 7.2 and is similar to the USER register, but it has one important difference in that it also reports adopted authority. High-level language programs and SQL routines such as functions, procedures, and triggers can optionally be created to run using either the caller's or the owner's user profile to determine data authorities. For example, an SQL procedure can be created to run under the owner's authority by specifying SET OPTION USRPRF=*OWNER . This special register can also be referenced as CURRENT_USER. It has a data type of VARCHAR(128). -- GLYPH SYSTEM_USER is the user profile that initiates the connection to the server. It is not used by RCAC, but is included here for completeness. Many jobs, including the QZDASOINIT prestarted jobs, initially connect to the server with a default user profile and then change to use some other user profile. SYSTEM_USER reports this value, typically QUSER for a QZDASOINIT job. It has a data type of VARCHAR(128). +- GLYPH SESSION\_USER is the same as the USER register, except that it has a data type of VARCHAR(128). +- GLYPH CURRENT USER was added in IBM i 7.2 and is similar to the USER register, but it has one important difference in that it also reports adopted authority. High-level language programs and SQL routines such as functions, procedures, and triggers can optionally be created to run using either the caller's or the owner's user profile to determine data authorities. For example, an SQL procedure can be created to run under the owner's authority by specifying SET OPTION USRPRF=*OWNER . This special register can also be referenced as CURRENT\_USER. It has a data type of VARCHAR(128). +- GLYPH SYSTEM\_USER is the user profile that initiates the connection to the server. It is not used by RCAC, but is included here for completeness. Many jobs, including the QZDASOINIT prestarted jobs, initially connect to the server with a default user profile and then change to use some other user profile. SYSTEM\_USER reports this value, typically QUSER for a QZDASOINIT job. It has a data type of VARCHAR(128). In addition to these four special registers, any of the DB2 special registers can be referenced as part of the rule text. @@ -736,9 +736,9 @@ Table 3-1 Special registers and their corresponding values | Special register | Corresponding value | |----------------------|---------------------------------------------------------------------------------------------------------------------------------------| -| USER or SESSION_USER | The effective user of the thread excluding adopted authority. | -| CURRENT_USER | The effective user of the thread including adopted authority. When no adopted authority is present, this has the same value as USER. | -| SYSTEM_USER | The authorization ID that initiated the connection. | +| USER or SESSION\_USER | The effective user of the thread excluding adopted authority. | +| CURRENT\_USER | The effective user of the thread including adopted authority. When no adopted authority is present, this has the same value as USER. | +| SYSTEM\_USER | The authorization ID that initiated the connection. | Figure 3-5 shows the difference in the special register values when an adopted authority is used: @@ -764,30 +764,30 @@ Table 3-2 Built-in global variables | Global variable | Type | Description | |-----------------------|--------------|----------------------------------------------------------------| -| CLIENT_HOST | VARCHAR(255) | Host name of the current client as returned by the system | -| CLIENT_IPADDR | VARCHAR(128) | IP address of the current client as returned by the system | -| CLIENT_PORT | INTEGER | Port used by the current client to communicate with the server | -| PACKAGE_NAME | VARCHAR(128) | Name of the currently running package | -| PACKAGE_SCHEMA | VARCHAR(128) | Schema name of the currently running package | -| PACKAGE_VERSION | VARCHAR(64) | Version identifier of the currently running package | -| ROUTINE_SCHEMA | VARCHAR(128) | Schema name of the currently running routine | -| ROUTINE_SPECIFIC_NAME | VARCHAR(128) | Name of the currently running routine | -| ROUTINE_TYPE | CHAR(1) | Type of the currently running routine | +| CLIENT\_HOST | VARCHAR(255) | Host name of the current client as returned by the system | +| CLIENT\_IPADDR | VARCHAR(128) | IP address of the current client as returned by the system | +| CLIENT\_PORT | INTEGER | Port used by the current client to communicate with the server | +| PACKAGE\_NAME | VARCHAR(128) | Name of the currently running package | +| PACKAGE\_SCHEMA | VARCHAR(128) | Schema name of the currently running package | +| PACKAGE\_VERSION | VARCHAR(64) | Version identifier of the currently running package | +| ROUTINE\_SCHEMA | VARCHAR(128) | Schema name of the currently running routine | +| ROUTINE\_SPECIFIC\_NAME | VARCHAR(128) | Name of the currently running routine | +| ROUTINE\_TYPE | CHAR(1) | Type of the currently running routine | -## 3.3 VERIFY_GROUP_FOR_USER function +## 3.3 VERIFY\_GROUP\_FOR\_USER function -The VERIFY_GROUP_FOR_USER function was added in IBM i 7.2. Although it is primarily intended for use with RCAC permissions and masks, it can be used in other SQL statements. The first parameter must be one of these three special registers: SESSION_USER, USER, or CURRENT_USER. The second and subsequent parameters are a list of user or group profiles. Each of these values must be 1 - 10 characters in length. These values are not validated for their existence, which means that you can specify the names of user profiles that do not exist without receiving any kind of error. +The VERIFY\_GROUP\_FOR\_USER function was added in IBM i 7.2. Although it is primarily intended for use with RCAC permissions and masks, it can be used in other SQL statements. The first parameter must be one of these three special registers: SESSION\_USER, USER, or CURRENT\_USER. The second and subsequent parameters are a list of user or group profiles. Each of these values must be 1 - 10 characters in length. These values are not validated for their existence, which means that you can specify the names of user profiles that do not exist without receiving any kind of error. If a special register value is in the list of user profiles or it is a member of a group profile included in the list, the function returns a long integer value of 1. Otherwise, it returns a value of 0. It never returns the null value. -Here is an example of using the VERIFY_GROUP_FOR_USER function: +Here is an example of using the VERIFY\_GROUP\_FOR\_USER function: - 1. There are user profiles for MGR, JANE, JUDY, and TONY. - 2. The user profile JANE specifies a group profile of MGR. - 3. If a user is connected to the server using user profile JANE, all of the following function invocations return a value of 1: ``` -VERIFY_GROUP_FOR_USER (CURRENT_USER, 'MGR') VERIFY_GROUP_FOR_USER (CURRENT_USER, 'JANE', 'MGR') VERIFY_GROUP_FOR_USER (CURRENT_USER, 'JANE', 'MGR', 'STEVE') The following function invocation returns a value of 0: VERIFY_GROUP_FOR_USER (CURRENT_USER, 'JUDY', 'TONY') +VERIFY\_GROUP\_FOR\_USER (CURRENT\_USER, 'MGR') VERIFY\_GROUP\_FOR\_USER (CURRENT\_USER, 'JANE', 'MGR') VERIFY\_GROUP\_FOR\_USER (CURRENT\_USER, 'JANE', 'MGR', 'STEVE') The following function invocation returns a value of 0: VERIFY\_GROUP\_FOR\_USER (CURRENT\_USER, 'JUDY', 'TONY') ``` ## 3.4 Establishing and controlling accessibility by using the RCAC rule text @@ -806,7 +806,7 @@ For the column mask, the logic follows the same rules as the CASE expression. Th For more information about what is permitted, see the "Database programming" topic of the IBM i 7.2 Knowledge Center, found at: -http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahg/rzahgdbp.htm?lang =en +http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzahg/rzahgdbp.htm?lang =en One of the first tasks in either the row permission or the column mask logic is to determine who the user is, and whether they have access to the data. Elegant methods to establish the identity and attributes of the user can be employed by using the special registers, global variables, and the VERIFY function. After the user's identity is established, it is a simple matter of allowing or disallowing access by using true or false testing. The examples that are included in this paper demonstrate some of the more common and obvious techniques. @@ -814,13 +814,13 @@ More sophisticated methods can employ existential, day of year / time of day, an Example 3-1 Subquery that is used as part of the rule -DATE_MASTER D +DATE\_MASTER D -D.BUSINESS_DAY = 'Y') +D.BUSINESS\_DAY = 'Y') -CURRENT_DATE IN (SELECT D.DATE_KEY FROM WHERE +CURRENT\_DATE IN (SELECT D.DATE\_KEY FROM WHERE -Given that joins and subqueries can be used to perform set-based operations against existing data that is housed in other objects, almost any relational test can be constructed. If the data in the objects is manipulated over time, the RCAC test logic (and user query results) can be changed without modifying the actual row permission or column mask. This includes moving a user from one group to another or changing a column value that is used to allow or disallow access. For example, if Saturday is now a valid business day, only the BUSINESS_DAY value in the DATE_MASTER must be updated, not the permission logic. This technique can potentially avoid downtime because of the exclusive lock that is needed on the table when adding or changing RCAC definitions. +Given that joins and subqueries can be used to perform set-based operations against existing data that is housed in other objects, almost any relational test can be constructed. If the data in the objects is manipulated over time, the RCAC test logic (and user query results) can be changed without modifying the actual row permission or column mask. This includes moving a user from one group to another or changing a column value that is used to allow or disallow access. For example, if Saturday is now a valid business day, only the BUSINESS\_DAY value in the DATE\_MASTER must be updated, not the permission logic. This technique can potentially avoid downtime because of the exclusive lock that is needed on the table when adding or changing RCAC definitions. ## 3.5 SELECT, INSERT, and UPDATE behavior with RCAC @@ -836,7 +836,7 @@ Note: DB2 does not provide any indication back to the user that the data set req This section illustrates with a simple example the usage of RCAC on a typical Human Resources application (schema). In this sample Human Resources schema, there is an important table that is called EMPLOYEES that contains all the information that is related to the employees of the company. Among the information that normally is stored in the EMPLOYEES table, there is some sensitive information that must be hidden from certain users: -- GLYPH Tax_Id information +- GLYPH Tax\_Id information - GLYPH YEAR of the birth date of the employee (hiding the age of the employee) In this example, there are four different types of users: @@ -848,21 +848,21 @@ In this example, there are four different types of users: The following sections describe step-by-step what is needed to be done to implement RCAC in this environment. -## 3.6.1 Assigning the QIBM_DB_SECADM function ID to the consultants +## 3.6.1 Assigning the QIBM\_DB\_SECADM function ID to the consultants -The consultant must have authority to implement RCAC, so you must use one of the function IDs that are provided in DB2 for i (see 2.1.5, "Security Administrator function: QIBM_DB_SECADM" on page 9). Complete the following steps: +The consultant must have authority to implement RCAC, so you must use one of the function IDs that are provided in DB2 for i (see 2.1.5, "Security Administrator function: QIBM\_DB\_SECADM" on page 9). Complete the following steps: - 1. Run the Change Functional Usage ( CHGFCNUSG ) CL commands that are shown in Example 3-2. These commands must be run by someone that has the *SECOFR authority. Example 3-2 Function ID required to implement RCAC -CHGFCNUSG FCNID(QIBM_DB_SECADM) USER(HBEDOYA) USAGE(*ALLOWED) CHGFCNUSG FCNID(QIBM_DB_SECADM) USER(MCAIN) USAGE(*ALLOWED) +CHGFCNUSG FCNID(QIBM\_DB\_SECADM) USER(HBEDOYA) USAGE(*ALLOWED) CHGFCNUSG FCNID(QIBM\_DB\_SECADM) USER(MCAIN) USAGE(*ALLOWED) - 2. There is a way to discover which user profiles have authorization to implement RCAC. This can be done by running the SQL statement that is shown in Example 3-3. Example 3-3 Verifying what user profiles have authorization to implement RCAC -SELECT function_id, user_name, usage, user_type FROM qsys2.function_usage WHERE function_id ='QIBM_DB_SECADM' ORDER BY user_name; +SELECT function\_id, user\_name, usage, user\_type FROM qsys2.function\_usage WHERE function\_id ='QIBM\_DB\_SECADM' ORDER BY user\_name; - 3. The result of the SQL statement is shown in Figure 3-6. In this example, either MCAIN or HBEDOYA can implement RCAC in the Human Resources database. @@ -895,7 +895,7 @@ Before implementing RCAC, run some simple SQL statements to demonstrate data acc Example 3-5 Counting the number of employees -SELECT COUNT(*) as ROW_COUNT FROM HR_SCHEMA.EMPLOYEES; +SELECT COUNT(*) as ROW\_COUNT FROM HR\_SCHEMA.EMPLOYEES; The result of this query is shown in Figure 3-7, which is the total number of employees of the company. @@ -907,7 +907,7 @@ Figure 3-7 Number of employees Example 3-6 Displaying the information of the Employees -SELECT EMPLOYEE_ID, LAST_NAME, JOB_DESCRIPTION, DATE_OF_BIRTH, TAX_ID, USER_ID, MANAGER_OF_EMPLOYEE FROM HR_SCHEMA.EMPLOYEES +SELECT EMPLOYEE\_ID, LAST\_NAME, JOB\_DESCRIPTION, DATE\_OF\_BIRTH, TAX\_ID, USER\_ID, MANAGER\_OF\_EMPLOYEE FROM HR\_SCHEMA.EMPLOYEES ## The result of this query is shown in Figure 3-8. @@ -928,10 +928,10 @@ To implement this row permission, run the SQL statement that is shown in Example Example 3-7 Creating a permission for the EMPLOYEE table ``` -CREATE PERMISSION HR_SCHEMA.PERMISSION1_ON_EMPLOYEES ON HR_SCHEMA.EMPLOYEES AS EMPLOYEES FOR ROWS WHERE ( VERIFY_GROUP_FOR_USER ( SESSION_USER , 'HR' ) = 1 ) OR ( VERIFY_GROUP_FOR_USER ( SESSION_USER , 'MGR' ) = 1 AND ( EMPLOYEES . MANAGER_OF_EMPLOYEE = SESSION_USER OR EMPLOYEES . USER_ID = SESSION_USER ) ) OR ( VERIFY_GROUP_FOR_USER ( SESSION_USER , 'EMP' ) = 1 AND EMPLOYEES . USER_ID = SESSION_USER ) ENFORCED FOR ALL ACCESS ENABLE ; +CREATE PERMISSION HR\_SCHEMA.PERMISSION1\_ON\_EMPLOYEES ON HR\_SCHEMA.EMPLOYEES AS EMPLOYEES FOR ROWS WHERE ( VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'HR' ) = 1 ) OR ( VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'MGR' ) = 1 AND ( EMPLOYEES . MANAGER\_OF\_EMPLOYEE = SESSION\_USER OR EMPLOYEES . USER\_ID = SESSION\_USER ) ) OR ( VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'EMP' ) = 1 AND EMPLOYEES . USER\_ID = SESSION\_USER ) ENFORCED FOR ALL ACCESS ENABLE ; ``` -- 2. Look at the definition of the table and see the permissions, as shown in Figure 3-9. QIBM_DEFAULT_EMPLOYEE_HR_SCHEMA is the default permission, as described in 3.1.2, "Enabling and activating RCAC" on page 16. +- 2. Look at the definition of the table and see the permissions, as shown in Figure 3-9. QIBM\_DEFAULT\_EMPLOYEE\_HR\_SCHEMA is the default permission, as described in 3.1.2, "Enabling and activating RCAC" on page 16. Figure 3-9 Row permissions that are shown in System i Navigator @@ -941,37 +941,37 @@ Figure 3-9 Row permissions that are shown in System i Navigator Define the different masks for the columns that are sensitive by completing the following steps: -- 1. Start with the DAY_OF_BIRTH column. In this example, the rules to enforce include the following ones: +- 1. Start with the DAY\_OF\_BIRTH column. In this example, the rules to enforce include the following ones: - -Human Resources can see the entire date of birth of the employees. - -Employees can see only their own date of birth. - -Managers can see the date of birth of their employees masked with YEAR being 9999. To implement this column mask, run the SQL statement that is shown in Example 3-8. -Example 3-8 Creation of a mask on the DATE_OF_BIRTH column +Example 3-8 Creation of a mask on the DATE\_OF\_BIRTH column -CREATE MASK HR_SCHEMA.MASK_DATE_OF_BIRTH_ON_EMPLOYEES ON HR_SCHEMA.EMPLOYEES AS EMPLOYEES FOR COLUMN DATE_OF_BIRTH +CREATE MASK HR\_SCHEMA.MASK\_DATE\_OF\_BIRTH\_ON\_EMPLOYEES ON HR\_SCHEMA.EMPLOYEES AS EMPLOYEES FOR COLUMN DATE\_OF\_BIRTH RETURN CASE ``` -WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'HR', 'EMP' ) = 1 THEN EMPLOYEES . DATE_OF_BIRTH WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'MGR' ) = 1 AND SESSION_USER = EMPLOYEES . USER_ID THEN EMPLOYEES . DATE_OF_BIRTH WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'MGR' ) = 1 AND SESSION_USER <> EMPLOYEES . USER_ID THEN ( 9999 || '-' || MONTH ( EMPLOYEES . DATE_OF_BIRTH ) || '-' || DAY (EMPLOYEES.DATE_OF_BIRTH )) ELSE NULL END ENABLE ; +WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'HR', 'EMP' ) = 1 THEN EMPLOYEES . DATE\_OF\_BIRTH WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'MGR' ) = 1 AND SESSION\_USER = EMPLOYEES . USER\_ID THEN EMPLOYEES . DATE\_OF\_BIRTH WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'MGR' ) = 1 AND SESSION\_USER <> EMPLOYEES . USER\_ID THEN ( 9999 || '-' || MONTH ( EMPLOYEES . DATE\_OF\_BIRTH ) || '-' || DAY (EMPLOYEES.DATE\_OF\_BIRTH )) ELSE NULL END ENABLE ; ``` -- 2. The other column to mask in this example is the TAX_ID information. In this example, the rules to enforce include the following ones: -- -Human Resources can see the unmasked TAX_ID of the employees. -- -Employees can see only their own unmasked TAX_ID. -- -Managers see a masked version of TAX_ID with the first five characters replaced with the X character (for example, XXX-XX-1234). -- -Any other person sees the entire TAX_ID as masked, for example, XXX-XX-XXXX. +- 2. The other column to mask in this example is the TAX\_ID information. In this example, the rules to enforce include the following ones: +- -Human Resources can see the unmasked TAX\_ID of the employees. +- -Employees can see only their own unmasked TAX\_ID. +- -Managers see a masked version of TAX\_ID with the first five characters replaced with the X character (for example, XXX-XX-1234). +- -Any other person sees the entire TAX\_ID as masked, for example, XXX-XX-XXXX. - To implement this column mask, run the SQL statement that is shown in Example 3-9. -Example 3-9 Creating a mask on the TAX_ID column +Example 3-9 Creating a mask on the TAX\_ID column ``` -CREATE MASK HR_SCHEMA.MASK_TAX_ID_ON_EMPLOYEES ON HR_SCHEMA.EMPLOYEES AS EMPLOYEES FOR COLUMN TAX_ID RETURN CASE WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'HR' ) = 1 THEN EMPLOYEES . TAX_ID WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'MGR' ) = 1 AND SESSION_USER = EMPLOYEES . USER_ID THEN EMPLOYEES . TAX_ID WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'MGR' ) = 1 AND SESSION_USER <> EMPLOYEES . USER_ID THEN ( 'XXX-XX-' CONCAT QSYS2 . SUBSTR ( EMPLOYEES . TAX_ID , 8 , 4 ) ) WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'EMP' ) = 1 THEN EMPLOYEES . TAX_ID ELSE 'XXX-XX-XXXX' END ENABLE ; +CREATE MASK HR\_SCHEMA.MASK\_TAX\_ID\_ON\_EMPLOYEES ON HR\_SCHEMA.EMPLOYEES AS EMPLOYEES FOR COLUMN TAX\_ID RETURN CASE WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'HR' ) = 1 THEN EMPLOYEES . TAX\_ID WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'MGR' ) = 1 AND SESSION\_USER = EMPLOYEES . USER\_ID THEN EMPLOYEES . TAX\_ID WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'MGR' ) = 1 AND SESSION\_USER <> EMPLOYEES . USER\_ID THEN ( 'XXX-XX-' CONCAT QSYS2 . SUBSTR ( EMPLOYEES . TAX\_ID , 8 , 4 ) ) WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'EMP' ) = 1 THEN EMPLOYEES . TAX\_ID ELSE 'XXX-XX-XXXX' END ENABLE ; ``` -- 3. Figure 3-10 shows the masks that are created in the HR_SCHEMA. +- 3. Figure 3-10 shows the masks that are created in the HR\_SCHEMA. Figure 3-10 Column masks shown in System i Navigator @@ -987,11 +987,11 @@ Now that you have created the row permission and the two column masks, RCAC must - /* Active Row Access Control (permissions) */ -/* Active Column Access Control (masks) ALTER TABLE HR_SCHEMA.EMPLOYEES ACTIVATE ROW ACCESS CONTROL ACTIVATE COLUMN ACCESS CONTROL; +/* Active Column Access Control (masks) ALTER TABLE HR\_SCHEMA.EMPLOYEES ACTIVATE ROW ACCESS CONTROL ACTIVATE COLUMN ACCESS CONTROL; */ -- 2. Look at the definition of the EMPLOYEE table, as shown in Figure 3-11. To do this, from the main navigation pane of System i Navigator, click Schemas  HR_SCHEMA  Tables , right-click the EMPLOYEES table, and click Definition . +- 2. Look at the definition of the EMPLOYEE table, as shown in Figure 3-11. To do this, from the main navigation pane of System i Navigator, click Schemas  HR\_SCHEMA  Tables , right-click the EMPLOYEES table, and click Definition . Figure 3-11 Selecting the EMPLOYEES table from System i Navigator @@ -1011,7 +1011,7 @@ You are now ready to start testing RCAC with the four different users. Complete Example 3-11 EMPLOYEES count -SELECT COUNT(*) as ROW_COUNT FROM HR_SCHEMA.EMPLOYEES; +SELECT COUNT(*) as ROW\_COUNT FROM HR\_SCHEMA.EMPLOYEES; - 2. The result of the query for a user that belongs to the HR group profile is shown in Figure 3-13. This user can see all the 42 rows (employees). @@ -1044,14 +1044,14 @@ Does the result make sense? Yes, it does because RCAC is enabled. Example 3-12 SELECT statement to test with the different users ``` -SELECT EMPLOYEE_ID, LAST_NAME, JOB_DESCRIPTION, DATE_OF_BIRTH, TAX_ID, USER_ID, MANAGER_OF_EMPLOYEE FROM HR_SCHEMA.EMPLOYEES +SELECT EMPLOYEE\_ID, LAST\_NAME, JOB\_DESCRIPTION, DATE\_OF\_BIRTH, TAX\_ID, USER\_ID, MANAGER\_OF\_EMPLOYEE FROM HR\_SCHEMA.EMPLOYEES ``` - 7. Figure 3-17 shows the results of the query for a Human Resources (VGLUCCHESS) user profile. The user can see all the rows and all the columns. Figure 3-17 SQL statement result by Human Resources user profile -- 8. Figure 3-18 shows the results of the same query for the Manager (TQSPENSER). Notice the masking of the DATE_OF_BIRTH and TAX_ID columns. +- 8. Figure 3-18 shows the results of the same query for the Manager (TQSPENSER). Notice the masking of the DATE\_OF\_BIRTH and TAX\_ID columns. Figure 3-18 SQL statement result by Manager profile @@ -1067,7 +1067,7 @@ Figure 3-20 SQL statement result by Consultant/DBE profile This section covers data access with a view and RCAC. Complete the following steps: -- 1. The EMPLOYEES table has a column that is called On_Leave_Flag (Figure 3-21 on page 33) indicating that the employee is on Leave of Absence. For this purpose, a view is created that lists only the employees that are on leave. +- 1. The EMPLOYEES table has a column that is called On\_Leave\_Flag (Figure 3-21 on page 33) indicating that the employee is on Leave of Absence. For this purpose, a view is created that lists only the employees that are on leave. Figure 3-21 Employees on leave @@ -1076,34 +1076,34 @@ Figure 3-21 Employees on leave Example 3-13 VIew of employees on leave ``` -CREATE VIEW HR_SCHEMA.EMPLOYEES_ON_LEAVE (EMPLOYEE_ID, FIRST_NAME, MIDDLE_INITIAL, LAST_NAME, WORK_DEPARTMENT, PHONE_EXTENSION, JOB_DESCRIPTION, DATE_OF_BIRTH, +CREATE VIEW HR\_SCHEMA.EMPLOYEES\_ON\_LEAVE (EMPLOYEE\_ID, FIRST\_NAME, MIDDLE\_INITIAL, LAST\_NAME, WORK\_DEPARTMENT, PHONE\_EXTENSION, JOB\_DESCRIPTION, DATE\_OF\_BIRTH, ``` -TAX_ID, USER_ID, MANAGER_OF_EMPLOYEE, ON_LEAVE_FLAG ) +TAX\_ID, USER\_ID, MANAGER\_OF\_EMPLOYEE, ON\_LEAVE\_FLAG ) AS -SELECT EMPLOYEE_ID, FIRST_NAME , MIDDLE_INITIAL, LAST_NAME , WORK_DEPARTMENT, PHONE_EXTENSION, JOB_DESCRIPTION, DATE_OF_BIRTH, TAX_ID, USER_ID, MANAGER_OF_EMPLOYEE, +SELECT EMPLOYEE\_ID, FIRST\_NAME , MIDDLE\_INITIAL, LAST\_NAME , WORK\_DEPARTMENT, PHONE\_EXTENSION, JOB\_DESCRIPTION, DATE\_OF\_BIRTH, TAX\_ID, USER\_ID, MANAGER\_OF\_EMPLOYEE, -ON_LEAVE_FLAG +ON\_LEAVE\_FLAG FROM -HR_SCHEMA.EMPLOYEES +HR\_SCHEMA.EMPLOYEES WHERE -ON_LEAVE_FLAG = 'Y'; +ON\_LEAVE\_FLAG = 'Y'; - 3. Use the view to query the data and see who is on leave. The SQL statement that is used is shown in Example 3-14: Example 3-14 SQL statement for employees on leave -SELECT EMPLOYEE_ID, LAST_NAME, JOB_DESCRIPTION, DATE_OF_BIRTH, TAX_ID, USER_ID, MANAGER_OF_EMPLOYEE FROM +SELECT EMPLOYEE\_ID, LAST\_NAME, JOB\_DESCRIPTION, DATE\_OF\_BIRTH, TAX\_ID, USER\_ID, MANAGER\_OF\_EMPLOYEE FROM -HR_SCHEMA.EMPLOYEES_ON_LEAVE; +HR\_SCHEMA.EMPLOYEES\_ON\_LEAVE; -- 4. Start with the Human Resources person (VGLUCCHESS) and see what is the result of the previous query. He sees the two employees that are on leave and no masking is done over the DATE_OF_BIRTH and TAX_ID columns. The results of the query are shown in Figure 3-22. +- 4. Start with the Human Resources person (VGLUCCHESS) and see what is the result of the previous query. He sees the two employees that are on leave and no masking is done over the DATE\_OF\_BIRTH and TAX\_ID columns. The results of the query are shown in Figure 3-22. Figure 3-22 Employees on leave - Human Resources user @@ -1147,7 +1147,7 @@ In this example, it is assumed that the Bank employees have access to the tables Bank customers have access to their accounts and transactions by using a new web application. Each customer has unique credentials for logging in to the application. The authentication of the customer is handled by the web server. After the customer is authenticated, the web server establishes a connection to DB2 for data access. This connection uses a common IBM i user profile that is known as WEBUSER. This user profile is secured and is used only by the web application. No Bank employee has access to the WEBUSER profile, and no customer has an IBM i user profile. -The customer's identity is passed to DB2 by using a global variable. The global variable is secured and can be accessed only by the WEBUSER. The web application sets the CUSTOMER_LOGIN_ID variable to the customer's login value. This value is compared to the customer's login value that is found in the CUSTOMER_LOGIN_ID column of the CUSTOMERS table. +The customer's identity is passed to DB2 by using a global variable. The global variable is secured and can be accessed only by the WEBUSER. The web application sets the CUSTOMER\_LOGIN\_ID variable to the customer's login value. This value is compared to the customer's login value that is found in the CUSTOMER\_LOGIN\_ID column of the CUSTOMERS table. Applications that do not use the web interface do not have to be changed because the global variable is NULL by default. @@ -1184,12 +1184,12 @@ Figure 4-3 Column masks | | | CUSTOMERS | ACCOUNTS | |----------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------| -| SECURITY | No Rows | CUSTOMER_DRIVERS_LICENSE_NUMBER CUSTOMER_EMAIL CUSTOMER_LOGIN_ID CUSTOMER_SECURITY_QUESTION CUSTOMER_SECURITY_QUESTION_ANSWER CUSTOMER_TAX_ID | ACCOUNT_NUMBER | -| DBE | All Rows | CUSTOMER_DRIVERS_LICENSE_NUMBER CUSTOMER_EMAIL CUSTOMER_LOGIN_ID CUSTOMER_SECURITY_QUESTION CUSTOMER_SECURITY_QUESTION_ANSWER CUSTOMER_TAX_ID | ACCOUNT NUMBER ACCOUNT_NUMBER | +| SECURITY | No Rows | CUSTOMER\_DRIVERS\_LICENSE\_NUMBER CUSTOMER\_EMAIL CUSTOMER\_LOGIN\_ID CUSTOMER\_SECURITY\_QUESTION CUSTOMER\_SECURITY\_QUESTION\_ANSWER CUSTOMER\_TAX\_ID | ACCOUNT\_NUMBER | +| DBE | All Rows | CUSTOMER\_DRIVERS\_LICENSE\_NUMBER CUSTOMER\_EMAIL CUSTOMER\_LOGIN\_ID CUSTOMER\_SECURITY\_QUESTION CUSTOMER\_SECURITY\_QUESTION\_ANSWER CUSTOMER\_TAX\_ID | ACCOUNT NUMBER ACCOUNT\_NUMBER | | ADMIN | All Rows | None | None | -| TELLER | All Rows | CUSTOMER_EMAIL CUSTOMER_LOGIN_ID CUSTOMER_SECURITY_QUESTION CUSTOMER_SECURITY_QUESTION_ANSWER CUSTOMER TAX ID _ _ | None | +| TELLER | All Rows | CUSTOMER\_EMAIL CUSTOMER\_LOGIN\_ID CUSTOMER\_SECURITY\_QUESTION CUSTOMER\_SECURITY\_QUESTION\_ANSWER CUSTOMER TAX ID \_ \_ | None | | CUSTOMER | Own Rows | None | None | -| PUBLIC | No Rows | CUSTOMER_DRIVERS_LICENSE_NUMBER CUSTOMER_EMAIL CUSTOMER LOGIN ID CUSTOMER_LOGIN_ID CUSTOMER_SECURITY_QUESTION CUSTOMER_SECURITY_QUESTION_ANSWER CUSTOMER_TAX_ID | ACCOUNT_NUMBER | +| PUBLIC | No Rows | CUSTOMER\_DRIVERS\_LICENSE\_NUMBER CUSTOMER\_EMAIL CUSTOMER LOGIN ID CUSTOMER\_LOGIN\_ID CUSTOMER\_SECURITY\_QUESTION CUSTOMER\_SECURITY\_QUESTION\_ANSWER CUSTOMER\_TAX\_ID | ACCOUNT\_NUMBER | For the demonstration and testing of RCAC in this example, the following users interact with the database. Furthermore, the column masking rules are developed independently of the row permissions. If a person does not have permission to access the row, the column mask processing does not occur. @@ -1212,9 +1212,9 @@ Figure 4-4 Data model of the banking scenario This section covers the following steps: - GLYPH Reviewing the tables that are used in this example -- GLYPH Assigning function ID QIBM_DB_SECADM to the Database Engineers group +- GLYPH Assigning function ID QIBM\_DB\_SECADM to the Database Engineers group - GLYPH Creating group profiles for the users and their roles -- GLYPH Creating the CUSTOMER_LOGIN_ID global variable +- GLYPH Creating the CUSTOMER\_LOGIN\_ID global variable - GLYPH Defining and creating row permissions - GLYPH Defining and creating column masks - GLYPH Restricting the inserting and updating of masked data @@ -1235,7 +1235,7 @@ Note: Appendix A, "Database definitions for the RCAC banking example" on page 12 To review the attributes of each table that is used in this banking example, complete the following steps: -- 1. Review the columns of each the tables through System i Navigator. Expand Database  named Database  Schemas  BANK_SCHEMA  Tables . +- 1. Review the columns of each the tables through System i Navigator. Expand Database  named Database  Schemas  BANK\_SCHEMA  Tables . - 2. Right-click the CUSTOMERS table and select Definition . Figure 4-6 shows the attributes for the CUSTOMERS table. The Row access control and Column access control options are not selected, which indicates that the table does not have RCAC implemented. Figure 4-6 CUSTOMERS table attributes @@ -1286,7 +1286,7 @@ Figure 4-14 Reviewing the constraints on the TRANSACTIONS table Now that you have reviewed the database model for this example, the following sections describe the steps that are required to implement RCAC in this banking scenario. -## 4.3.2 Assigning function ID QIBM_DB_SECADM to the Database Engineers group +## 4.3.2 Assigning function ID QIBM\_DB\_SECADM to the Database Engineers group The first step is to assign the appropriate function usage ID to the Database Engineers (DBEs) that will be implementing RCAC. For a description of function usage IDs, see 2.1, "Roles" on page 8. In this example, the DBEs are users MCAIN and HBEDOYA. @@ -1354,33 +1354,33 @@ Figure 4-23 Newly created group profiles -## 4.3.4 Creating the CUSTOMER_LOGIN_ID global variable +## 4.3.4 Creating the CUSTOMER\_LOGIN\_ID global variable -In this step, you create a global variable that is used to capture the Customer_Login_ID information, which is required to validate the permissions. For more information about global variables, see 3.2.2, "Built-in global variables" on page 19. +In this step, you create a global variable that is used to capture the Customer\_Login\_ID information, which is required to validate the permissions. For more information about global variables, see 3.2.2, "Built-in global variables" on page 19. ## Complete the following steps: -- 1. From System i Navigator, under the schema Bank_Schema, right-click Global Variable and select New  Global Variable , as shown in Figure 4-24. +- 1. From System i Navigator, under the schema Bank\_Schema, right-click Global Variable and select New  Global Variable , as shown in Figure 4-24. Figure 4-24 Creating a global variable -- 2. The New Global Variable window opens, as shown in Figure 4-25. Enter the global variable name of CUSTOMER_LOGIN_ID, select the data type of VARCHAR, and leave the default value of NULL. This default value ensures that users that do not use the web interface do not have permission to access the data. Click OK . +- 2. The New Global Variable window opens, as shown in Figure 4-25. Enter the global variable name of CUSTOMER\_LOGIN\_ID, select the data type of VARCHAR, and leave the default value of NULL. This default value ensures that users that do not use the web interface do not have permission to access the data. Click OK . -Figure 4-25 Creating a global variable called CUSTOMER_LOGIN_ID +Figure 4-25 Creating a global variable called CUSTOMER\_LOGIN\_ID -- 3. Now that the global variable is created, assign permissions to the variable so that it can be set by the program. Right-click the CUSTOMER_LOGIN_ID global variable and select Permissions , as shown in Figure 4-26. +- 3. Now that the global variable is created, assign permissions to the variable so that it can be set by the program. Right-click the CUSTOMER\_LOGIN\_ID global variable and select Permissions , as shown in Figure 4-26. -Figure 4-26 Setting permissions on the CUSTOMER_LOGIN_ID global variable +Figure 4-26 Setting permissions on the CUSTOMER\_LOGIN\_ID global variable - 4. The Permissions window opens, as shown in Figure 4-27. Select Change authority for Webuser so that the application can set this global variable. -Figure 4-27 Setting change permissions for Webuser on the CUSTOMER_LOGIN_ID global variable +Figure 4-27 Setting change permissions for Webuser on the CUSTOMER\_LOGIN\_ID global variable @@ -1388,7 +1388,7 @@ Figure 4-27 Setting change permissions for Webuser on the CUSTOMER_LOGIN_ID glob You now ready to define the row permissions of the tables. Complete the following steps: -- 1. From the navigation pane of System i Navigator, click Schemas  BANK_SCHEMA , right-click Row Permissions , and select New  Row Permission , as shown in Figure 4-28. +- 1. From the navigation pane of System i Navigator, click Schemas  BANK\_SCHEMA , right-click Row Permissions , and select New  Row Permission , as shown in Figure 4-28. Figure 4-28 Selecting new row permissions @@ -1396,7 +1396,7 @@ Figure 4-28 Selecting new row permissions - 2. The New Row Permission window opens, as shown in Figure 4-29. Enter the information regarding the row permissions on the CUSTOMERS table. This row permission defines what is established in the following policy: - -User profiles that belong to DBE, ADMIN, and TELLER group profiles can see all the rows. -- -User profiles that belong to the CUSTOMERS group profile (that is, the WEBUSER user) can see only the rows that match their customer login ID. The login ID value representing the online banking user is passed from the web application to the database by using the global variable CUSTOMER_LOGIN_ID. The permission rule uses a subquery to check whether the global variable matches the CUSTOMER_LOGIN_ID column value in the CUSTOMERS table. +- -User profiles that belong to the CUSTOMERS group profile (that is, the WEBUSER user) can see only the rows that match their customer login ID. The login ID value representing the online banking user is passed from the web application to the database by using the global variable CUSTOMER\_LOGIN\_ID. The permission rule uses a subquery to check whether the global variable matches the CUSTOMER\_LOGIN\_ID column value in the CUSTOMERS table. - -Any other user profile cannot see any rows at all. Select the Enabled option. Click OK . @@ -1407,7 +1407,7 @@ Figure 4-29 New row permissions on the CUSTOMERS table - 3. Define the row permissions for the ACCOUNTS table. The New Row Permission window opens, as shown in Figure 4-30. Enter the information regarding the row permissions on the ACCOUNTS table. This row permission defines what is established in the following policy: - -User profiles that belong to DBE, ADMIN and TELLER group profiles can see all the rows. -- -User profiles that belong to the CUSTOMERS group profile (that is, the WEBUSER user) can see only the rows that match their customer login ID. The login ID value representing the online banking user is passed from the web application to the database by using the global variable CUSTOMER_LOGIN_ID. The permission rule uses a subquery to check whether the global variable matches the CUSTOMER_LOGIN_ID column value in the CUSTOMERS table. +- -User profiles that belong to the CUSTOMERS group profile (that is, the WEBUSER user) can see only the rows that match their customer login ID. The login ID value representing the online banking user is passed from the web application to the database by using the global variable CUSTOMER\_LOGIN\_ID. The permission rule uses a subquery to check whether the global variable matches the CUSTOMER\_LOGIN\_ID column value in the CUSTOMERS table. - -Any other user profile cannot see any rows at all. Select the Enabled option. Click OK . @@ -1418,9 +1418,9 @@ Figure 4-30 New row permissions on the ACCOUNTS table - 4. Define the row permissions on the TRANSACTIONS table. The New Row Permission window opens, as shown in Figure 4-31. Enter the information regarding the row permissions on the TRANSACTIONS table. This row permission defines what is established in the following policy: - -User profiles that belong to DBE, ADMIN, and TELLER group profiles can see all of the rows. -- -User profiles that belong to the CUSTOMERS group profile (that is, the WEBUSER user) can see only the rows that match their customer login ID. The login ID value representing the online banking user is passed from the web application to the database by using the global variable CUSTOMER_LOGIN_ID. The permission rule uses a subquery to check whether the global variable matches the CUSTOMER_LOGIN_ID column value in the CUSTOMERS table. +- -User profiles that belong to the CUSTOMERS group profile (that is, the WEBUSER user) can see only the rows that match their customer login ID. The login ID value representing the online banking user is passed from the web application to the database by using the global variable CUSTOMER\_LOGIN\_ID. The permission rule uses a subquery to check whether the global variable matches the CUSTOMER\_LOGIN\_ID column value in the CUSTOMERS table. -Note: You must join back to ACCOUNTS and then to CUSTOMERS by using a subquery to check whether the global variable matches CUSTOMER_LOGIN_ID. Also, if the row permission or column mask rule text references another table with RCAC defined, the RCAC for the referenced table is ignored. +Note: You must join back to ACCOUNTS and then to CUSTOMERS by using a subquery to check whether the global variable matches CUSTOMER\_LOGIN\_ID. Also, if the row permission or column mask rule text references another table with RCAC defined, the RCAC for the referenced table is ignored. - -Any other user profile cannot see any rows at all. @@ -1432,7 +1432,7 @@ Figure 4-31 New row permissions on the TRANSACTIONS table - 5. To verify that the row permissions are enabled, from System i Navigator, click Row Permissions , as shown in Figure 4-32. The three row permissions are created and enabled. -Figure 4-32 List of row permissions on BANK_SCHEMA +Figure 4-32 List of row permissions on BANK\_SCHEMA @@ -1440,7 +1440,7 @@ Figure 4-32 List of row permissions on BANK_SCHEMA This section defines the masks on the columns. Complete the following steps: -- 1. From the main navigation pane of System i Navigator, click Schemas  BANK_SCHEMA , right-click Column Masks , and select New  Column Mask , as shown in Figure 4-33. +- 1. From the main navigation pane of System i Navigator, click Schemas  BANK\_SCHEMA , right-click Column Masks , and select New  Column Mask , as shown in Figure 4-33. Figure 4-33 Creating a column mask @@ -1448,7 +1448,7 @@ Figure 4-33 Creating a column mask - 2. In the New Column Mask window, which is shown in Figure 4-34, enter the following information: - -Select the CUSTOMERS table on which to create the column mask. -- -Select the Column to mask; in this example, it is CUSTOMER_EMAIL. +- -Select the Column to mask; in this example, it is CUSTOMER\_EMAIL. - -Define the masking logic depending on the rules that you want to enforce. In this example, either the ADMIN or CUSTOMER group profiles can see the entire email address; otherwise, it is masked to ****@****. Select the Enabled option. Click OK . @@ -1458,16 +1458,16 @@ Figure 4-34 Defining a column mask on the CUSTOMERS table - 3. Repeat steps 1 on page 58 and 2 to create column masks for the following columns: -- -MASK_DRIVERS_LICENSE_ON_CUSTOMERS -- -MASK_LOGIN_ID_ON_CUSTOMERS -- -MASK_SECURITY_QUESTION_ANSWER_ON_CUSTOMERS -- -MASK_ACCOUNT_NUMBER_ON_ACCOUNTS -- -MASK_SECURITY_QUESTION_ON_CUSTOMERS -- -MASK_TAX_ID_ON_CUSTOMERS +- -MASK\_DRIVERS\_LICENSE\_ON\_CUSTOMERS +- -MASK\_LOGIN\_ID\_ON\_CUSTOMERS +- -MASK\_SECURITY\_QUESTION\_ANSWER\_ON\_CUSTOMERS +- -MASK\_ACCOUNT\_NUMBER\_ON\_ACCOUNTS +- -MASK\_SECURITY\_QUESTION\_ON\_CUSTOMERS +- -MASK\_TAX\_ID\_ON\_CUSTOMERS - 4. To verify that the column masks are enabled, from System i Navigator, click Column Masks , as shown in Figure 4-35. The seven column masks are created and enabled. -Figure 4-35 List of column masks on BANK_SCHEMA +Figure 4-35 List of column masks on BANK\_SCHEMA @@ -1477,7 +1477,7 @@ This step defines the check constraints that support the column masks to make su ## Complete the following steps: -- 1. Create a check constraint on the column CUSTOMER_EMAIL in the CUSTOMERS table. From the navigation pane of System i Navigator, right-click the CUSTOMERS table and select Definition , as shown Figure 4-36 +- 1. Create a check constraint on the column CUSTOMER\_EMAIL in the CUSTOMERS table. From the navigation pane of System i Navigator, right-click the CUSTOMERS table and select Definition , as shown Figure 4-36 Figure 4-36 Definition of the CUSTOMERS table @@ -1490,15 +1490,15 @@ Figure 4-37 Adding a check constraint - 3. The New Check Constraint window opens, as shown in Figure 4-38. Complete the following steps: -- a. Select the CUSTOMER_EMAIL column. -- b. Enter the check constraint condition. In this example, specify CUSTOMER_EMAIL to be different from ****@****, which is the mask value. +- a. Select the CUSTOMER\_EMAIL column. +- b. Enter the check constraint condition. In this example, specify CUSTOMER\_EMAIL to be different from ****@****, which is the mask value. - c. Select the On update violation, preserve column value option and click OK . Figure 4-38 Specifying a new check constraint on the CUSTOMERS table -- 4. Figure 4-39 shows that there is now a check constraint on the CUSTOMERS table that prevents any masked data from being updated to the CUSTOMER_EMAIL column. +- 4. Figure 4-39 shows that there is now a check constraint on the CUSTOMERS table that prevents any masked data from being updated to the CUSTOMER\_EMAIL column. Figure 4-39 Check constraint on the CUSTOMERS table @@ -1536,7 +1536,7 @@ Figure 4-43 Enabling RCAC on TRANSACTIONS This section displays all the row permissions after enabling RCAC. Complete the following steps: -- 1. From System i Navigator, click Row Permissions , as shown in Figure 4-44. Three additional Row Permissions are added (QIBM_DEFAULT*). There is one per each row permission. +- 1. From System i Navigator, click Row Permissions , as shown in Figure 4-44. Three additional Row Permissions are added (QIBM\_DEFAULT*). There is one per each row permission. Figure 4-44 Row permissions after enabling RCAC @@ -1548,9 +1548,9 @@ Figure 4-45 Selecting row permission definition -- 3. A window opens, as shown in Figure 4-46. Take note of the nonsensical search condition (0=1) of the QIBM_DEFAULT row permission. This permission is ORed with all of the others and it ensures that if someone does not meet any of the criteria from the row permission then this condition is tested, and because it is false the access is denied. +- 3. A window opens, as shown in Figure 4-46. Take note of the nonsensical search condition (0=1) of the QIBM\_DEFAULT row permission. This permission is ORed with all of the others and it ensures that if someone does not meet any of the criteria from the row permission then this condition is tested, and because it is false the access is denied. -Figure 4-46 Search condition of the QIBM_DEFAULT row permission +Figure 4-46 Search condition of the QIBM\_DEFAULT row permission @@ -1558,14 +1558,14 @@ Figure 4-46 Search condition of the QIBM_DEFAULT row permission You are now ready to test the RCAC definitions. Run the following SQL statements with each type of user (DBE, SECURITY, TELLER, ADMIN, and WEBUSER): -- GLYPH A SELECT statement that returns the SESSION_USER. +- GLYPH A SELECT statement that returns the SESSION\_USER. - GLYPH A SELECT statement that counts the customers from the CUSTOMER table. There are 90 customers in the CUSTOMER table. -- GLYPH A simple SELECT statement that returns the following output from the CUSTOMERS table ordered by customer_name: -- -c u s t o m e r _ i d -- -customer_name -- -customer_email -- -c u s t o m e r _ t a x _ i d -- -customer_drivers_license_number +- GLYPH A simple SELECT statement that returns the following output from the CUSTOMERS table ordered by customer\_name: +- -c u s t o m e r \_ i d +- -customer\_name +- -customer\_email +- -c u s t o m e r \_ t a x \_ i d +- -customer\_drivers\_license\_number ## Data access for a DBE user with RCAC @@ -1661,9 +1661,9 @@ Figure 4-59 WEBUSER session user -- 2. A global variable (CUSTOMER_LOGIN_ID) is set by the web application and then is used to check the row permissions. Figure 4-60 shows setting the global variable by using the customer login ID. +- 2. A global variable (CUSTOMER\_LOGIN\_ID) is set by the web application and then is used to check the row permissions. Figure 4-60 shows setting the global variable by using the customer login ID. -Figure 4-60 Setting the global variable CUSTOMER_LOGIN_ID +Figure 4-60 Setting the global variable CUSTOMER\_LOGIN\_ID @@ -1789,7 +1789,7 @@ The following list documents some of the query output differences that can occur For a list of the differences and additional details, see the IBM i Memo to Users Version 7.2 , found at: -http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahg/rzahgmtu.htm +http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzahg/rzahgmtu.htm In addition, the performance of a native query with SQE can be different. It is possible that a new index or keyed logical file might need to be created to improve the performance. @@ -1875,23 +1875,23 @@ An example of this situation is shown in Figure 6-1. However, note that aggregat SELECT -CREDIT_CARD_NUMBER, +CREDIT\_CARD\_NUMBER, FROM GROUP BY -CREDIT_CARD_NUMBER +CREDIT\_CARD\_NUMBER ORDER BY -CREDIT_CARD_NUMBER; +CREDIT\_CARD\_NUMBER; ## Without RCAC Masking ## With RCAC Masking -| CREDIT CARD NUMBER _ _ | TOTAL | +| CREDIT CARD NUMBER \_ \_ | TOTAL | |--------------------------|---------------| | 3785 0000 0000 1234 | 233.50 | | 3785 1111 1111 1234 | 105.10 | @@ -1906,7 +1906,7 @@ CREDIT_CARD_NUMBER; Figure 6-1 Timing of column masking -| CREDIT CARD NUMBER _ _ | TOTAL | +| CREDIT CARD NUMBER \_ \_ | TOTAL | |---------------------------|---------------| | **** **** **** 1234 | 233.50 | | **** **** **** 1234 | 105.10 | @@ -2096,13 +2096,13 @@ Figure 6-17 SQL Performance Monitor showing statements and RCAC -When implementing RCAC as part of a comprehensive and pervasive data access control initiative, consider that the database monitoring and analysis tools can collect literal values that are passed as part of SQL statements. These literal values can be viewed as part of the information collected. If any of the literals are based on or are used with masked columns, it is important to review the database engineer's policy for viewing these data elements. For example, supposed that column CUSTOMER_TAX_ID is deemed masked for the database engineer and the CUSTOMER_TAX_ID column is used in a predicate as follows: +When implementing RCAC as part of a comprehensive and pervasive data access control initiative, consider that the database monitoring and analysis tools can collect literal values that are passed as part of SQL statements. These literal values can be viewed as part of the information collected. If any of the literals are based on or are used with masked columns, it is important to review the database engineer's policy for viewing these data elements. For example, supposed that column CUSTOMER\_TAX\_ID is deemed masked for the database engineer and the CUSTOMER\_TAX\_ID column is used in a predicate as follows: -WHERE CUSTOMER_TAX_ID = '123-45-7890' +WHERE CUSTOMER\_TAX\_ID = '123-45-7890' -The literal value of '123-45-7890' is visible to the analyst, effectively exposing sensitive information. If this is not acceptable, you must implement the SYSPROC.SET_COLUMN_ATTRIBUTE procedure. +The literal value of '123-45-7890' is visible to the analyst, effectively exposing sensitive information. If this is not acceptable, you must implement the SYSPROC.SET\_COLUMN\_ATTRIBUTE procedure. -The SET_COLUMN_ATTRIBUTE procedure sets the SECURE attribute for a column so that variable values that are used for the column cannot be seen in the SQL Performance Monitor, SQL Plan Cache Snapshot, or Visual Explain. +The SET\_COLUMN\_ATTRIBUTE procedure sets the SECURE attribute for a column so that variable values that are used for the column cannot be seen in the SQL Performance Monitor, SQL Plan Cache Snapshot, or Visual Explain. ## 6.4.2 Index advisor @@ -2124,7 +2124,7 @@ Figure 6-19 Index advisor based on the RCAC rule For more information about creating and using indexes, see IBM DB2 for i indexing methods and strategies , found at: -http://www.ibm.com/partnerworld/wps/servlet/ContentHandler/stg_ast_sys_wp_db2_i_in dexing_methods_strategies +http://www.ibm.com/partnerworld/wps/servlet/ContentHandler/stg\_ast\_sys\_wp\_db2\_i\_in dexing\_methods\_strategies ## 6.4.3 Metadata using catalogs @@ -2136,44 +2136,44 @@ Figure 6-20 RCAC and catalogs The SYSCONTROLS catalog view contains the following columns: -- GLYPH COLUMN_NAME -- GLYPH CONTROL_TYPE -- GLYPH CREATE_TIME +- GLYPH COLUMN\_NAME +- GLYPH CONTROL\_TYPE +- GLYPH CREATE\_TIME - GLYPH ENABLE - GLYPH ENFORCED -- GLYPH ASP_NUMBER +- GLYPH ASP\_NUMBER - GLYPH IMPLICIT - GLYPH LABEL -- GLYPH LAST_ALTERED -- GLYPH LONG_COMMENT -- GLYPH RCAC_NAME -- GLYPH RCAC_OWNER -- GLYPH RCAC_SCHEMA +- GLYPH LAST\_ALTERED +- GLYPH LONG\_COMMENT +- GLYPH RCAC\_NAME +- GLYPH RCAC\_OWNER +- GLYPH RCAC\_SCHEMA - GLYPH RULETEXT -- GLYPH SYSTEM_COLUMN_NAME -- GLYPH SYSTEM_TABLE_NAME -- GLYPH SYSTEM_TABLE_SCHEMA -- GLYPH TABLE_NAME -- GLYPH TABLE_SCHEMA +- GLYPH SYSTEM\_COLUMN\_NAME +- GLYPH SYSTEM\_TABLE\_NAME +- GLYPH SYSTEM\_TABLE\_SCHEMA +- GLYPH TABLE\_NAME +- GLYPH TABLE\_SCHEMA - GLYPH TBCORRELATION The SYSCONTROLSDEP catalog view contains the following columns: -- GLYPH COLUMN_NAME -- GLYPH CONTROL_TYPE -- GLYPH IASP_NUMBER -- GLYPH OBJECT_NAME -- GLYPH OBJECT_SCHEMA -- GLYPH OBJECT_TYPE -- GLYPH PARM_SIGNATURE -- GLYPH RCAC_NAME -- GLYPH RCAC_SCHEMA -- GLYPH SYSTEM_TABLE_NAME -- GLYPH SYSTEM_TABLE_SCHEMA +- GLYPH COLUMN\_NAME +- GLYPH CONTROL\_TYPE +- GLYPH IASP\_NUMBER +- GLYPH OBJECT\_NAME +- GLYPH OBJECT\_SCHEMA +- GLYPH OBJECT\_TYPE +- GLYPH PARM\_SIGNATURE +- GLYPH RCAC\_NAME +- GLYPH RCAC\_SCHEMA +- GLYPH SYSTEM\_TABLE\_NAME +- GLYPH SYSTEM\_TABLE\_SCHEMA For more information, see the IBM i 7.2 DB2 for i SQL Reference Guide , found at: -http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzintro.htm?lang =en +http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/db2/rbafzintro.htm?lang =en ## 6.5 Views, materialized query tables, and query rewrite with RCAC @@ -2210,30 +2210,30 @@ CREATE SCHEMA Schema1; CREATE TABLE Schema1.employee(userID varchar(128), Locati - 2. Create a row permission that allows the employees to see only rows from the region they work in: ``` -/* Create permission that only allows the employees to see rows from the region they work in */ CREATE PERMISSION Schema1.Sales_PERM1 ON schema1.sales FOR ROWS WHERE CURRENT_USER in (SELECT userId FROM schema1.employee E WHERE e.regionid = regionid) ENFORCED FOR ALL ACCESS ENABLE; +/* Create permission that only allows the employees to see rows from the region they work in */ CREATE PERMISSION Schema1.Sales\_PERM1 ON schema1.sales FOR ROWS WHERE CURRENT\_USER in (SELECT userId FROM schema1.employee E WHERE e.regionid = regionid) ENFORCED FOR ALL ACCESS ENABLE; ``` - 3. Create an MQT to summarize sales by location: --- Create MQT to summarize sales by location -- This has all of the data. The schema1.sales_perm1 predicate was not applied CREATE TABLE Schema1.Location_Sales_MQT as AS (SELECT LocationID, SUM(Saleamt) as Total_Location_Sales FROM SCHEMA1.SALES GROUP BY LOCATIONID) DATA INITIALLY DEFERRED REFRESH DEFERRED MAINTAINED BY USER; +-- Create MQT to summarize sales by location -- This has all of the data. The schema1.sales\_perm1 predicate was not applied CREATE TABLE Schema1.Location\_Sales\_MQT as AS (SELECT LocationID, SUM(Saleamt) as Total\_Location\_Sales FROM SCHEMA1.SALES GROUP BY LOCATIONID) DATA INITIALLY DEFERRED REFRESH DEFERRED MAINTAINED BY USER; - 4. Populate the MQT (permission is not applied): -/* Populate the MQT - Permission not applied here */ REFRESH TABLE Schema1.Location_Sales_MQT +/* Populate the MQT - Permission not applied here */ REFRESH TABLE Schema1.Location\_Sales\_MQT -The following query matches Location_Sales_MQT, but it cannot be used because it does not have column regionid, which is needed by the schema1.sales_PERM1 permission: +The following query matches Location\_Sales\_MQT, but it cannot be used because it does not have column regionid, which is needed by the schema1.sales\_PERM1 permission: SELECT Locationid, sum(SALEAMT) FROM schema1.sales GROUP BY locationid; - 5. Create an MQT to summarize by region and location: --- MQT to summarize by region and location Create table schema1.Region_Location_Sales_MQT as AS (SELECT REGIONID, LocationID, SUM(Saleamt) as Total_Location_Sales FROM SCHEMA1.SALES GROUP BY REGIONID, LOCATIONID) DATA INITIALLY DEFERRED REFRESH DEFERRED MAINTAINED BY USER; +-- MQT to summarize by region and location Create table schema1.Region\_Location\_Sales\_MQT as AS (SELECT REGIONID, LocationID, SUM(Saleamt) as Total\_Location\_Sales FROM SCHEMA1.SALES GROUP BY REGIONID, LOCATIONID) DATA INITIALLY DEFERRED REFRESH DEFERRED MAINTAINED BY USER; -- 6. Populate the Region_location_Sales_MQT (permission not applied): +- 6. Populate the Region\_location\_Sales\_MQT (permission not applied): -/* Populate the Region_location_Sales_MQT - Permission not applied here */ Refresh table schema1.Region_Location_Sales_MQT +/* Populate the Region\_location\_Sales\_MQT - Permission not applied here */ Refresh table schema1.Region\_Location\_Sales\_MQT -The following query can use the Region_location_SALES_MQT because it has REGIONID, which is required for the schema1.sales_PERM1 permission: +The following query can use the Region\_location\_SALES\_MQT because it has REGIONID, which is required for the schema1.sales\_PERM1 permission: SELECT Locationid, sum(SALEAMT) FROM schema1.sales GROUP BY locationid; @@ -2317,11 +2317,11 @@ In the Example 6-4, the mask is defined to return a value of 'XXX-XX-nnnn' for a Example 6-4 Check constraint to avoid masked data ``` -CREATE SCHEMA MY_LIB SET SCHEMA MY_LIB CREATE TABLE MY_LIB.EMP_INFO (COL1_name CHAR(10) WITH DEFAULT 'DEFAULT', COL2_ssn CHAR(11) WITH DEFAULT 'DEFAULT') CREATE MASK MASK_ssn ON MY_LIB.EMP_INFO FOR COLUMN COL2_ssn RETURN CASE WHEN VERIFY_GROUP_FOR_USER ( SESSION_USER , 'DBMGR' ) = 1 THEN COL2_ssn +CREATE SCHEMA MY\_LIB SET SCHEMA MY\_LIB CREATE TABLE MY\_LIB.EMP\_INFO (COL1\_name CHAR(10) WITH DEFAULT 'DEFAULT', COL2\_ssn CHAR(11) WITH DEFAULT 'DEFAULT') CREATE MASK MASK\_ssn ON MY\_LIB.EMP\_INFO FOR COLUMN COL2\_ssn RETURN CASE WHEN VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'DBMGR' ) = 1 THEN COL2\_ssn ``` ``` -ELSE 'XXX-XX-'||SUBSTR(COL2_ssn,8,4) END ENABLE | /* Check constraint for the update and insert.*/ ALTER TABLE MY_LIB.EMP_INFO ADD CONSTRAINT MASK_ssn_preserve CHECK(SUBSTR(COL2_ssn,1,7)<>'XXX-XX-') -- Allow any value other than the mask ON UPDATE VIOLATION PRESERVE COL2_ssn -- Don't update the mask portion of the existing value ON INSERT VIOLATION SET COL2_ssn = DEFAULT -- for insert set this to the default value. +ELSE 'XXX-XX-'||SUBSTR(COL2\_ssn,8,4) END ENABLE | /* Check constraint for the update and insert.*/ ALTER TABLE MY\_LIB.EMP\_INFO ADD CONSTRAINT MASK\_ssn\_preserve CHECK(SUBSTR(COL2\_ssn,1,7)<>'XXX-XX-') -- Allow any value other than the mask ON UPDATE VIOLATION PRESERVE COL2\_ssn -- Don't update the mask portion of the existing value ON INSERT VIOLATION SET COL2\_ssn = DEFAULT -- for insert set this to the default value. ``` ## 6.8.2 Before trigger solution @@ -2331,7 +2331,7 @@ The actions that are described in Example 6-4 on page 108 for ON UPDATE VIOLATIO Example 6-5 Before trigger to avoid masked data ``` -CREATE TRIGGER PREVENT_MASK_SSN BEFORE INSERT OR UPDATE ON MY_LIB.EMP_INFO REFERENCING NEW ROW AS N OLD ROW AS O FOR EACH ROW MODE DB2ROW SECURED WHEN(SUBSTR(N.COL2_ssn,1,7) = 'XXX-XX-') BEGIN IF INSERTING THEN SET N.COL2_ssn = DEFAULT; ELSEIF UPDATING THEN SET N.COL2_ssn = O.COL2_ssn; END IF; END +CREATE TRIGGER PREVENT\_MASK\_SSN BEFORE INSERT OR UPDATE ON MY\_LIB.EMP\_INFO REFERENCING NEW ROW AS N OLD ROW AS O FOR EACH ROW MODE DB2ROW SECURED WHEN(SUBSTR(N.COL2\_ssn,1,7) = 'XXX-XX-') BEGIN IF INSERTING THEN SET N.COL2\_ssn = DEFAULT; ELSEIF UPDATING THEN SET N.COL2\_ssn = O.COL2\_ssn; END IF; END ``` ## 6.9 Triggers and functions (SECURED) @@ -2347,7 +2347,7 @@ Any triggers that are defined on a table must be created with an attribute that Example 6-6 Trigger SECURED ``` -/* Trigger created with the SECURED attribute */ CREATE TRIGGER PREVENT_MASK_SSN BEFORE INSERT OR UPDATE ON MY_LIB.EMP_INFO REFERENCING NEW ROW AS N OLD ROW AS O FOR EACH ROW MODE DB2ROW SECURED WHEN(SUBSTR(N.COL2_ssn,1,7) = 'XXX-XX-') BEGIN IF INSERTING THEN SET N.COL2_ssn = DEFAULT; ELSEIF UPDATING THEN SET N.COL2_ssn = O.COL2_ssn; END IF; END +/* Trigger created with the SECURED attribute */ CREATE TRIGGER PREVENT\_MASK\_SSN BEFORE INSERT OR UPDATE ON MY\_LIB.EMP\_INFO REFERENCING NEW ROW AS N OLD ROW AS O FOR EACH ROW MODE DB2ROW SECURED WHEN(SUBSTR(N.COL2\_ssn,1,7) = 'XXX-XX-') BEGIN IF INSERTING THEN SET N.COL2\_ssn = DEFAULT; ELSEIF UPDATING THEN SET N.COL2\_ssn = O.COL2\_ssn; END IF; END ``` ## 6.9.2 Functions @@ -2355,21 +2355,21 @@ Example 6-6 Trigger SECURED Within a CREATE PERMISSION or CREATE MASK , a function can be called. Because that UDF has access to the data before the RCAC rules are applied, the SECURE attribute is required on that function, as shown in Example 6-7. ``` -Example 6-7 Specifying SECURED on a function CREATE PERMISSION SCHEMA.PERM1 ON SCHEMA.TABLE1 FOR ROWS WHERE MY_UDF(CURRENT_USER,COLUMN1) = 1 ENFORCED FOR ALL ACCESS ENABLE; CREATE FUNCTION MY_UDF (INP1 CHAR(32), INP2 INTEGER) Returns INTEGER LANGUAGE SQL CONTAINS SQL SECURED +Example 6-7 Specifying SECURED on a function CREATE PERMISSION SCHEMA.PERM1 ON SCHEMA.TABLE1 FOR ROWS WHERE MY\_UDF(CURRENT\_USER,COLUMN1) = 1 ENFORCED FOR ALL ACCESS ENABLE; CREATE FUNCTION MY\_UDF (INP1 CHAR(32), INP2 INTEGER) Returns INTEGER LANGUAGE SQL CONTAINS SQL SECURED ``` -The SECURED attribute of MY_UDF signifies that the function is considered secure for RCAC. If a function is called from an SQL statement, and references a column in a table that has RCAC, it must be declared as secure. In that case, if the secure function calls other functions, they are not validated to confirm whether they are secure. +The SECURED attribute of MY\_UDF signifies that the function is considered secure for RCAC. If a function is called from an SQL statement, and references a column in a table that has RCAC, it must be declared as secure. In that case, if the secure function calls other functions, they are not validated to confirm whether they are secure. Consider the following examples: -- GLYPH Table1 has RCAC defined and enabled. SELECT MY_UDF2(Column2) from schema.table1. -- MY_UDF2 must be created with the SECURED attribute. If MY_UDF2 invokes MY_UDF3, there is no checking to ensure that it is also created with SECURED. +- GLYPH Table1 has RCAC defined and enabled. SELECT MY\_UDF2(Column2) from schema.table1. +- MY\_UDF2 must be created with the SECURED attribute. If MY\_UDF2 invokes MY\_UDF3, there is no checking to ensure that it is also created with SECURED. - NOT SECURED is the default on the create function unless SECURED is explicitly selected. This same rule applies for any function that might be invoked with a masked column specified as an argument. - GLYPH Table2 column SSN has a column mask that is defined on it. -- SELECT MY_UDF4(SSN) from table2. Because SSN has a column mask that is defined, MY_UDF4 must be created with the SECURED attribute. +- SELECT MY\_UDF4(SSN) from table2. Because SSN has a column mask that is defined, MY\_UDF4 must be created with the SECURED attribute. ## 6.10 RCAC is only one part of the solution @@ -2431,7 +2431,7 @@ Important: Although these capabilities make it easy to temporarily turn off RCAC DB2 also can regenerate an existing row permission or column mask. This regenerate option can be useful with more complex RCAC definitions that reference other DB2 objects. -For example, consider a row permission on an ACCOUNTS table (PERMISSION1_ON_ACCOUNTS). The ACCOUNTS table row permission references and compares columns in the CUSTOMERS table. When the definition of the CUSTOMERS table changes, DB2 does not check to determine whether the change to the CUSTOMERS table breaks the ACCOUNTS table row permission. If this table definition change does break the row permission, an error does not surface until an application tries to read rows from the ACCOUNTS table. +For example, consider a row permission on an ACCOUNTS table (PERMISSION1\_ON\_ACCOUNTS). The ACCOUNTS table row permission references and compares columns in the CUSTOMERS table. When the definition of the CUSTOMERS table changes, DB2 does not check to determine whether the change to the CUSTOMERS table breaks the ACCOUNTS table row permission. If this table definition change does break the row permission, an error does not surface until an application tries to read rows from the ACCOUNTS table. Instead of waiting for an application to detect this error, the REGENERATE option can be used on the ACCOUNTS row permission. The REGENERATE option returns an error if the change in the CUSTOMERS table definition causes the row permission to be invalid. In this way, the row permission can be proactively corrected before an application discovers the error. @@ -2445,7 +2445,7 @@ Row permissions and column masks are stored in the DB2 table object itself, so t Save and restore processing works fine with RCAC if the RCAC definition does not reference other DB2 objects other than the table over which they are defined. When the RCAC definition has dependencies on other DB2 objects, the restore process is much more challenging. -For example, assume that the BANKSCHEMA library (which is the system name or short name for the schema long name of BANK_SCHEMA) is saved and restored into a library named BANK_TEST. Recall from the example in 7.1.4, "Regenerating" on page 114 that the row permission on the ACCOUNTS table references the CUSTOMERS table (… SELECT C.CUSTOMER_ID FROM CUSTOMERS C …). After the restore operation, the ACCOUNTS row permission still references the CUSTOMERS table in BANK_SCHEMA because DB2 explicitly qualifies all object references when the row permission or column mask is created. The restore processing does not change the explicit qualification from BANK_SCHEMA to BANK_TEST. As a result, the restored ACCOUNTS row permission now depends on DB2 objects residing in a different schema, even though it was not created that way originally. For more details, see Figure 7-1. +For example, assume that the BANKSCHEMA library (which is the system name or short name for the schema long name of BANK\_SCHEMA) is saved and restored into a library named BANK\_TEST. Recall from the example in 7.1.4, "Regenerating" on page 114 that the row permission on the ACCOUNTS table references the CUSTOMERS table (… SELECT C.CUSTOMER\_ID FROM CUSTOMERS C …). After the restore operation, the ACCOUNTS row permission still references the CUSTOMERS table in BANK\_SCHEMA because DB2 explicitly qualifies all object references when the row permission or column mask is created. The restore processing does not change the explicit qualification from BANK\_SCHEMA to BANK\_TEST. As a result, the restored ACCOUNTS row permission now depends on DB2 objects residing in a different schema, even though it was not created that way originally. For more details, see Figure 7-1. Figure 7-1 Restoring tables to different schemas @@ -2470,9 +2470,9 @@ The tasks and operations of security administrators and database engineers who a A new journal entry type of "AX" for journal entry code "T" (audit trail) is now used for RCAC. More information about the journaling of RCAC operations can be found in the following documents: - GLYPH IBM i Version 7.2 Journal Management Guide , found at: -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzaki/rzakiprintthis .htm?lang=en +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzaki/rzakiprintthis .htm?lang=en - GLYPH IBM i Version 7.2 Security Reference Guide , found at: -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzarl/rzarlkickoff.h tm?lang=en +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzarl/rzarlkickoff.h tm?lang=en @@ -2527,25 +2527,25 @@ This appendix provides the database definitions or DDLs to re-create the Row and Example A-1 DDL script to implement the RCAC banking example ``` -/* Database Definitions for RCAC Bank Scenario */ /* Schema */ CREATE SCHEMA BANK_SCHEMA FOR SCHEMA BANKSCHEMA ; /* Global Variable */ CREATE VARIABLE BANK_SCHEMA.CUSTOMER_LOGIN_ID VARCHAR( 30) ; LABEL ON VARIABLE BANK_SCHEMA.CUSTOMER_LOGIN_ID IS 'Customer''s log in value passed by web application' ; /* Tables */ CREATE TABLE BANK_SCHEMA.CUSTOMERS ( CUSTOMER_ID FOR COLUMN CUSTO00001 INTEGER GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE NO ORDER CACHE 20 ), CUSTOMER_NAME FOR COLUMN CUSTO00002 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER_ADDRESS FOR COLUMN CUSTO00003 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER_CITY FOR COLUMN CUSTO00004 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER_STATE FOR COLUMN CUSTO00005 CHAR(2) CCSID 37 NOT NULL , CUSTOMER_PHONE FOR COLUMN CUSTO00006 CHAR(10) CCSID 37 NOT NULL , CUSTOMER_EMAIL FOR COLUMN CUSTO00007 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER_TAX_ID FOR COLUMN CUSTO00008 CHAR(11) CCSID 37 NOT NULL , CUSTOMER_DRIVERS_LICENSE_NUMBER FOR COLUMN CUSTO00012 CHAR(13) CCSID 37 DEFAULT NULL , CUSTOMER_LOGIN_ID FOR COLUMN CUSTO00009 VARCHAR(30) CCSID 37 DEFAULT NULL , CUSTOMER_SECURITY_QUESTION FOR COLUMN CUSTO00010 VARCHAR(100) CCSID 37 DEFAULT NULL , +/* Database Definitions for RCAC Bank Scenario */ /* Schema */ CREATE SCHEMA BANK\_SCHEMA FOR SCHEMA BANKSCHEMA ; /* Global Variable */ CREATE VARIABLE BANK\_SCHEMA.CUSTOMER\_LOGIN\_ID VARCHAR( 30) ; LABEL ON VARIABLE BANK\_SCHEMA.CUSTOMER\_LOGIN\_ID IS 'Customer''s log in value passed by web application' ; /* Tables */ CREATE TABLE BANK\_SCHEMA.CUSTOMERS ( CUSTOMER\_ID FOR COLUMN CUSTO00001 INTEGER GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE NO ORDER CACHE 20 ), CUSTOMER\_NAME FOR COLUMN CUSTO00002 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER\_ADDRESS FOR COLUMN CUSTO00003 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER\_CITY FOR COLUMN CUSTO00004 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER\_STATE FOR COLUMN CUSTO00005 CHAR(2) CCSID 37 NOT NULL , CUSTOMER\_PHONE FOR COLUMN CUSTO00006 CHAR(10) CCSID 37 NOT NULL , CUSTOMER\_EMAIL FOR COLUMN CUSTO00007 VARCHAR(30) CCSID 37 NOT NULL , CUSTOMER\_TAX\_ID FOR COLUMN CUSTO00008 CHAR(11) CCSID 37 NOT NULL , CUSTOMER\_DRIVERS\_LICENSE\_NUMBER FOR COLUMN CUSTO00012 CHAR(13) CCSID 37 DEFAULT NULL , CUSTOMER\_LOGIN\_ID FOR COLUMN CUSTO00009 VARCHAR(30) CCSID 37 DEFAULT NULL , CUSTOMER\_SECURITY\_QUESTION FOR COLUMN CUSTO00010 VARCHAR(100) CCSID 37 DEFAULT NULL , ``` A ``` -CUSTOMER_SECURITY_QUESTION_ANSWER FOR COLUMN CUSTO00011 VARCHAR(100) CCSID 37 DEFAULT NULL , INSERT_TIMESTAMP FOR COLUMN INSER00001 TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP IMPLICITLY HIDDEN , UPDATE_TIMESTAMP FOR COLUMN UPDAT00001 TIMESTAMP GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL IMPLICITLY HIDDEN , CONSTRAINT BANK_SCHEMA.CUSTOMER_ID_PK PRIMARY KEY( CUSTOMER_ID ) ) ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_LOGIN_ID_UK UNIQUE( CUSTOMER_LOGIN_ID ) ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_DRIVERS_LICENSE_CHECK CHECK( CUSTOMER_DRIVERS_LICENSE_NUMBER <> '*************' ) ON UPDATE VIOLATION PRESERVE CUSTOMER_DRIVERS_LICENSE_NUMBER ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_EMAIL_CHECK CHECK( CUSTOMER_EMAIL <> '****@****' ) ON UPDATE VIOLATION PRESERVE CUSTOMER_EMAIL ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_LOGIN_ID_CHECK CHECK( CUSTOMER_LOGIN_ID <> '*****' ) ON INSERT VIOLATION SET CUSTOMER_LOGIN_ID = DEFAULT ON UPDATE VIOLATION PRESERVE CUSTOMER_LOGIN_ID ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_SECURITY_QUESTION_CHECK CHECK( CUSTOMER_SECURITY_QUESTION_ANSWER <> '*****' ) ON INSERT VIOLATION SET CUSTOMER_SECURITY_QUESTION_ANSWER = DEFAULT ON UPDATE VIOLATION PRESERVE CUSTOMER_SECURITY_QUESTION_ANSWER ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_SECURITY_QUESTION_ANSWER CHECK( CUSTOMER_SECURITY_QUESTION <> '*****' ) ON INSERT VIOLATION SET CUSTOMER_SECURITY_QUESTION = DEFAULT ON UPDATE VIOLATION PRESERVE CUSTOMER_SECURITY_QUESTION ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK_SCHEMA.CUSTOMER_TAX_ID_CHECK CHECK( CUSTOMER_TAX_ID <> 'XXX-XX-XXXX' AND SUBSTR ( CUSTOMER_TAX_ID , 1 , 7 ) <> 'XXX-XX-' ) ON UPDATE VIOLATION PRESERVE CUSTOMER_TAX_ID ; CREATE TABLE BANK_SCHEMA.ACCOUNTS ( ACCOUNT_ID INTEGER GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE NO ORDER CACHE 20 ), CUSTOMER_ID FOR COLUMN CUSTID INTEGER NOT NULL , ACCOUNT_NUMBER FOR COLUMN ACCOUNTNO VARCHAR(50) CCSID 37 NOT NULL , ACCOUNT_NAME FOR COLUMN ACCOUNTNAM CHAR(12) CCSID 37 NOT NULL , ACCOUNT_DATE_OPENED FOR COLUMN OPENDATE DATE DEFAULT CURRENT_DATE , ACCOUNT_DATE_CLOSED FOR COLUMN CLOSEDATE DATE DEFAULT NULL , ACCOUNT_CURRENT_BALANCE FOR COLUMN ACCTBAL DECIMAL(11, 2) NOT NULL DEFAULT 0 , INSERT_TIMESTAMP FOR COLUMN INSDATE TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP IMPLICITLY HIDDEN , UPDATE_TIMESTAMP FOR COLUMN UPDDATE TIMESTAMP GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL IMPLICITLY HIDDEN , CONSTRAINT BANK_SCHEMA.ACCOUNT_ID_PK PRIMARY KEY( ACCOUNT_ID ) ); +CUSTOMER\_SECURITY\_QUESTION\_ANSWER FOR COLUMN CUSTO00011 VARCHAR(100) CCSID 37 DEFAULT NULL , INSERT\_TIMESTAMP FOR COLUMN INSER00001 TIMESTAMP NOT NULL DEFAULT CURRENT\_TIMESTAMP IMPLICITLY HIDDEN , UPDATE\_TIMESTAMP FOR COLUMN UPDAT00001 TIMESTAMP GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL IMPLICITLY HIDDEN , CONSTRAINT BANK\_SCHEMA.CUSTOMER\_ID\_PK PRIMARY KEY( CUSTOMER\_ID ) ) ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_LOGIN\_ID\_UK UNIQUE( CUSTOMER\_LOGIN\_ID ) ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_DRIVERS\_LICENSE\_CHECK CHECK( CUSTOMER\_DRIVERS\_LICENSE\_NUMBER <> '*************' ) ON UPDATE VIOLATION PRESERVE CUSTOMER\_DRIVERS\_LICENSE\_NUMBER ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_EMAIL\_CHECK CHECK( CUSTOMER\_EMAIL <> '****@****' ) ON UPDATE VIOLATION PRESERVE CUSTOMER\_EMAIL ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_LOGIN\_ID\_CHECK CHECK( CUSTOMER\_LOGIN\_ID <> '*****' ) ON INSERT VIOLATION SET CUSTOMER\_LOGIN\_ID = DEFAULT ON UPDATE VIOLATION PRESERVE CUSTOMER\_LOGIN\_ID ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_SECURITY\_QUESTION\_CHECK CHECK( CUSTOMER\_SECURITY\_QUESTION\_ANSWER <> '*****' ) ON INSERT VIOLATION SET CUSTOMER\_SECURITY\_QUESTION\_ANSWER = DEFAULT ON UPDATE VIOLATION PRESERVE CUSTOMER\_SECURITY\_QUESTION\_ANSWER ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_SECURITY\_QUESTION\_ANSWER CHECK( CUSTOMER\_SECURITY\_QUESTION <> '*****' ) ON INSERT VIOLATION SET CUSTOMER\_SECURITY\_QUESTION = DEFAULT ON UPDATE VIOLATION PRESERVE CUSTOMER\_SECURITY\_QUESTION ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ADD CONSTRAINT BANK\_SCHEMA.CUSTOMER\_TAX\_ID\_CHECK CHECK( CUSTOMER\_TAX\_ID <> 'XXX-XX-XXXX' AND SUBSTR ( CUSTOMER\_TAX\_ID , 1 , 7 ) <> 'XXX-XX-' ) ON UPDATE VIOLATION PRESERVE CUSTOMER\_TAX\_ID ; CREATE TABLE BANK\_SCHEMA.ACCOUNTS ( ACCOUNT\_ID INTEGER GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE NO ORDER CACHE 20 ), CUSTOMER\_ID FOR COLUMN CUSTID INTEGER NOT NULL , ACCOUNT\_NUMBER FOR COLUMN ACCOUNTNO VARCHAR(50) CCSID 37 NOT NULL , ACCOUNT\_NAME FOR COLUMN ACCOUNTNAM CHAR(12) CCSID 37 NOT NULL , ACCOUNT\_DATE\_OPENED FOR COLUMN OPENDATE DATE DEFAULT CURRENT\_DATE , ACCOUNT\_DATE\_CLOSED FOR COLUMN CLOSEDATE DATE DEFAULT NULL , ACCOUNT\_CURRENT\_BALANCE FOR COLUMN ACCTBAL DECIMAL(11, 2) NOT NULL DEFAULT 0 , INSERT\_TIMESTAMP FOR COLUMN INSDATE TIMESTAMP NOT NULL DEFAULT CURRENT\_TIMESTAMP IMPLICITLY HIDDEN , UPDATE\_TIMESTAMP FOR COLUMN UPDDATE TIMESTAMP GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL IMPLICITLY HIDDEN , CONSTRAINT BANK\_SCHEMA.ACCOUNT\_ID\_PK PRIMARY KEY( ACCOUNT\_ID ) ); ``` ``` -ALTER TABLE BANK_SCHEMA.ACCOUNTS ADD CONSTRAINT BANK_SCHEMA.ACCOUNT_CUSTOMER_ID_FK FOREIGN KEY( CUSTOMER_ID ) REFERENCES BANK_SCHEMA.CUSTOMERS ( CUSTO00001 ) ON DELETE RESTRICT ON UPDATE RESTRICT ; ALTER TABLE BANK_SCHEMA.ACCOUNTS ADD CONSTRAINT BANK_SCHEMA.ACCOUNT_NUMBER_CHECK CHECK( ACCOUNT_NUMBER <> '*****' ) ON UPDATE VIOLATION PRESERVE ACCOUNT_NUMBER ; CREATE TABLE BANK_SCHEMA.TRANSACTIONS FOR SYSTEM NAME TRANS ( TRANSACTION_ID FOR COLUMN TRANS00001 INTEGER GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE NO ORDER CACHE 20 ), ACCOUNT_ID INTEGER NOT NULL , TRANSACTION_TYPE FOR COLUMN TRANS00002 CHAR(1) CCSID 37 NOT NULL , TRANSACTION_DATE FOR COLUMN TRANS00003 DATE NOT NULL DEFAULT CURRENT_DATE , TRANSACTION_TIME FOR COLUMN TRANS00004 TIME NOT NULL DEFAULT CURRENT_TIME , TRANSACTION_AMOUNT FOR COLUMN TRANS00005 DECIMAL(11, 2) NOT NULL , INSERT_TIMESTAMP FOR COLUMN INSER00001 TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP IMPLICITLY HIDDEN , UPDATE_TIMESTAMP FOR COLUMN UPDAT00001 TIMESTAMP GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL IMPLICITLY HIDDEN , CONSTRAINT BANK_SCHEMA.TRANSACTION_ID_PK PRIMARY KEY( TRANSACTION_ID ) ) ; ALTER TABLE BANK_SCHEMA.TRANSACTIONS ADD CONSTRAINT BANK_SCHEMA.TRANSACTIONS_ACCOUNT_ID_FK FOREIGN KEY( ACCOUNT_ID ) REFERENCES BANK_SCHEMA.ACCOUNTS ( ACCOUNT_ID ) ON DELETE RESTRICT ON UPDATE RESTRICT ; /* Permissions and Masks */ CREATE PERMISSION BANK_SCHEMA.PERMISSION1_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR ROWS WHERE ( QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'DBE' , 'ADMIN' , 'TELLER' ) = 1 ) OR ( QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 AND ( C . CUSTOMER_LOGIN_ID = BANK_SCHEMA . CUSTOMER_LOGIN_ID ) ) ENFORCED FOR ALL ACCESS ENABLE ; CREATE MASK BANK_SCHEMA.MASK_EMAIL_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER_EMAIL RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER_EMAIL WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER_EMAIL ELSE '****@****' END ENABLE ; CREATE MASK BANK_SCHEMA.MASK_TAX_ID_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER_TAX_ID RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 +ALTER TABLE BANK\_SCHEMA.ACCOUNTS ADD CONSTRAINT BANK\_SCHEMA.ACCOUNT\_CUSTOMER\_ID\_FK FOREIGN KEY( CUSTOMER\_ID ) REFERENCES BANK\_SCHEMA.CUSTOMERS ( CUSTO00001 ) ON DELETE RESTRICT ON UPDATE RESTRICT ; ALTER TABLE BANK\_SCHEMA.ACCOUNTS ADD CONSTRAINT BANK\_SCHEMA.ACCOUNT\_NUMBER\_CHECK CHECK( ACCOUNT\_NUMBER <> '*****' ) ON UPDATE VIOLATION PRESERVE ACCOUNT\_NUMBER ; CREATE TABLE BANK\_SCHEMA.TRANSACTIONS FOR SYSTEM NAME TRANS ( TRANSACTION\_ID FOR COLUMN TRANS00001 INTEGER GENERATED ALWAYS AS IDENTITY ( START WITH 1 INCREMENT BY 1 NO MINVALUE NO MAXVALUE NO CYCLE NO ORDER CACHE 20 ), ACCOUNT\_ID INTEGER NOT NULL , TRANSACTION\_TYPE FOR COLUMN TRANS00002 CHAR(1) CCSID 37 NOT NULL , TRANSACTION\_DATE FOR COLUMN TRANS00003 DATE NOT NULL DEFAULT CURRENT\_DATE , TRANSACTION\_TIME FOR COLUMN TRANS00004 TIME NOT NULL DEFAULT CURRENT\_TIME , TRANSACTION\_AMOUNT FOR COLUMN TRANS00005 DECIMAL(11, 2) NOT NULL , INSERT\_TIMESTAMP FOR COLUMN INSER00001 TIMESTAMP NOT NULL DEFAULT CURRENT\_TIMESTAMP IMPLICITLY HIDDEN , UPDATE\_TIMESTAMP FOR COLUMN UPDAT00001 TIMESTAMP GENERATED ALWAYS FOR EACH ROW ON UPDATE AS ROW CHANGE TIMESTAMP NOT NULL IMPLICITLY HIDDEN , CONSTRAINT BANK\_SCHEMA.TRANSACTION\_ID\_PK PRIMARY KEY( TRANSACTION\_ID ) ) ; ALTER TABLE BANK\_SCHEMA.TRANSACTIONS ADD CONSTRAINT BANK\_SCHEMA.TRANSACTIONS\_ACCOUNT\_ID\_FK FOREIGN KEY( ACCOUNT\_ID ) REFERENCES BANK\_SCHEMA.ACCOUNTS ( ACCOUNT\_ID ) ON DELETE RESTRICT ON UPDATE RESTRICT ; /* Permissions and Masks */ CREATE PERMISSION BANK\_SCHEMA.PERMISSION1\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR ROWS WHERE ( QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'DBE' , 'ADMIN' , 'TELLER' ) = 1 ) OR ( QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 AND ( C . CUSTOMER\_LOGIN\_ID = BANK\_SCHEMA . CUSTOMER\_LOGIN\_ID ) ) ENFORCED FOR ALL ACCESS ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_EMAIL\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER\_EMAIL RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER\_EMAIL WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER\_EMAIL ELSE '****@****' END ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_TAX\_ID\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER\_TAX\_ID RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 ``` ``` -THEN C . CUSTOMER_TAX_ID WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'TELLER' ) = 1 THEN ( 'XXX-XX-' CONCAT QSYS2 . SUBSTR ( C . CUSTOMER_TAX_ID , 8 , 4 ) ) WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER_TAX_ID ELSE 'XXX-XX-XXXX' END ENABLE ; CREATE MASK BANK_SCHEMA.MASK_DRIVERS_LICENSE_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER_DRIVERS_LICENSE_NUMBER RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER_DRIVERS_LICENSE_NUMBER WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'TELLER' ) = 1 THEN C . CUSTOMER_DRIVERS_LICENSE_NUMBER WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER_DRIVERS_LICENSE_NUMBER ELSE '*************' END ENABLE ; CREATE MASK BANK_SCHEMA.MASK_LOGIN_ID_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER_LOGIN_ID RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER_LOGIN_ID WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER_LOGIN_ID ELSE '*****' END ENABLE ; CREATE MASK BANK_SCHEMA.MASK_SECURITY_QUESTION_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER_SECURITY_QUESTION RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER_SECURITY_QUESTION WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER_SECURITY_QUESTION ELSE '*****' END ENABLE ; CREATE MASK BANK_SCHEMA.MASK_SECURITY_QUESTION_ANSWER_ON_CUSTOMERS ON BANK_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER_SECURITY_QUESTION_ANSWER RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER_SECURITY_QUESTION_ANSWER WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER_SECURITY_QUESTION_ANSWER ELSE '*****' END ENABLE ; ALTER TABLE BANK_SCHEMA.CUSTOMERS ACTIVATE ROW ACCESS CONTROL ACTIVATE COLUMN ACCESS CONTROL ; +THEN C . CUSTOMER\_TAX\_ID WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'TELLER' ) = 1 THEN ( 'XXX-XX-' CONCAT QSYS2 . SUBSTR ( C . CUSTOMER\_TAX\_ID , 8 , 4 ) ) WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER\_TAX\_ID ELSE 'XXX-XX-XXXX' END ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_DRIVERS\_LICENSE\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER\_DRIVERS\_LICENSE\_NUMBER RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER\_DRIVERS\_LICENSE\_NUMBER WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'TELLER' ) = 1 THEN C . CUSTOMER\_DRIVERS\_LICENSE\_NUMBER WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER\_DRIVERS\_LICENSE\_NUMBER ELSE '*************' END ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_LOGIN\_ID\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER\_LOGIN\_ID RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER\_LOGIN\_ID WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER\_LOGIN\_ID ELSE '*****' END ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_SECURITY\_QUESTION\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER\_SECURITY\_QUESTION RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER\_SECURITY\_QUESTION WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER\_SECURITY\_QUESTION ELSE '*****' END ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_SECURITY\_QUESTION\_ANSWER\_ON\_CUSTOMERS ON BANK\_SCHEMA.CUSTOMERS AS C FOR COLUMN CUSTOMER\_SECURITY\_QUESTION\_ANSWER RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 THEN C . CUSTOMER\_SECURITY\_QUESTION\_ANSWER WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN C . CUSTOMER\_SECURITY\_QUESTION\_ANSWER ELSE '*****' END ENABLE ; ALTER TABLE BANK\_SCHEMA.CUSTOMERS ACTIVATE ROW ACCESS CONTROL ACTIVATE COLUMN ACCESS CONTROL ; ``` ``` -CREATE PERMISSION BANK_SCHEMA.PERMISSION1_ON_ACCOUNTS ON BANK_SCHEMA.ACCOUNTS AS A FOR ROWS WHERE ( QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'DBE' , 'ADMIN' , 'TELLER' ) = 1 ) OR ( QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 AND ( A . CUSTOMER_ID IN ( SELECT C . CUSTOMER_ID FROM BANK_SCHEMA . CUSTOMERS C WHERE C . CUSTOMER_LOGIN_ID = BANK_SCHEMA . CUSTOMER_LOGIN_ID ENFORCED FOR ALL ACCESS ENABLE ; CREATE MASK BANK_SCHEMA.MASK_ACCOUNT_NUMBER_ON_ACCOUNTS ON BANK_SCHEMA.ACCOUNTS AS A FOR COLUMN ACCOUNT_NUMBER RETURN CASE WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'ADMIN' ) = 1 THEN A . ACCOUNT_NUMBER WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'TELLER' ) = 1 THEN A . ACCOUNT_NUMBER WHEN QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 THEN A . ACCOUNT_NUMBER ELSE '*****' END ENABLE ; ALTER TABLE BANK_SCHEMA.ACCOUNTS ACTIVATE ROW ACCESS CONTROL ACTIVATE COLUMN ACCESS CONTROL ; CREATE PERMISSION BANK_SCHEMA.PERMISSION1_ON_TRANSACTIONS ON BANK_SCHEMA.TRANSACTIONS AS T FOR ROWS WHERE ( QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'DBE' , 'ADMIN' , 'TELLER' ) = 1 ) OR ( QSYS2 . VERIFY_GROUP_FOR_USER ( SESSION_USER , 'CUSTOMER' ) = 1 AND ( T . ACCOUNT_ID IN ( SELECT A . ACCOUNT_ID FROM BANK_SCHEMA . ACCOUNTS A WHERE A . CUSTOMER_ID IN ( SELECT C . CUSTOMER_ID FROM BANK_SCHEMA . CUSTOMERS C WHERE C . CUSTOMER_LOGIN_ID = BANK_SCHEMA . CUSTOMER_LOGIN_ID ENFORCED FOR ALL ACCESS ENABLE ; ALTER TABLE BANK_SCHEMA.TRANSACTIONS ACTIVATE ROW ACCESS CONTROL ; /* END */ +CREATE PERMISSION BANK\_SCHEMA.PERMISSION1\_ON\_ACCOUNTS ON BANK\_SCHEMA.ACCOUNTS AS A FOR ROWS WHERE ( QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'DBE' , 'ADMIN' , 'TELLER' ) = 1 ) OR ( QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 AND ( A . CUSTOMER\_ID IN ( SELECT C . CUSTOMER\_ID FROM BANK\_SCHEMA . CUSTOMERS C WHERE C . CUSTOMER\_LOGIN\_ID = BANK\_SCHEMA . CUSTOMER\_LOGIN\_ID ENFORCED FOR ALL ACCESS ENABLE ; CREATE MASK BANK\_SCHEMA.MASK\_ACCOUNT\_NUMBER\_ON\_ACCOUNTS ON BANK\_SCHEMA.ACCOUNTS AS A FOR COLUMN ACCOUNT\_NUMBER RETURN CASE WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'ADMIN' ) = 1 THEN A . ACCOUNT\_NUMBER WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'TELLER' ) = 1 THEN A . ACCOUNT\_NUMBER WHEN QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 THEN A . ACCOUNT\_NUMBER ELSE '*****' END ENABLE ; ALTER TABLE BANK\_SCHEMA.ACCOUNTS ACTIVATE ROW ACCESS CONTROL ACTIVATE COLUMN ACCESS CONTROL ; CREATE PERMISSION BANK\_SCHEMA.PERMISSION1\_ON\_TRANSACTIONS ON BANK\_SCHEMA.TRANSACTIONS AS T FOR ROWS WHERE ( QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'DBE' , 'ADMIN' , 'TELLER' ) = 1 ) OR ( QSYS2 . VERIFY\_GROUP\_FOR\_USER ( SESSION\_USER , 'CUSTOMER' ) = 1 AND ( T . ACCOUNT\_ID IN ( SELECT A . ACCOUNT\_ID FROM BANK\_SCHEMA . ACCOUNTS A WHERE A . CUSTOMER\_ID IN ( SELECT C . CUSTOMER\_ID FROM BANK\_SCHEMA . CUSTOMERS C WHERE C . CUSTOMER\_LOGIN\_ID = BANK\_SCHEMA . CUSTOMER\_LOGIN\_ID ENFORCED FOR ALL ACCESS ENABLE ; ALTER TABLE BANK\_SCHEMA.TRANSACTIONS ACTIVATE ROW ACCESS CONTROL ; /* END */ ``` ## Related publications @@ -2557,22 +2557,22 @@ The publications that are listed in this section are considered suitable for a m These publications are relevant as further information sources: - GLYPH IBM DB2 for i indexing methods and strategies white paper: -- http://www.ibm.com/partnerworld/wps/servlet/ContentHandler/stg_ast_sys_wp_db2_i _indexing_methods_strategies +- http://www.ibm.com/partnerworld/wps/servlet/ContentHandler/stg\_ast\_sys\_wp\_db2\_i \_indexing\_methods\_strategies - GLYPH IBM i Memo to Users Version 7.2 : -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahg/rzahgmtu.htm +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzahg/rzahgmtu.htm - GLYPH IBM i Version 7.2 DB2 for i SQL Reference Guide : -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/db2/rbafzintro.htm?l ang=en +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/db2/rbafzintro.htm?l ang=en - GLYPH IBM i Version 7.2 Journal Management Guide : -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzaki/rzakiprintthis .htm?lang=en +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzaki/rzakiprintthis .htm?lang=en - GLYPH IBM i Version 7.2 Security Reference Guide : -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzarl/rzarlkickoff.h tm?lang=en +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzarl/rzarlkickoff.h tm?lang=en ## Online resources These websites are relevant as further information sources: - GLYPH Database programming topic of the IBM i 7.2 IBM Knowledge Center: -- http://www-01.ibm.com/support/knowledgecenter/ssw_ibm_i_72/rzahg/rzahgdbp.htm?l ang=en +- http://www-01.ibm.com/support/knowledgecenter/ssw\_ibm\_i\_72/rzahg/rzahgdbp.htm?l ang=en - GLYPH Identity Theft Resource Center - http://www.idtheftcenter.org - GLYPH Ponemon Institute