public final class EndUserSecurityContext
extends java.lang.Object
The security context in which the end user of an application executes
database operations. An EndUserSecurityContext allows an
Oracle JDBC Connection to operate under the security policy
of a specific end user, rather than the security policy of a database
user. This class is designed for integration with the Oracle Deep Data
Security feature of Oracle Database (i.e., DeepSec).
Instances of EndUserSecurityContext encapsulate the set of
values listed below.
EndUserSecurityContext must have a database access token. The
claims of this token should be mapped to a DATA ROLE or APPLICATION IDENTITY
defined in the database.
EndUserSecurityContext must either have an end user token or
end user name. The claims of an end user token should be mapped to a
DATA ROLE defined in the database. An end user name should be defined
as an END USER in the database.
EndUserSecurityContext may have data roles that are enabled
and disabled based on application logic, rather than a database security
policy. This type of DATA ROLE is defined in the database with a DISABLED
clause.
EndUserSecurityContext may have attributes. Attributes are
contained in JSON objects conforming to a JSON schema of an END USER CONTEXT
declared in the database. Usage of attributes can be seen in
this code example.
EndUserSecurityContext has a look-up key which the
database associates to a set of END USER CONTEXT attributes. The database
applies the same attributes to all database operations executed with an
EndUserSecurityContext having the same look-up key.
EndUserSecurityContext that identifies an end user with a
security token, its look-up key is automatically generated by the database.
lookUpKey() method
because the value is generated by the database, not the application.
EndUserSecurityContext that identifies an end user with a
name, its look-up key may be automatically generated by an instance of this
class or provided by an application.
lookUpKey() method because
this value is generated by the application, not by the database.
EndUserSecurityContext have the following
API contract.
EndUserSecurityContext are immutable. Mutation is
accomplished through "wither" methods, such as
withDatabaseAccessToken(CharSequence). These methods return a copy
of an EndUserSecurityContext with an old value replaced by a new
value.
NullPointerException if
a parameter is null or the parameter is a collection or array that contains
null.
CharSequence is
passed to a method, no reference to the original object will be kept, since a
CharSequence may be mutable. Likewise, no reference
will be retained to a char[] returned by a method, because a
char[] is mutable. This behavior ensures that all instances of
EndUserSecurityContext are immutable, while also allowing
security-sensitive values to be cleared from memory, as shown in
this code example.
A Connection from the Oracle JDBC Driver may be configured
with an instance of EndUserSecurityContext in either of the
following ways:
EndUserSecurityContextProvider can be
installed as a service provider. Before each database operation, an
OracleConnection retrieves the current
EndUserSecurityContext from that provider. Open-source
provider implementations are available from the
ojdbc-extensions project.
If setEndUserSecurityContext is called when a provider is
also installed, Oracle JDBC ignores the provider until
clearEndUserSecurityContext is called.
This first example shows how to use an EndUserSecurityContext
with a database access token and end user token. In this example, the
application defines a getDatabaseAccessToken() method that
returns a token authorizing the application to access the database. An OAUTH
client library might be used to request a database access token. The
application also defines a getCurrentUser() method that returns
information about the currently authenticated end user. This method
might be implemented using a security library that provides authentication
details to a thread when handling the end user's request.
public void example(DataSource dataSource) throws SQLException {
try (Connection connection = dataSource.getConnection()) {
char[] databaseAccessToken = getDatabaseAccessToken();
char[] endUserToken = getCurrentUser().getSecurityToken();
final EndUserSecurityContext endUserSecurityContext;
try {
endUserSecurityContext = EndUserSecurityContext.createWithToken(
CharBuffer.wrap(databaseAccessToken),
CharBuffer.wrap(endUserToken));
}
finally {
Arrays.fill(databaseAccessToken, (char)0);
Arrays.fill(endUserToken, (char)0);
}
OracleConnection oracleConnection =
connection instanceof OracleConnection
? (OracleConnection)connection
: connection.unwrap(OracleConnection.class);
oracleConnection.setEndUserSecurityContext(endUserSecurityContext);
try {
executeSqlAsEndUser(connection);
}
finally {
oracleConnection.clearEndUserSecurityContext();
}
}
}
The next example shows how to check the expiration time of a database
access token and end user token. In this example, the expiration times are
checked to ensure the tokens won't expire in the next two minutes. The
database only requires that tokens be valid at the start of a SQL
operation, and so a two-minute threshold would be excessive if only
one SQL command needs to be executed with the
EndUserSecurityContext. But if multiple SQL commands are
executed, the tokens will need to remain valid until the last SQL command
begins executing. Two minutes is just an example of a threshold that might be
used. The actual threshold should depend on application specific timeouts for
JDBC Connection usage, such as those that a connection pool might offer.
void example(EndUserSecurityContext endUserSecurityContext, Connection connection) throws SQLException {
OffsetDateTime deadline = OffsetDateTime.now().plusMinutes(2);
OffsetDateTime databaseAccessTokenExpiration = endUserSecurityContext.databaseAccessTokenExpiration();
if (databaseAccessTokenExpiration.isBefore(deadline)) {
String refreshedToken = refreshDatabaseAccessToken();
endUserSecurityContext = endUserSecurityContext.withDatabaseAccessToken(refreshedToken);
}
OffsetDateTime endUserTokenExpiration = endUserSecurityContext.endUserTokenExpiration();
if (endUserTokenExpiration.isBefore(deadline)) {
String refreshedToken = refreshEndUserToken();
endUserSecurityContext = endUserSecurityContext.withEndUserToken(refreshedToken);
}
OracleConnection oracleConnection =
connection instanceof OracleConnection
? (OracleConnection)connection
: connection.unwrap(OracleConnection.class);
oracleConnection.setEndUserSecurityContext(endUserSecurityContext);
try {
executeSqlAsEndUser(connection);
}
finally {
oracleConnection.clearEndUserSecurityContext();
}
}
The next example shows how to configure an
EndUserSecurityContext with attributes of two END USER CONTEXT
objects defined as:
CREATE END USER CONTEXT user_info USING JSON SCHEMA '{
"type": "object",
"properties": {
"first_name": { "type": "string", "default": "(GUEST)" },
"last_name": { "type": "string", "default": "(GUEST)" }
}
}';
CREATE END USER CONTEXT location_info USING JSON SCHEMA '{
"type" : "object",
"properties" : {
"address" : {
"type" : "object",
"properties" : {
"street_name": {"type": "string", "default": "(UNKNOWN)"},
"address_number": {"type": "integer", "default": 0}
}
},
"country": { "type": "string", "default": "(UNKNOWN)"},
"state": { "type": "string", "default": "(UNKNOWN)"},
"city": { "type": "string", "default": "(UNKNOWN)"}
}
}';
While the attributes of an END USER CONTEXT are represented as a JSON object,
an application might use a different representation to store information
about its end users. In the code that follows, an application defines a
UserDetails object that encapsulates attributes of its end
users, and it translates this information into JSON objects. The JSON
objects are represented as instances of OracleJsonObject created by
an OracleJsonFactory.
It is important to note that a fully-qualified END USER CONTEXT name must be
provided in the Map passed to the withAttributes
method. A fully-qualified name includes the name of the database schema in
which an END USER CONTEXT is declared, using this format:
{schema}.{name}. The database will not recognize unqualified
names of an END USER CONTEXT.
void example(
UserDetails userDetails,
EndUserSecurityContext endUserSecurityContext,
Connection connection)
throws SQLException {
OracleJsonFactory jsonFactory = new OracleJsonFactory();
OracleJsonObject jsonUserInfo = jsonFactory.createObject();
Name name = userDetails.name();
jsonUserInfo.put("first_name", name.firstName());
jsonUserInfo.put("last_name", name.lastName());
OracleJsonObject jsonLocationInfo = jsonFactory.createObject();
Location location = userDetails.location();
jsonLocationInfo.put("city", location.city());
jsonLocationInfo.put("state", location.state());
jsonLocationInfo.put("country", location.country());
OracleJsonObject jsonAddress = jsonFactory.createObject();
jsonAddress.put("street_name", location.streetName());
jsonAddress.put("address_number", location.addressNumber());
jsonLocationInfo.put("address", jsonAddress);
// END USER CONTEXT names MUST be fully qualified with their schema name
endUserSecurityContext = endUserSecurityContext.withAttributes(Map.of(
"app_schema.user_info", jsonUserInfo,
"app_schema.location_info", jsonLocationInfo));
OracleConnection oracleConnection =
connection instanceof OracleConnection
? (OracleConnection)connection
: connection.unwrap(OracleConnection.class);
oracleConnection.setEndUserSecurityContext(endUserSecurityContext);
try {
executeSqlAsEndUser(connection);
}
finally {
oracleConnection.clearEndUserSecurityContext();
}
}
The next example shows how the database stores attributes based on a
look-up key that is automatically generated from the database access
token, end user token, and application managed data roles of an
EndUserSecurityContext.
void example(EndUserSecurityContext endUserSecurityContext, Connection connection) throws SQLException {
OracleConnection oracleConnection =
connection instanceof OracleConnection
? (OracleConnection) connection
: connection.unwrap(OracleConnection.class);
try {
// Executes SQL with: {"a":1, "b":1}
OracleJsonObject attributes = new OracleJsonFactory().createObject();
attributes.put("a", 1);
attributes.put("b", 1);
endUserSecurityContext = endUserSecurityContext.withAttributes("scott.example", attributes);
oracleConnection.setEndUserSecurityContext(endUserSecurityContext);
executeSqlAsEndUser(connection);
// Executes SQL with: {"a":1, "b":2}, even though "a" is not present in
// the attributes of the EndUserSecurityContext. The value of "a" is 1
// because the look-up key is identical to that of the previous
// operation. The look-up key is identical because the database
// access token, end user token, and application managed data roles are
// identical.
attributes.remove("a");
attributes.put("b", 2);
endUserSecurityContext = endUserSecurityContext.withAttributes("scott.example", attributes);
oracleConnection.setEndUserSecurityContext(endUserSecurityContext);
executeSqlAsEndUser(connection);
// Executes SQL with: {"a":1, "b":2}, even though no attributes are
// present in the EndUserSecurityContext, the database will again see
// an identical look-up key.
endUserSecurityContext = endUserSecurityContext.withAttributes(Collections.emptyMap());
oracleConnection.setEndUserSecurityContext(endUserSecurityContext);
executeSqlAsEndUser(connection);
}
finally {
oracleConnection.clearEndUserSecurityContext();
}
}
| Modifier and Type | Method and Description |
|---|---|
java.util.Map<java.lang.String,OracleJsonObject> |
attributes()
Returns an
unmodifiable
Map of END USER CONTEXT attributes,
represented as unmodifiable instances of OracleJsonObject. |
static EndUserSecurityContext |
createWithName(java.lang.CharSequence databaseAccessToken,
java.lang.String endUserName)
Creates a security context in which an application with a
database access token
executes database operations for an
end user identified by a name.
|
static EndUserSecurityContext |
createWithName(java.lang.CharSequence databaseAccessToken,
java.lang.String endUserName,
java.lang.CharSequence lookUpKey)
Creates a security context in which an application with a
database access token
executes database operations for an
end user identified by a name.
|
static EndUserSecurityContext |
createWithToken(java.lang.CharSequence databaseAccessToken,
java.lang.CharSequence endUserToken)
Creates a security context in which an application with a
database access token
executes database operations for an
end user identified by a security token.
|
char[] |
databaseAccessToken()
Returns the
database access token of this
EndUserSecurityContext. |
java.time.OffsetDateTime |
databaseAccessTokenExpiration()
Returns the expiration time of the
database access token.
|
java.util.Set<java.lang.String> |
dataRoles()
Returns an
unmodifiable set of
application managed data roles that have been
enabled for the end user. |
java.util.Optional<java.lang.String> |
endUserName()
Returns an
Optional containing an
end user name,
Optional.empty() if the end user is identified by a security token
instead of a name. |
java.util.Optional<char[]> |
endUserToken()
Returns an
Optional containing the
end user token, or
Optional.empty() if the end user is identified by a name instead of
a token. |
java.time.OffsetDateTime |
endUserTokenExpiration()
Returns the expiration time of the
end user token, or
OffsetDateTime.MAX if the end user is identified by a name instead
of a token. |
boolean |
equals(java.lang.Object object)
Returns
true if an object is an
EndUserSecurityContext encapsulating values which are all
equal to those of this EndUserSecurityContext. |
int |
hashCode()
Returns a hash code computed from all values encapsulated by this
EndUserSecurityContext. |
java.util.Optional<char[]> |
lookUpKey()
Returns an
Optional containing the
look-up key for this
EndUserSecurityContext if the end user is identified by a
name, or Optional.empty() if the end user is identified
by a token instead of a name. |
java.lang.String |
toString()
Returns a String containing non-security-sensitive values encapsulated by
this
EndUserSecurityContext; This method will NOT return
the clear text form of security tokens, look-up keys, or any other
information that grants access to a secured system. |
EndUserSecurityContext |
withAttributes(java.util.Map<java.lang.String,OracleJsonObject> attributes)
Returns a copy of this
EndUserSecurityContext with a new set
of attributes mapped to their
fully qualified END USER CONTEXT names. |
EndUserSecurityContext |
withAttributes(java.lang.String contextName,
OracleJsonObject attributes)
Returns a copy of this
EndUserSecurityContext with a new set
of attributes mapped to a fully qualified
END USER CONTEXT name. |
EndUserSecurityContext |
withDatabaseAccessToken(java.lang.CharSequence databaseAccessToken)
Returns a copy of this
EndUserSecurityContext with a new
database access token. |
EndUserSecurityContext |
withDataRoles(java.util.Collection<java.lang.String> dataRoles)
Returns a copy of this
EndUserSecurityContext with a new set
of application managed data roles. |
EndUserSecurityContext |
withDataRoles(java.lang.String... dataRoles)
Returns a copy of this
EndUserSecurityContext with a new set
of application managed data roles. |
EndUserSecurityContext |
withEndUserName(java.lang.String endUserName)
Returns a copy of this
EndUserSecurityContext with a new
end user name and automatically
generated look-up key. |
EndUserSecurityContext |
withEndUserName(java.lang.String endUserName,
java.lang.CharSequence lookUpKey)
|
EndUserSecurityContext |
withEndUserToken(java.lang.CharSequence endUserToken)
Returns a copy of this
EndUserSecurityContext with a new
end user token. |
public java.time.OffsetDateTime databaseAccessTokenExpiration()
Returns the expiration time of the database access token. This method can be used to check if the database access token needs to be refreshed before a long-running sequence of SQL operations, as shown in this example.
public char[] databaseAccessToken()
Returns the
database access token of this
EndUserSecurityContext.
public EndUserSecurityContext withDatabaseAccessToken(java.lang.CharSequence databaseAccessToken)
Returns a copy of this EndUserSecurityContext with a new
database access token.
databaseAccessToken - A database access token. Not null.
Not retained.EndUserSecurityContext with a new database
access token. Not null.public java.time.OffsetDateTime endUserTokenExpiration()
Returns the expiration time of the
end user token, or
OffsetDateTime.MAX if the end user is identified by a name instead
of a token. This method can be used to check if the end user token needs to
be refreshed before a long-running sequence of SQL operations, as shown
in this example.
OffsetDateTime.MAX if the end user is identified by a name instead
of a token. Not null.public java.util.Optional<char[]> endUserToken()
Returns an Optional containing the
end user token, or
Optional.empty() if the end user is identified by a name instead of
a token.
Optional containing an end user token, or
Optional.empty(). Not null. The char[] of a non-empty
Optional is not retained.public EndUserSecurityContext withEndUserToken(java.lang.CharSequence endUserToken)
Returns a copy of this EndUserSecurityContext with a new
end user token.
endUserToken - An end user token. Not null.
Not retained.EndUserSecurityContext with a new end user
token.public java.util.Optional<java.lang.String> endUserName()
Returns an Optional containing an
end user name,
Optional.empty() if the end user is identified by a security token
instead of a name.
Optional.empty() if the end user
is identified by a security token instead of a name.public EndUserSecurityContext withEndUserName(java.lang.String endUserName)
Returns a copy of this EndUserSecurityContext with a new
end user name and automatically
generated look-up key. The name should be
enclosed in double-quotes if the END USER was declared using a
quoted identifier.
endUserName - The name of an end user. Not null.EndUserSecurityContext with a new end user
name and automatically generated look-up key. Not null.public EndUserSecurityContext withEndUserName(java.lang.String endUserName, java.lang.CharSequence lookUpKey)
Returns a copy of this EndUserSecurityContext with a new
end user name and
look-up key. The name should be enclosed in
double-quotes if the END USER was declared using a
quoted identifier.
endUserName - The name of an end user. Not null.lookUpKey - A look-up key for the end user context. Not null.
Not retained.EndUserSecurityContext with a new end user
name and look-up key. Not null.public java.util.Optional<char[]> lookUpKey()
Returns an Optional containing the
look-up key for this
EndUserSecurityContext if the end user is identified by a
name, or Optional.empty() if the end user is identified
by a token instead of a name.
Optional containing a look-up key, or
Optional.empty(). The CharSequence of a non-empty
Optional is not retained.public java.util.Set<java.lang.String> dataRoles()
Returns an unmodifiable set of
application managed data roles that have been
enabled for the end user.
This method does not query the database to obtain all data roles that are
enabled for the end user; It only returns the names of data roles
provided to methods such as withDataRoles(Collection).
public EndUserSecurityContext withDataRoles(java.lang.String... dataRoles)
Returns a copy of this EndUserSecurityContext with a new set
of application managed data roles. A data role
name should be enclosed in double-quotes if the DATA ROLE was
declared using a
quoted identifier.
dataRoles - An array of data role names. Not null, and must not
contain null. Not retained.EndUserSecurityContext with a new set of data
roles. Not null.public EndUserSecurityContext withDataRoles(java.util.Collection<java.lang.String> dataRoles)
Returns a copy of this EndUserSecurityContext with a new set
of application managed data roles. A data role
name should be enclosed in double-quotes if the DATA ROLE was declared
using a
quoted identifier.
dataRoles - A Collection of data role names. Not null, and must not
contain null. Not retained.EndUserSecurityContext with a new set of data
roles. Not null.public java.util.Map<java.lang.String,OracleJsonObject> attributes()
Returns an unmodifiable
Map of END USER CONTEXT attributes,
represented as unmodifiable instances of OracleJsonObject. The
Map returned by this method maps the name of an END USER
CONTEXT to an OracleJsonObject containing the values of its
attributes.
This method does not query the database to obtain all attributes for an end
user. It only returns attributes configured by methods such as
withAttributes(Map).
public EndUserSecurityContext withAttributes(java.lang.String contextName, OracleJsonObject attributes)
Returns a copy of this EndUserSecurityContext with a new set
of attributes mapped to a fully qualified
END USER CONTEXT name. A fully qualified name includes both a schema name
and the END USER CONTEXT name, as in "app_schema.user_context" rather than
"user_context". An end user context name should be enclosed in
double-quotes if the END USER CONTEXT was declared using a
quoted identifier.
contextName - The fully qualified name of an END USER CONTEXT.
Not null.attributes - The attributes of an END USER CONTEXT. Not null.
Not retained.EndUserSecurityContext with a new set of
attributes. Not null.public EndUserSecurityContext withAttributes(java.util.Map<java.lang.String,OracleJsonObject> attributes)
Returns a copy of this EndUserSecurityContext with a new set
of attributes mapped to their
fully qualified END USER CONTEXT names. A fully qualified
name includes both a schema name and the END USER CONTEXT name, as in
"app_schema.user_context" rather than "user_context". An end user context
name should be enclosed in double-quotes if an END USER CONTEXT was
declared using a
quoted identifier.
attributes - A mapping of fully qualified END USER
CONTEXT names to their attributes. The Map must not be null and must not
contain null keys or values. The Map and its
OracleJsonObject values are
not retained.EndUserSecurityContext with a new set of
attributes. Not null.public boolean equals(java.lang.Object object)
true if an object is an
EndUserSecurityContext encapsulating values which are all
equal to those of this EndUserSecurityContext.equals in class java.lang.Objectpublic int hashCode()
EndUserSecurityContext.hashCode in class java.lang.Objectpublic java.lang.String toString()
EndUserSecurityContext; This method will NOT return
the clear text form of security tokens, look-up keys, or any other
information that grants access to a secured system.toString in class java.lang.Objectpublic static EndUserSecurityContext createWithToken(java.lang.CharSequence databaseAccessToken, java.lang.CharSequence endUserToken)
Creates a security context in which an application with a
database access token
executes database operations for an
end user identified by a security token.
The look-up key of the
EndUserSecurityContext is automatically generated by the
database.
databaseAccessToken - A security token that authorizes an application
to access the database. Not null. Not retained.endUserToken - A security token that identifies an end user. Not null.
Not retained.EndUserSecurityContext. Not null.public static EndUserSecurityContext createWithName(java.lang.CharSequence databaseAccessToken, java.lang.String endUserName)
Creates a security context in which an application with a
database access token
executes database operations for an
end user identified by a name.
The look-up key of the
EndUserSecurityContext is automatically generated by this
class. The end user name should be enclosed in double-quotes if the END
USER was declared using a
quoted identifier.
databaseAccessToken - A security token that authorizes an application
to access the database. Not null. Not retained.endUserName - The name of an end user. Not null.
Not retained.EndUserSecurityContext. Not null.public static EndUserSecurityContext createWithName(java.lang.CharSequence databaseAccessToken, java.lang.String endUserName, java.lang.CharSequence lookUpKey)
Creates a security context in which an application with a
database access token
executes database operations for an
end user identified by a name.
The look-up key of the
EndUserSecurityContext is provided to this method, rather than
automatically generated. The end user name should be enclosed in
double-quotes if the END USER was declared using a
quoted identifier.
databaseAccessToken - A security token that authorizes an application
to access the database. Not null. Not retained.endUserName - The name of an end user. Not null.lookUpKey - The look-up key of the
EndUserSecurityContext. Not null.
Not retained.EndUserSecurityContext. Not null.