6.1 Triple-Level Security

The triple-level security option provides a thin layer of RDF-specific capabilities on top of Oracle AI Database native support for label security.

To use triple-level security, specify SEM_RDFSA.TRIPLE_LEVEL_ONLY 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_LEVEL_ONLY, network_owner=>'RDFOWNR', network_name=>'OLS_NET');

Ensure, you do not specify any of the other available parameters for the SEM_RDFSA.APPLY_OLS_POLICY procedure.

When you use triple-level security, OLS is applied to each RDF graph in the network. That is, label security is applied to the relevant internal tables.

With triple-level security, duplicate triples with different labels can be inserted in the RDF graph. For example, assume that you have a triple with a very sensitive label, such as:

(<urn:X>,<urn:P>,<urn:Y>, "TOPSECRET")

This does not prevent a low-privileged (UNCLASSIFIED) user from inserting the triple (<urn:X>,<urn:P>,<urn:Y>, "UNCLASSIFIED"). Because SPARQL and SEM_MATCH do not return label information, a query will return both rows (assuming the user has appropriate privileges), and it will not be easy to distinguish between the TOPSECRET and UNCLASSIFIED triples.

To filter out such low-security triples when querying the RDF graphs, you can use one or more of the following options with SEM_MATCH:

  • POLICY_NAME specifies the OLS policy name.

  • MIN_LABEL specifies the minimum label for triples that are included in the query.

In other words, every triple that contains a label that is strictly dominated by MIN_LABEL is not included in the query. For example, to filter out the UNCLASSIFIED triple, you could use the following query (assuming the OLS policy name is DEFENSE and that the query user has read privileges over UNCLASSIFIED and TOPSECRET triples):

SELECT s,p,y FROM table(sem_match('{?s ?p ?y}' , 
  sem_models(TEST'), null, null, null, null, 
  'MIN_LABEL=TOPSECRET POLICY_NAME=DEFENSE',
  null, null, ‘FGAC_ADMIN’, 'OLS_NET'));

Note that the filtering in the preceding example occurs in addition to the security checks performed by the native OLS software.

After a triple has been inserted, you can view and update the label information through the CTXT1 column in the application table for the RDF graph (assuming that you have the WRITEUP and WRITEDOWN privileges to modify the labels).

There are no restrictions on who can perform inference or bulk loading with triple-level security; all of the inferred or bulk loaded triples are inserted with the user's session row label. Note that you can change the session labels by using the SA_UTL package. (For more information about SA_UTL, see Oracle Label Security Administrator's Guide.)

6.1.1 Fine-Grained Security for Inferred Data and Ladder-Based Inference (LBI)

When triple-level security is turned on for RDF data stored in Oracle AI Database, asserted facts are tagged with data labels to enforce mandatory access control. In addition, when a user invokes the forward-chaining based inference function through the SEM_APIS.CREATE_INFERRED_GRAPH procedure, the newly inferred relationships will be tagged with the current row label (SA_UTL.NUMERIC_ROW_LABEL).

These newly inferred relationships are derived solely based on the information that the user is allowed to access. These relationships do, however, share the same data label. This is understandable because a SEM_APIS.CREATE_INFERRED_GRAPH call can be viewed as a three-step process: read operation, followed by a logical inference computation, followed by a write operation. The read operation gathers information upon which inference computation is based, and it is restricted by access privileges, the user's label, and the data labels; the logical inference computation step is purely mathematical; and the final write of inferred information into the entailed graph is no different from the same user asserting some new facts (which happen to be calculated by the previous step).

Having all inferred assertions tagged with a single label is sufficient if a user only owns a single label. It is, however, not fine-grained enough when there are multiple labels owned by the same user, which is a common situation in a multitenancy setup.

For example, assume a user sets its user label and data label as TopSecret, invokes SEM_APIS.CREATE_INFERRED_GRAPH, switches to a weaker label named Secret, and finally performs a SPARQL query. The query will not be able to see any of those newly inferred relationships because they were all tagged with the TopSecret label. However, if the user switches back to the TopSecret label, now every single inferred relationship is visible. It is "all or nothing" (that is, all visible or nothing visible) as far as inferred relationships are concerned.

When multiple labels are available for use by a given user, you normally want to assign different labels to different inferred relationships. There are two ways to achieve this goal:

Ladder-based inference, effective with Oracle Database 12c Release 1 (12.1), is probably the simpler and more convenient of the two approaches.

Invoking SEM_APIS.CREATE_INFERRED_GRAPH Multiple Times

Assume a security policy named DEFENSE, a user named SCOTT, and a sequence of user labels Label1, Label2, ..., Labeln owned by SCOTT. The following call by SCOTT sets the label as Label1, runs the inference for the first time, and tags the newly inferred triples with Label1:

EXECUTE sa_utl.set_label('defense',char_to_label('defense','Label1'));
EXECUTE sa_utl.set_row_label('defense',char_to_label('defense','Label1'));
EXECUTE sem_apis.create_inferred_graph('inf', sem_models('contracts'), sem_rulebases('owlprime'), SEM_APIS.REACH_CLOSURE, null,'',network_owner=>'RDFOWNR',network_name=>'OLS_NET');

Now, SCOTT switches the label to Label2, runs the inference a second time, and tags the newly inferred triples with Label2. Obviously, if Label2 is dominated by Label1, then no new triples will be inferred because Label2 cannot see anything beyond what Label1 is allowed to see. If Label2 is not dominated by Label1, the read step of the inference process will probably see a different set of triples, and consequently the inference call can produce some new triples, which will in turn be tagged with Label2.

For the purpose of this example, assume the following condition holds true: for any 1 <= i < j <= n, Labelj is not dominated by Labeli.

EXECUTE sa_utl.set_label('defense',char_to_label('defense','Label2'));
EXECUTE sa_utl.set_row_label('defense',char_to_label('defense','Label2'));
EXECUTE sem_apis.create_inferred_graph('inf', sem_models('contracts'), sem_rulebases('owlprime'), SEM_APIS.REACH_CLOSURE, null, 'ENTAIL_ANYWAY=T', network_owner=>'RDFOWNR', network_name=>'OLS_NET');

SCOTT continues the preceding actions using the rest of the labels in the label sequence: Label1, Label2, ..., Labeln. The last step will be as follows:

EXECUTE sa_utl.set_label('defense',char_to_label('defense','Labeln'));
EXECUTE sa_utl.set_row_label('defense',char_to_label('defense','Labeln'));
EXECUTE sem_apis.create_inferred_graph('inf', sem_models('contracts'), sem_rulebases('owlprime'), SEM_APIS.REACH_CLOSURE, null, 'ENTAIL_ANYWAY=T', network_owner=>'RDFOWNR', network_name=>'OLS_NET');

After all these actions are performed, the inference graph probably consists of triples tagged with various different labels.

Using Ladder-Based Inference (LBI)

Basically, ladder-based inference (LBI) wraps in one API call all the actions described in the Invoking SEM_APIS.CREATE_INFERRED_GRAPH Multiple Times approach. Visually, those actions are like climbing up a ladder. When proceeding from one label to the next, more asserted facts become visible or accessible (assuming the new label is not dominated by any of the previous ones), and therefore new relationships can be inferred.

The syntax to invoke LBI is shown in the following example.

EXECUTE sem_apis.create_inferred_graph('inf',
  sem_models('contracts'),
  sem_rulebases('owlprime'),
  SEM_APIS.REACH_CLOSURE,
  null,
  null,
  ols_ladder_inf_lbl_seq=>'numericLabel1 numericLabel2 numericLabel3 numericLabel4',
  network_owner=>'RDFOWNR',
  network_name=>'OLS_NET'
);

The parameter ols_ladder_inf_lbl_seq specifies a sequence of labels. This sequence is provided as a list of numeric labels delimited by spaces. When using LBI, it is a good practice to arrange the sequence of labels so that weaker labels are put before stronger labels. This will reduce the size of the inferred graph. (If labels do not dominate each other, they can be specified in any order.)

6.1.2 Extended Example: Applying OLS Triple-Level Security on RDF Data

This section presents an extended example illustrating how to apply triple-level 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_LEVEL_ONLY) 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_LEVEL_ONLY,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
    
    1 row 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> 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 from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    urn:john
    urn:mary
    urn:spouseOf
    
    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  '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 from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    jasmin#*!@
    urn:john
    urn:mary
    urn:secretId
    urn:spouseOf
    
    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 the triple
    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
    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               jasmin#*!@             1000
    john                   spouseOf               mary                   1000
    
    3 rows selected.
    
    SQL> select value_name from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    jasmin#*!@
    urn:childOf
    urn:john
    urn:mary
    urn:parentOf
    urn:secretId
    urn:spouseOf
    
    7 rows selected.
    
    SQL> 
    SQL> conn b/<password_for_b>
    Connected.
    SQL> -- 5) use default label ('SE::' or 1500) for triple
    SQL> INSERT INTO rdfownr.NET1#RDFT_PERSON(triple) values (sdo_rdf_triple_s('person',
      2  '<urn:spouseOf>','rdf:type','owl:SymmetricProperty',
      3  '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               jasmin#*!@             1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    4 rows selected.
    
    SQL> select value_name from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    jasmin#*!@
    urn:childOf
    urn:john
    urn:mary
    urn:parentOf
    urn:secretId
    urn:spouseOf
    
    7 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 from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    jasmin#*!@
    urn:bob
    urn:childOf
    urn:john
    urn:mary
    urn:parentOf
    urn:secretId
    urn:spouseOf
    
    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 from rdfownr.values_view order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    1
    jasmin#*!@
    true
    urn:bob
    urn:childOf
    urn:john
    urn:mary
    urn:parentOf
    urn:secretId
    urn:spouseOf
    
    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               jasmin#*!@             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               jasmin#*!@             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               jasmin#*!@             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               jasmin#*!@             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)
    
    
    
    1 row 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               jasmin#*!@             1000
    john                   spouseOf               mary                   1000, 1500
    spouseOf               type                   SymmetricProperty      1500
    
    6 rows selected.
    
    SQL> select value_name from rdfownr.values_view group by value_name order by 1;
    
    VALUE_NAME
    -----------------------------------------------------------------
    1
    100K
    Smith
    jasmin#*!@
    true
    urn:bob
    urn:childOf
    urn:john
    urn:lastName
    urn:mary
    urn:parentOf
    urn:salary
    urn:secretId
    urn:spouseOf
    
    14 rows selected.