4.2.4 Store the Database Password in a Keystore
PGX requires a database account to read data from the database into memory. The account should be a low-privilege account (see Security Best Practices with Graph Data).
As described in Reading Graphs from Oracle Database into the Graph Server (PGX), you can read data from the database into the graph server without specifying additional authentication as long as the token is valid for that database user. But if you want to access a graph from a different user, you can do so, as long as that user's password is stored in a Java Keystore file for protection.
You can use the keytool
command that is bundled together with the JDK to generate such a keystore file on the command line. See the following script as an example:
# Add a password for the 'database1' connection
keytool -importpass -alias database1 -keystore keystore.p12
# 1. Enter the password for the keystore
# 2. Enter the password for the database
# Add another password (for the 'database2' connection)
keytool -importpass -alias database2 -keystore keystore.p12
# List what's in the keystore using the keytool
keytool -list -keystore keystore.p12
If you are using Java version 8 or lower, you should pass the additional parameter -storetype pkcs12
to the keytool commands in the preceding example.
You can store more than one password into a single keystore file. Each password can be referenced using the alias name provided.
- Either, Write the PGX graph configuration file to load from the property graph schema
- Or, Write the PGX graph configuration file to load a graph directly from relational tables
- Read the data
- Secure coding tips for graph client applications
Either, Write the PGX graph configuration file to load from the property graph schema
Next write a PGX graph configuration file in JSON format. The file tells PGX where to load the data from, how the data looks like and the keystore alias to use. The following example shows a graph configuration to read data stored in the Oracle property graph format.
{
"format": "pg",
"db_engine": "rdbms",
"name": "hr",
"jdbc_url": "jdbc:oracle:thin:@myhost:1521/orcl",
"username": "hr",
"keystore_alias": "database1",
"vertex_props": [{
"name": "COUNTRY_NAME",
"type": "string"
}, {
"name": "DEPARTMENT_NAME",
"type": "string"
}, {
"name": "SALARY",
"type": "double"
}],
"partition_while_loading": "by_label",
"loading": {
"load_vertex_labels": true,
"load_edge_label": true
}
}
(For the full list of available configuration fields, including their meanings and default values, see Graph Configuration Options.)
Or, Write the PGX graph configuration file to load a graph directly from relational tables
The following example loads a subset of the HR sample data from relational tables directly into PGX as a graph. The configuration file specifies a mapping from relational to graph format by using the concept of vertex and edge providers.
Note:
Specifying the vertex_providers
and edge_providers
properties loads the data into an optimized representation of the graph.
{
"name":"hr",
"jdbc_url":"jdbc:oracle:thin:@myhost:1521/orcl",
"username":"hr",
"keystore_alias":"database1",
"vertex_id_strategy": "no_ids",
"vertex_providers":[
{
"name":"Employees",
"format":"rdbms",
"database_table_name":"EMPLOYEES",
"key_column":"EMPLOYEE_ID",
"key_type": "string",
"props":[
{
"name":"FIRST_NAME",
"type":"string"
},
{
"name":"LAST_NAME",
"type":"string"
},
{
"name":"EMAIL",
"type":"string"
},
{
"name":"SALARY",
"type":"long"
}
]
},
{
"name":"Jobs",
"format":"rdbms",
"database_table_name":"JOBS",
"key_column":"JOB_ID",
"key_type": "string",
"props":[
{
"name":"JOB_TITLE",
"type":"string"
}
]
},
{
"name":"Departments",
"format":"rdbms",
"database_table_name":"DEPARTMENTS",
"key_column":"DEPARTMENT_ID",
"key_type": "string",
"props":[
{
"name":"DEPARTMENT_NAME",
"type":"string"
}
]
}
],
"edge_providers":[
{
"name":"WorksFor",
"format":"rdbms",
"database_table_name":"EMPLOYEES",
"key_column":"EMPLOYEE_ID",
"source_column":"EMPLOYEE_ID",
"destination_column":"EMPLOYEE_ID",
"source_vertex_provider":"Employees",
"destination_vertex_provider":"Employees"
},
{
"name":"WorksAs",
"format":"rdbms",
"database_table_name":"EMPLOYEES",
"key_column":"EMPLOYEE_ID",
"source_column":"EMPLOYEE_ID",
"destination_column":"JOB_ID",
"source_vertex_provider":"Employees",
"destination_vertex_provider":"Jobs"
},
{
"name":"WorkedAt",
"format":"rdbms",
"database_table_name":"JOB_HISTORY",
"key_column":"EMPLOYEE_ID",
"source_column":"EMPLOYEE_ID",
"destination_column":"DEPARTMENT_ID",
"source_vertex_provider":"Employees",
"destination_vertex_provider":"Departments",
"props":[
{
"name":"START_DATE",
"type":"local_date"
},
{
"name":"END_DATE",
"type":"local_date"
}
]
}
]
}
Read the data
Now you can instruct PGX to connect to the database and read the data by passing in both the keystore and the configuration file to PGX, using one of the following approaches:
- Interactively in the graph shell
If you are using the graph shell, start it with the
--secret_store
option. It will prompt you for the keystore password and then attach the keystore to your current session. For example:cd /opt/oracle/graph ./bin/opg4j --secret_store /etc/my-secrets/keystore.p12 enter password for keystore /etc/my-secrets/keystore.p12:
Inside the shell, you can then use normal PGX APIs to read the graph into memory by passing the JSON file you just wrote into the
readGraphWithProperties
API:opg4j> var graph = session.readGraphWithProperties("config.json") graph ==> PgxGraph[name=hr,N=215,E=415,created=1576882388130]
- As a PGX preloaded graphAs a server administrator, you can instruct PGX to load graphs into memory upon server startup. To do so, modify the PGX configuration file at
/etc/oracle/graph/pgx.conf
and add the path the graph configuration file to thepreload_graphs
section. For example:{ ... "preload_graphs": [{ "name": "hr", "path": "/path/to/config.json" }], "authorization": [{ "pgx_role": "GRAPH_DEVELOPER", "pgx_permissions": [{ "preloaded_graph": "hr", "grant": "read" }] }, .... ] }
As root user, edit the service file at/etc/systemd/system/pgx.service
and change theExecStart
command to specify the location of the keystore containing the password:ExecStart=/bin/bash start-server --secret-store /etc/keystore.p12
Note:
Please note that/etc/keystore.p12
must not be password protected for this to work. Instead protect the file via file system permission that is only readable byoraclegraph
user.After the file is edited, reload the changes using:sudo systemctl daemon-reload
Finally start the server:sudo systemctl start pgx
- In a Java application
To register a keystore in a Java application, use the
registerKeystore()
API on thePgxSession
object. For example:import oracle.pgx.api.*; class Main { public static void main(String[] args) throws Exception { String baseUrl = args[0]; String keystorePath = "/etc/my-secrets/keystore.p12"; char[] keystorePassword = args[1].toCharArray(); String graphConfigPath = args[2]; ServerInstance instance = Pgx.getInstance(baseUrl); try (PgxSession session = instance.createSession("my-session")) { session.registerKeystore(keystorePath, keystorePassword); PgxGraph graph = session.readGraphWithProperties(graphConfigPath); System.out.println("N = " + graph.getNumVertices() + " E = " + graph.getNumEdges()); } } }
You can compile and run the preceding sample program using the Oracle Graph Client package. For example:cd $GRAPH_CLIENT // create Main.java with above contents javac -cp 'lib/*' Main.java java -cp '.:conf:lib/*' Main http://myhost:7007 MyKeystorePassword path/to/config.json
Secure coding tips for graph client applications
When writing graph client applications, make sure to never store any passwords or other secrets in clear text in any files or in any of your code.
Do not accept passwords or other secrets through command line arguments either. Instead, use Console.html#readPassword()
from the JDK.
Parent topic: User Authentication and Authorization