6.2 Triple-and-Values Security

The triple-and-values security option extends the label security support provided by the triple-level security option to include security for lexical values as well.

This security option provides a thin layer of RDF-specific capabilities on top of the Oracle AI Database native support for label security. It provides not only the capabilities supported by the triple-level option but also supports the additional capability of applying label security to the internal (values) table used for mapping RDF lexical values to unique numeric identifiers.

To use triple-and-values security, specify SEM_RDFSA.TRIPLE_AND_VALUES as the rdfsa_options parameter value when you execute the SEM_RDFSA.APPLY_OLS_POLICY procedure. For example:

EXECUTE sem_rdfsa.apply_ols_policy('defense', SEM_RDFSA.TRIPLE_AND_VALUES,
        network_owner=>‘RDFOWNR’, network_name=>'OLS_NET');

Since the methods for associating label security to the RDF triples are the same as described in the section on Triple-Level Security, we outline below only the methods relevant for associating label security to the lexical values for the (subject, predicate, object, and optionally, named graph) components of an RDF triple.

Note that the labels attached to the individual components of a triple can be independent from one another and from the label attached to the triple. Thus, a single triple can have up to five distinct associated labels – up to four labels for the individual components of the triple and one for the triple itself.

With triple-and-values security, duplicate lexical values with different labels can be inserted in the values table. For example, assume that you already have a lexical value, say <urn:Y>, stored with a very sensitive label (TOPSECRET). Now, a low-privileged (UNCLASSIFIED) user is inserting the following triple:

<urn:X> <urn:P> <urn:Y> .

Since the already-stored, but TOPSECRET, <urn:Y> lexical value is not visible to the low-privileged user, <urn:Y> gets stored again, this time with the UNCLASSIFIED label. A subsequent query posed by a high-privileged user, with TOPSECRET label, will see both instances of the <urn:Y> lexical value, thereby returning duplicate result rows caused by presence of the two instances of the <urn:Y> lexical value. The following are some of the ways for mitigating the issues related to duplicate lexical values:

  • Preventing or minimizing the occurrence of duplicate lexical values: When inserting triples, associate the lowest label with lexical values that are expected to be used by users with different privilege levels.
  • Eliminating duplicate result rows from query results: Use of DISTINCT in the SELECT clause of queries can eliminate the duplicate results. Note that this will eliminate all duplicate result rows including those that may not have been caused by presence of duplicate lexical values.

6.2.1 Extended Example: Applying OLS Triple-and-Values Security on RDF Data

This section presents an extended example illustrating how to apply triple-and-values Oracle Label Security (OLS) to RDF data. The examples are very simplified, and do not reflect recommended practices regarding user names and passwords.

  1. Create the policy and related administrative users.

    The code example in this step performs the following actions in the same or slightly different order:

    1. Enable OLS in the database.
    2. Connect as SYSDBA to create security admin users: fgac_admin and defense_admin.
    3. Grant the LBAC_DBA role to fgac_admin to allow the creation of a new OLS DEFENSE policy which creates the DEFENSE_DBA role.
    4. Grant the DEFENSE_DBA role to defense_admin enabling users to set up the labels for the DEFENSE policy.
    5. Assign the appropriate labels to the intended users of the database objects to which the DEFENSE policy may be applied.
    SQL> conn sys/<password_for_sys> as sysdba
    Connected.
    
    SQL> -- enable OLS in the database
    SQL> exec LBACSYS.configure_ols;
    
    PL/SQL procedure successfully completed.
    
    SQL> exec LBACSYS.OLS_ENFORCEMENT.enable_ols;
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> -- create user for security admin, grant LBAC_DBA role
    SQL> create user fgac_admin identified by <password_for_fgac_admin>;
    
    User created.
    
    SQL> grant connect, unlimited tablespace to fgac_admin;
    
    Grant succeeded.
    
    SQL> grant LBAC_DBA to fgac_admin;
    
    Grant succeeded.
    
    SQL> grant execute on sa_sysdba to fgac_admin;
    
    Grant succeeded.
    
    SQL> 
    SQL> conn fgac_admin/<password_for_fgac_admin>
    Connected.
    SQL> -- create policy DEFENSE, which creates the DEFENSE_DBA role
    SQL> EXECUTE SA_SYSDBA.CREATE_POLICY('DEFENSE','def_label');
    
    PL/SQL procedure successfully completed.
    
    SQL> select column_name from lbacsys.all_sa_policies where policy_name='DEFENSE';
    
    COLUMN_NAME
    ---------------
    DEF_LABEL
    
    1 row selected.
    
    SQL> 
    SQL> conn sys/<password_for_sys>
    Connected.
    SQL> -- create user for policy admin of the DEFENSE policy
    SQL> -- create policy admin user for the DEFENSE policy (created above)
    SQL> create user defense_admin identified by <password_for_defense_admin>;
    
    User created.
    
    SQL> grant connect, unlimited tablespace to defense_admin;
    
    Grant succeeded.
    
    SQL> grant DEFENSE_DBA to defense_admin;
    
    Grant succeeded.
    
    SQL> grant execute on sa_components to defense_admin;
    
    Grant succeeded.
    
    SQL> grant execute on sa_user_admin to defense_admin;
    
    Grant succeeded.
    
    SQL> grant execute on sa_label_admin to defense_admin;
    
    Grant succeeded.
    
    SQL> grant execute on sa_policy_admin to defense_admin;
    
    Grant succeeded.
    
    SQL> 
    SQL> conn defense_admin/<password_for_defense_admin>
    Connected.
    SQL> 
    SQL> BEGIN
      2    ------- create levels -------
      3    SA_COMPONENTS.CREATE_LEVEL('defense',3000,'TS','TOP SECRET');
      4    SA_COMPONENTS.CREATE_LEVEL('defense',2000,'SE','SECRET');
      5    SA_COMPONENTS.CREATE_LEVEL('defense',1000,'UN','UNCLASSIFIED');
      6  
      7    ------ create labels (using the components defined above) -------
      8    SA_LABEL_ADMIN.CREATE_LABEL('defense',1000,'UN');
      9    SA_LABEL_ADMIN.CREATE_LABEL('defense',1500,'SE');
     10    SA_LABEL_ADMIN.CREATE_LABEL('defense',3100,'TS');
     11  
     12    ------ assign default labels to users -------
     13    SA_USER_ADMIN.SET_USER_LABELS('defense', 'RDFOWNR', 'SE');
     14    SA_USER_ADMIN.SET_USER_LABELS('defense', 'A', 'UN');
     15    SA_USER_ADMIN.SET_USER_LABELS('defense', 'B', 'SE');
     16    SA_USER_ADMIN.SET_USER_LABELS('defense', 'C', 'TS');
     17    SA_USER_ADMIN.SET_USER_LABELS('defense', 'Q', 'SE');
     18  END;
     19  /
    
    PL/SQL procedure successfully completed.
  2. Create an RDF network, RDF graph, and share it with users in various modes.

    The code example in this step performs the following actions in the same or slightly different order:

    1. Connect as SYSDBA to create the rdfownr user and several other users such as a, b, c, and q.
    2. Connect as rdfownr to create the NET1 RDF network.
    3. Granting shared access on the RDF network to a, b, c, and q (query-only) users.
    4. Grant SA_ONLY access on the RDF network to the defense_admin user enabling them to perform OLS security related operations on the network only. This does not grant access to the data stored in the RDF network.
    5. Connect as defense_admin and apply the DEFENSE OLS policy (TRIPLE_AND_VALUES) on the RDF network.
    6. Connect as rdfownr to create an RDF graph named PERSON and grant DML and query access on the graph to users a, b, c, and query-only access to user q.
    SQL> conn sys/<password_for_sys>
    Connected.
    
    SQL> -- create user that can be owner of RDF networks
    SQL> create user rdfownr identified by <password_for_rdfownr>;
    
    User created.
    
    SQL> grant CREATE JOB, connect, resource, unlimited tablespace to rdfownr;
    
    Grant succeeded.
    
    SQL> 
    SQL> --- create general users
    SQL> create user a identified by <password_for_a>;
    
    User created.
    
    SQL> grant connect, unlimited tablespace to a;
    
    Grant succeeded.
    
    SQL> create user b identified by <password_for_b>;
    
    User created.
    
    SQL> grant connect, unlimited tablespace to b;
    
    Grant succeeded.
    
    SQL> create user c identified by <password_for_c>;
    
    User created.
    
    SQL> grant connect, unlimited tablespace to c;
    
    Grant succeeded.
    
    SQL> create user q identified by <password_for_q>;
    
    User created.
    
    SQL> grant connect, unlimited tablespace to q;
    
    Grant succeeded.
    
    SQL> 
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> 
    SQL> -- create an RDF network and enable it for sharing with other users
    SQL> exec sem_apis.create_rdf_network('<tablespace_name>',null,network_owner=>'rdfownr',network_name=>'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> exec sem_apis.enable_network_sharing(network_owner=>'rdfownr',network_name=>'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> -- grant network access to a few users: full-access (they can create their own graphs) or query-only access
    SQL> EXECUTE sem_apis.enable_network_sharing('RDFOWNR', 'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> BEGIN
      2    sem_apis.grant_network_access_privs('RDFOWNR', 'NET1', 'A');
      3    sem_apis.grant_network_access_privs('RDFOWNR', 'NET1', 'B');
      4    sem_apis.grant_network_access_privs('RDFOWNR', 'NET1', 'C');
      5    sem_apis.grant_network_access_privs('RDFOWNR', 'NET1', 'Q', options=>' QUERY_ONLY=T ');
      6  END;
      7  /
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> -- share in SA_ONLY=T mode with policy admin user (note: this sharing mode provides no visibility to RDF data)
    SQL> exec sem_apis.grant_network_access_privs('rdfownr', 'NET1', 'DEFENSE_ADMIN', options=>' SA_ONLY=T ');
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> -- tables in RDF network are not visible to security and policy admin users (EXCEPT when managing security policies on them)
    SQL> conn fgac_admin/<password_for_fgac_admin>
    Connected.
    SQL> select table_name from SYS.all_tables where table_name LIKE 'NET1#RDF%' order by 1;
    
    no rows selected
    
    SQL> conn defense_admin/<password_for_defense_admin>
    Connected.
    SQL> select table_name from SYS.all_tables where table_name LIKE 'NET1#RDF%' order by 1;
    
    no rows selected
    
    SQL> 
    SQL> -- APPLY_OLS_POLICY on the RDF network
    SQL> conn defense_admin/<password_for_defense_admin>
    Connected.
    SQL> exec sem_rdfsa.apply_ols_policy('DEFENSE', sem_rdfsa.TRIPLE_AND_VALUES,network_owner=>'rdfownr',network_name=>'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> -- after APPLY_OLS_POLICY: a new column gets added to RDF_VALUE$ table and to RDF_LINK$ table
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> select table_name, column_name, data_type from sys.user_tab_columns where table_name IN ('NET1#RDF_VALUE$', 'NET1#RDF_LINK$') and column_name='DEF_LABEL' order by 1;
    
    TABLE_NAME                     COLUMN_NAME     DATA_TYPE
    ------------------------------ --------------- ----------
    NET1#RDF_LINK$                 DEF_LABEL       NUMBER
    NET1#RDF_VALUE$                DEF_LABEL       NUMBER
    
    2 rows selected.
    
    SQL> 
    SQL> -- disable the OLS policy and set the label for all the pre-existing values to the lowest label
    SQL> conn defense_admin/<password_for_defense_admin>
    Connected.
    SQL> exec sem_rdfsa.disable_ols_policy(network_owner=>'rdfownr',network_name=>'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> -- re-enable the OLS policy and verify that the values are visible to all (EXCEPT security and policy admin users)
    SQL> conn defense_admin/<password_for_defense_admin>
    Connected.
    SQL> exec sem_rdfsa.enable_ols_policy(network_owner=>'rdfownr',network_name=>'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> -- create an RDF graph (owned by rdfownr)
    SQL> exec sem_apis.create_rdf_graph('PERSON',null,null,network_owner=>'rdfownr',network_name=>'NET1');
    
    PL/SQL procedure successfully completed.
    
    SQL> -- the corr. RDFT view, used as target for DML operations for this graph, has the extra label column
    SQL> desc NET1#RDFT_PERSON
     Name                                                                                                              Null?    Type
     ----------------------------------------------------------------------------------------------------------------- -------- ----------------------------------------------------------------------------
     TRIPLE                                                                                                                     MDSYS.SDO_RDF_TRIPLE_S
     DEF_LABEL                                                                                                                  NUMBER
    
    SQL> -- the corr. RDFM view, used as source for query processing for this graph, has the extra column
    SQL> desc NET1#RDFM_PERSON
     Name                                                                                                              Null?    Type
     ----------------------------------------------------------------------------------------------------------------- -------- ----------------------------------------------------------------------------
     P_VALUE_ID                                                                                                        NOT NULL NUMBER
     START_NODE_ID                                                                                                              NUMBER
     CANON_END_NODE_ID                                                                                                          NUMBER
     END_NODE_ID                                                                                                                NUMBER
     MODEL_ID                                                                                                                   NUMBER
     COST                                                                                                                       NUMBER
     DEF_LABEL                                                                                                                  NUMBER
     CTXT2                                                                                                                      VARCHAR2(4000)
     DISTANCE                                                                                                                   NUMBER
     EXPLAIN                                                                                                                    VARCHAR2(4000)
     PATH                                                                                                                       VARCHAR2(4000)
     G_ID                                                                                                                       NUMBER
     LINK_ID                                                                                                                    VARCHAR2(89)
    
    SQL> 
    SQL> -- share access to the above graph with users who already have shared access to network (Note: user Q is given query-only access)
    SQL> BEGIN
      2    sem_apis.grant_model_access_privs('PERSON','a',sys.odcivarchar2list('INSERT','UPDATE','DELETE','SELECT','QUERY'),network_owner=>'rdfownr',network_name=>'NET1');
      3    sem_apis.grant_model_access_privs('PERSON','b',sys.odcivarchar2list('INSERT','UPDATE','DELETE','SELECT','QUERY'),network_owner=>'rdfownr',network_name=>'NET1');
      4    sem_apis.grant_model_access_privs('PERSON','c',sys.odcivarchar2list('INSERT','UPDATE','DELETE','SELECT','QUERY'),network_owner=>'rdfownr',network_name=>'NET1');
      5    sem_apis.grant_model_access_privs('PERSON','q',sys.odcivarchar2list('QUERY'),network_owner=>'rdfownr',network_name=>'NET1');
      6  END;
      7  /
    
    PL/SQL procedure successfully completed.
  3. Set up utility functions and views used for illustration purposes.

    The following code creates the utility views, TRIPLES_VIEW and VALUES_VIEW. These return a simplified view of the triples and values, along with the relevant OLS labels, that are visible to the user. If a value is not found for a given id, it returns the id itself but prefixed with an asterisk.

    SQL> -- set up for reporting visibility of triples and values
    SQL> column visible_to_users format a30
    SQL> column subj format a22
    SQL> column pred format a22
    SQL> column obj format a22
    SQL> column labels format a30
    SQL> --
    SQL> create or replace function shortval (val_id number) return varchar2 as
      2    shval varchar2(100);
      3  begin
      4    select NVL(vname_suffix, vname_prefix) into shval from rdfownr.net1#rdf_value$ where value_id = val_id and rownum < 2;
      5    return substr(shval,1,20);
      6  exception
      7    when no_data_found then
      8      return '*' || val_id;
      9  end;
     10  /
    Function created.
    
    SQL> grant execute on shortval to public;
    Grant succeeded.
    
    SQL> create or replace view triples_view as
      2  select rdfownr.shortval(t.triple.rdf_s_id) subj, rdfownr.shortval(t.triple.rdf_p_id) pred, rdfownr.shortval(t.triple.rdf_c_id) obj, def_label
      3  from rdfownr.NET1#RDFT_PERSON t;
    View created.
     
    SQL> grant read on triples_view to public;
    Grant succeeded.
    
    SQL> create or replace view values_view as
      2  select value_id, value_name
      3  from rdfownr.net1#rdf_value$
      4  where (value_name NOT LIKE '%rdf%' and value_name NOT LIKE '%owl%' and canon_id is NULL);
    View created.
    
    SQL> grant read on values_view to public;
    Grant succeeded.
  4. Set Fine Grained Access Control (FGAC) when using SQL insert into RDF graphs.

    Note that the label assigned to a user determines what labels can be associated with the data inserted by the user. It could be the label assigned to the user or any label that is dominated by the user's label. The assigned label also determines what data from such a table is visible to the user.

    SQL> --
    SQL> -- INSERT using SQL Insert statement
    SQL> --
    SQL> 
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> 
    SQL> -- 1) use default label, which is 'SE::' or 1500 for rdfownr, for the triple as well as the values
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple) values (sdo_rdf_triple_s('person',
      2  '<urn:john>','<urn:spouseOf>','<urn:mary>',
      3  'rdfownr','NET1'));
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL> 
    SQL> -- check labels for the visible triples and values
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    john                   spouseOf               mary                   1500
    
    1 row selected.
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    urn:john                                                          1500
    urn:mary                                                          1500
    urn:spouseOf                                                      1500
    
    3 rows selected.
    
    SQL> 
    SQL> conn c/<password_for_c>
    Connected.
    SQL> -- 2) use default explicit label 'UN::' (1000) for the triple
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple, def_label) values (sdo_rdf_triple_s('person',
      2  '<urn:john>','<urn:secretId>','"jasmin#*!@"',
      3  sys.odcinumberlist(char_to_label('DEFENSE', 'UN::'), char_to_label('DEFENSE', 'UN::'), char_to_label('DEFENSE', 'TS::')),
      4  'rdfownr','NET1'), char_to_label('DEFENSE', 'UN::'));
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL> 
    SQL> -- check labels for the visible triples and values
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    john                   secretId               jasmin#*!@             1000
    john                   spouseOf               mary                   1500
    
    2 rows selected.
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    jasmin#*!@                                                        3100
    urn:john                                                          1500
    urn:mary                                                          1500
    urn:secretId                                                      1000
    urn:spouseOf                                                      1500
    
    5 rows selected.
    
    SQL> 
    SQL> conn a/<password_for_a>1
    Connected.
    SQL> -- 3) use default label, which is 'UN::' or 1000 for user A, for both the triple as well as the values
    SQL> -- this triple already exists, but with a higher label (1500) and hence not visible to the current user: so, a duplicate triple gets inserted
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple) values (sdo_rdf_triple_s('person',
      2  '<urn:john>','<urn:spouseOf>','<urn:mary>',
      3  'rdfownr','NET1'));
    
    1 row created.
    
    SQL> 
    SQL> -- 4) use default label ('UN::' or 1000) for the triple and all its values
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple) values (sdo_rdf_triple_s('person',
      2  '<urn:childOf>','owl:inverseOf','<urn:parentOf>',
      3  'rdfownr','NET1'));
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL> 
    SQL> -- check labels for the visible triples and values
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000
    
    3 rows selected.
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    urn:childOf                                                       1000
    urn:john                                                          1000
    urn:mary                                                          1000
    urn:parentOf                                                      1000
    urn:secretId                                                      1000
    urn:spouseOf                                                      1000
    
    6 rows selected.
    
    SQL> 
    SQL> conn b/<password_for_b>
    Connected.
    SQL> -- 5) use default label ('SE::' or 1500) for triple, but lowest label ('UN' or 1000) for values
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple) values (sdo_rdf_triple_s('person',
      2  '<urn:spouseOf>','rdf:type','owl:SymmetricProperty',
      3  sys.odcinumberlist(char_to_label('DEFENSE', 'UN::'), char_to_label('DEFENSE', 'UN::'), char_to_label('DEFENSE', 'UN::')),
      4  'rdfownr','NET1'));
    
    1 row created.
    
    SQL> 
    SQL> -- check labels for the visible triples and values
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    4 rows selected..
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    urn:childOf                                                       1000
    urn:john                                                          1000, 1500
    urn:mary                                                          1000, 1500
    urn:parentOf                                                      1000
    urn:secretId                                                      1000
    urn:spouseOf                                                      1000, 1500
    
    6 rows selected.
    
    SQL> 
    SQL> conn c/<password_for_c>
    Connected.
    SQL> -- 6) use default label ('TS::' or 3100) for the triple
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple) values (sdo_rdf_triple_s('person',
      2  '<urn:john>','<urn:childOf>','<urn:bob>',
      3  'rdfownr','NET1'));
    
    1 row created.
    
    SQL> COMMIT;
    
    Commit complete.
    
    SQL> 
    SQL> -- check labels for the visible triples and values
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   childOf                bob                    3100
    john                   secretId               jasmin#*!@             1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    5 rows selected.
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    jasmin#*!@                                                        3100
    urn:bob                                                           3100
    urn:childOf                                                       1000
    urn:john                                                          1000, 1500
    urn:mary                                                          1000, 1500
    urn:parentOf                                                      1000
    urn:secretId                                                      1000
    urn:spouseOf                                                      1000, 1500
    
    8 rows selected.
  5. Perform label-based inference by iterating repeatedly over the given sequence of labels.

    The following code shows label-based inference with the label sequence <1000, 1500, 3100>. A derived triple is marked with the earliest (minimum) label without which it cannot be derived. For example, the derived triple bob parentOf john will have the 3100 ('TS::') label because it cannot be derived with any label below 3100 as john childOf bob is only visible when user has label of at least 3100.

    SQL> conn sys/<password_for_sys> as sysdba
    Connected.
    
    SQL> -- allow "exchange partition" operations used during inference
    SQL> grant EXEMPT ACCESS POLICY on schema rdfownr to rdfownr;
    Grant succeeded.
    
    SQL> conn c/<password_for_c>
    Connected.
    
    SQL> BEGIN
      2   sem_apis.create_inferred_graph('inf',
      3    sem_models('PERSON'),
      4    sem_rulebases('OWL2RL'),
      5    SEM_APIS.REACH_CLOSURE,
      6    null,
      7    null,
      8    ols_ladder_inf_lbl_seq=>'1000 1500 3100',
      9    network_owner=>'RDFOWNR',
     10    network_name=>'NET1'
     11  );
     12  END;
     13  /
    
    PL/SQL procedure successfully completed.
    
    SQL> conn sys/<password_for_sys> as sysdba
    Connected.
    
    SQL> -- "exchange partition" operations not needed any more
    SQL> revoke EXEMPT ACCESS POLICY on schema rdfownr from rdfownr;
    Revoke succeeded.
    
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    
    SQL> create or replace view inf_triples_view as
      2  select rdfownr.shortval(t.start_node_id) subj, rdfownr.shortval(t.p_value_id) pred, rdfownr.shortval(canon_end_node_id) obj, def_label
      3  from rdfownr.NET1#RDFI_INF t;
    View created.
    
    SQL> grant read on inf_triples_view to public;
    Grant succeeded.
    
    SQL> conn c/<password_for_c>
    Connected.
    
    SQL> -- check labels for the visible triples and values
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.inf_triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    bob                    parentOf               john                   3100
    mary                   spouseOf               john                   1500
    parentOf               inverseOf              childOf                1000
    spouseOf               inverseOf              spouseOf               1500
    
    4 rows selected.
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    1                                                                 3100
    jasmin#*!@                                                        3100
    true                                                              3100
    urn:bob                                                           3100
    urn:childOf                                                       1000
    urn:john                                                          1000, 1500
    urn:mary                                                          1000, 1500
    urn:parentOf                                                      1000
    urn:secretId                                                      1000
    urn:spouseOf                                                      1000, 1500
    
    10 rows selected
    
    SQL> 
    SQL> -- visibility note: users with lower label may sometimes be able to see a triple but not the higher-labeled values used in that triple
    SQL> conn a/<password_for_a>
    Connected.
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000
    
    3 rows selected.
    
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.inf_triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    parentOf               inverseOf              childOf                1000
    
    1 row selected.
    
    SQL> conn b/<password_for_b>
    Connected.
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    4 rows selected.
    
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.inf_triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    mary                   spouseOf               john                   1500
    parentOf               inverseOf              childOf                1000
    spouseOf               inverseOf              spouseOf               1500
    
    3 rows selected.
    
    SQL> conn c/<password_for_c>
    Connected.
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   childOf                bob                    3100
    john                   secretId               jasmin#*!@             1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    5 rows selected.
    
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.inf_triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    bob                    parentOf               john                   3100
    mary                   spouseOf               john                   1500
    parentOf               inverseOf              childOf                1000
    spouseOf               inverseOf              spouseOf               1500
    
    4 rows selected.
    
    SQL> conn q/<password_for_q>
    Connected.
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    4 rows selected.
    
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.inf_triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    mary                   spouseOf               john                   1500
    parentOf               inverseOf              childOf                1000
    spouseOf               inverseOf              spouseOf               1500
    
    3 rows selected.
    
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    4 rows selected.
    
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.inf_triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    mary                   spouseOf               john                   1500
    parentOf               inverseOf              childOf                1000
    spouseOf               inverseOf              spouseOf               1500
    
    3 rows selected.
  6. Define and use label functions.

    The following code shows bulkload-time use of a label function that associates a higher label ('SE::') with a triple when the predicate is <urn:salary> and a lower label ('UN::') otherwise.

    SQL> -- use of label functions and its use during bulk-load 
    SQL> conn sys/<password_for_sys>
    Connected.
    SQL> grant execute on to_lbac_data_label to rdfownr;
    
    Grant succeeded.
    
    SQL> 
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> 
    SQL> CREATE TABLE stage_table (
      2                       RDF$STC_sub varchar2(4000) not null,
      3                       RDF$STC_pred varchar2(4000) not null,
      4                       RDF$STC_obj varchar2(4000) not null
      5  );
    
    Table created.
    
    SQL> 
    SQL> insert into stage_table values ('<urn:john>','<urn:lastName>','"Smith"');
    
    1 row created.
    
    SQL> insert into stage_table values ('<urn:john>','<urn:salary>','"100K"');
    
    1 row created.
    
    SQL> commit;
    
    Commit complete.
    
    SQL> 
    SQL> -- define a label function for generating label for a given triple
    SQL> -- label function for generating labels for triples (in RDF_LINK$)
    SQL> CREATE OR REPLACE FUNCTION gen_triple_label_on_pid (p_value_id number) Return LBACSYS.LBAC_LABEL
      2  as
      3    i_label varchar2(80);
      4    vty varchar2(10);
      5    vnm varchar2(100);
      6  BEGIN
      7    select value_type, value_name into vty, vnm from rdfownr.net1#rdf_value$ where value_id=p_value_id;
      8    if (vty = 'UR' and vnm = 'urn:salary') then
      9      i_label := 'SE::';
     10    else
     11      i_label := 'UN::';
     12    end if;
     13    RETURN TO_LBAC_DATA_LABEL('DEFENSE',i_label);
     14  END;
     15  /
    
    Function created.
    
    SQL> 
    SQL> grant execute on gen_triple_label_on_pid to LBAC_TRIGGER;
    
    Grant succeeded.
    
    SQL> 
    SQL> -- ALTER_OLS_TABLE_POLICY to include use of label function for triples
    SQL> conn defense_admin/<password_for_defense_admin>
    Connected.
    SQL> exec sem_rdfsa.alter_ols_table_policy('triple', 'label_function', 'rdfownr.gen_triple_label_on_pid(:new.p_value_id)', network_owner=>'rdfownr',network_name=>'net1');
    
    PL/SQL procedure successfully completed.
    
    SQL> select * from lbacsys.all_sa_table_policies where schema_name='RDFOWNR' order by policy_name, table_name;
    
    POLICY_NAM SCHEMA_NAME                                                                                                                      TABLE_NAME                     STATUS
    ---------- -------------------------------------------------------------------------------------------------------------------------------- ------------------------------ --------
    TABLE_OPTIONS
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    FUNCTION
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    PREDICATE
    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    DEFENSE    RDFOWNR                                                                                                                          NET1#RDF_LINK$                 ENABLED
    READ_CONTROL, INSERT_CONTROL, UPDATE_CONTROL, DELETE_CONTROL, LABEL_DEFAULT, LABEL_UPDATE, CHECK_CONTROL
    rdfownr.gen_triple_label_on_pid(:new.p_value_id)
    
    DEFENSE    RDFOWNR                                                                                                                          NET1#RDF_VALUE$                ENABLED
    READ_CONTROL, INSERT_CONTROL, UPDATE_CONTROL, DELETE_CONTROL, LABEL_DEFAULT, LABEL_UPDATE, CHECK_CONTROL
    
    2 rows selected.
    
    SQL> 
    SQL> -- bulk-load uses designated label function to mark the salary triple as 'SE::' (1500) while marking the "lastName" triples as 'UN::' (1000)
    SQL> conn rdfownr/<password_for_rdfownr>
    Connected.
    SQL> exec sem_apis.bulk_load_from_staging_table('person','rdfownr','stage_table',network_owner=>'rdfownr',network_name=>'net1');
    
    PL/SQL procedure successfully completed.
    
    SQL> 
    SQL> select subj, pred, obj, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.triples_view group by subj, pred, obj order by 1,2,3;
    
    SUBJ                   PRED                   OBJ                    LABELS
    ---------------------- ---------------------- ---------------------- ------------------------------
    childOf                inverseOf              parentOf               1000
    john                   lastName               Smith                  1000
    john                   salary                 100K                   1500
    john                   secretId               *7305832552150903811   1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    6 rows selected.
    
    SQL> select value_name, listagg(def_label,', ') within group (order by def_label) labels from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME                                                        LABELS
    ----------------------------------------------------------------- ------------------------------
    100K                                                              1500
    Smith                                                             1500
    urn:childOf                                                       1000
    urn:john                                                          1000, 1500
    urn:lastName                                                      1500
    urn:mary                                                          1000, 1500
    urn:parentOf                                                      1000
    urn:salary                                                        1500
    urn:secretId                                                      1000
    urn:spouseOf                                                      1000, 1500
    
    10 rows selected