Class EndUserSecurityContext
- java.lang.Object
-
- oracle.jdbc.EndUserSecurityContext
-
public final class EndUserSecurityContext extends java.lang.ObjectThe security context in which the end user of an application executes database operations. An
EndUserSecurityContextallows an Oracle JDBCConnectionto 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., Deep Sec).Instances of
EndUserSecurityContextencapsulate the set of values listed below.- Database Access Token
-
An
EndUserSecurityContextmust have a database access token. The claims of this token should be mapped to a DATA ROLE or APPLICATION IDENTITY defined in the database. - End User Token or End User Name
-
An
EndUserSecurityContextmust 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. - Application Managed Data Roles
-
An
EndUserSecurityContextmay 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. - Attributes
-
An
EndUserSecurityContextmay 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. - Look-Up Key
-
Every
EndUserSecurityContexthas 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 anEndUserSecurityContexthaving the same look-up key.-
For an
EndUserSecurityContextthat identifies an end user with a security token, its look-up key is automatically generated by the database.- The generated key is derived from the database access token, end user token, and application managed data roles.
-
The look-up key cannot be accessed using the
lookUpKey()method because the value is generated by the database, not the application.
-
For an
EndUserSecurityContextthat 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.- An automatically generated look-up key is derived from the database access token, end user name, and application managed data roles.
-
The look-up key can be accessed using the
lookUpKey()method because this value is generated by the application, not by the database.
-
For an
API Contract
Instances ofEndUserSecurityContexthave the following API contract.-
Instances of
EndUserSecurityContextare immutable. Mutation is accomplished through "wither" methods, such aswithDatabaseAccessToken(CharSequence). These methods return a copy of anEndUserSecurityContextwith an old value replaced by a new value. - No method ever returns null.
-
No method accepts null. All methods throw a
NullPointerExceptionif a parameter is null or the parameter is a collection or array that contains null. -
Methods do not retain references to any mutable objects provided as
parameters or returned as results. For example, if a
CharSequenceis passed to a method, no reference to the original object will be kept, since aCharSequencemay be mutable. Likewise, no reference will be retained to achar[]returned by a method, because achar[]is mutable. This behavior ensures that all instances ofEndUserSecurityContextare immutable, while also allowing security-sensitive values to be cleared from memory, as shown in this code example.
Usage
A
Connectionfrom the Oracle JDBC Driver may be configured with an instance ofEndUserSecurityContextin either of the following ways:- Application code may call setEndUserSecurityContext and clearEndUserSecurityContext as shown in the code example below.
-
An implementation of
EndUserSecurityContextProvidercan be installed as a service provider. Before each database operation, anOracleConnectionretrieves the currentEndUserSecurityContextfrom that provider. Open-source provider implementations are available from the ojdbc-extensions project. IfsetEndUserSecurityContextis called when a provider is also installed, Oracle JDBC ignores the provider untilclearEndUserSecurityContextis called.
Code Examples
This first example shows how to use an
EndUserSecurityContextwith a database access token and end user token. In this example, the application defines agetDatabaseAccessToken()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 agetCurrentUser()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
EndUserSecurityContextwith 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
UserDetailsobject that encapsulates attributes of its end users, and it translates this information into JSON objects. The JSON objects are represented as instances ofOracleJsonObjectcreated by anOracleJsonFactory.It is important to note that a fully-qualified END USER CONTEXT name must be provided in the
Mappassed to thewithAttributesmethod. 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(); } }- Since:
- 26
-
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description java.util.Map<java.lang.String,OracleJsonObject>attributes()Returns anunmodifiableMapof END USER CONTEXT attributes, represented as unmodifiable instances ofOracleJsonObject.static EndUserSecurityContextcreateWithName(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 EndUserSecurityContextcreateWithName(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 EndUserSecurityContextcreateWithToken(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 thisEndUserSecurityContext.java.time.OffsetDateTimedatabaseAccessTokenExpiration()Returns the expiration time of the database access token.java.util.Set<java.lang.String>dataRoles()Returns anunmodifiable setof application managed data roles that have been enabled for the end user.java.util.Optional<java.lang.String>endUserName()Returns anOptionalcontaining 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 anOptionalcontaining the end user token, orOptional.empty()if the end user is identified by a name instead of a token.java.time.OffsetDateTimeendUserTokenExpiration()Returns the expiration time of the end user token, orOffsetDateTime.MAXif the end user is identified by a name instead of a token.booleanequals(java.lang.Object object)Returnstrueif an object is anEndUserSecurityContextencapsulating values which are all equal to those of thisEndUserSecurityContext.inthashCode()Returns a hash code computed from all values encapsulated by thisEndUserSecurityContext.java.util.Optional<char[]>lookUpKey()Returns anOptionalcontaining the look-up key for thisEndUserSecurityContextif the end user is identified by a name, orOptional.empty()if the end user is identified by a token instead of a name.java.lang.StringtoString()Returns a String containing non-security-sensitive values encapsulated by thisEndUserSecurityContext; 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.EndUserSecurityContextwithAttributes(java.lang.String contextName, OracleJsonObject attributes)Returns a copy of thisEndUserSecurityContextwith a new set of attributes mapped to a fully qualified END USER CONTEXT name.EndUserSecurityContextwithAttributes(java.util.Map<java.lang.String,OracleJsonObject> attributes)Returns a copy of thisEndUserSecurityContextwith a new set of attributes mapped to their fully qualified END USER CONTEXT names.EndUserSecurityContextwithDatabaseAccessToken(java.lang.CharSequence databaseAccessToken)Returns a copy of thisEndUserSecurityContextwith a new database access token.EndUserSecurityContextwithDataRoles(java.lang.String... dataRoles)Returns a copy of thisEndUserSecurityContextwith a new set of application managed data roles.EndUserSecurityContextwithDataRoles(java.util.Collection<java.lang.String> dataRoles)Returns a copy of thisEndUserSecurityContextwith a new set of application managed data roles.EndUserSecurityContextwithEndUserName(java.lang.String endUserName)Returns a copy of thisEndUserSecurityContextwith a new end user name and automatically generated look-up key.EndUserSecurityContextwithEndUserName(java.lang.String endUserName, java.lang.CharSequence lookUpKey)EndUserSecurityContextwithEndUserToken(java.lang.CharSequence endUserToken)Returns a copy of thisEndUserSecurityContextwith a new end user token.
-
-
-
Method Detail
-
databaseAccessTokenExpiration
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.
- Returns:
- The expiration time of the database access token. Not null.
-
databaseAccessToken
public char[] databaseAccessToken()
Returns the database access token of this
EndUserSecurityContext.- Returns:
- The database access token. Not null. Not retained.
-
withDatabaseAccessToken
public EndUserSecurityContext withDatabaseAccessToken(java.lang.CharSequence databaseAccessToken)
Returns a copy of this
EndUserSecurityContextwith a new database access token.- Parameters:
databaseAccessToken- A database access token. Not null. Not retained.- Returns:
- A new
EndUserSecurityContextwith a new database access token. Not null.
-
endUserTokenExpiration
public java.time.OffsetDateTime endUserTokenExpiration()
Returns the expiration time of the end user token, or
OffsetDateTime.MAXif 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.- Returns:
- The expiration time of the end user token, or
OffsetDateTime.MAXif the end user is identified by a name instead of a token. Not null.
-
endUserToken
public java.util.Optional<char[]> endUserToken()
Returns an
Optionalcontaining the end user token, orOptional.empty()if the end user is identified by a name instead of a token.- Returns:
- An
Optionalcontaining an end user token, orOptional.empty(). Not null. Thechar[]of a non-emptyOptionalis not retained.
-
withEndUserToken
public EndUserSecurityContext withEndUserToken(java.lang.CharSequence endUserToken)
Returns a copy of this
EndUserSecurityContextwith a new end user token.- Parameters:
endUserToken- An end user token. Not null. Not retained.- Returns:
- A new
EndUserSecurityContextwith a new end user token.
-
endUserName
public java.util.Optional<java.lang.String> endUserName()
Returns an
Optionalcontaining an end user name,Optional.empty()if the end user is identified by a security token instead of a name.- Returns:
- The name of an end user,
Optional.empty()if the end user is identified by a security token instead of a name.
-
withEndUserName
public EndUserSecurityContext withEndUserName(java.lang.String endUserName)
Returns a copy of this
EndUserSecurityContextwith 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.- Parameters:
endUserName- The name of an end user. Not null.- Returns:
- A new
EndUserSecurityContextwith a new end user name and automatically generated look-up key. Not null.
-
withEndUserName
public EndUserSecurityContext withEndUserName(java.lang.String endUserName, java.lang.CharSequence lookUpKey)
Returns a copy of this
EndUserSecurityContextwith 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.- Parameters:
endUserName- The name of an end user. Not null.lookUpKey- A look-up key for the end user context. Not null. Not retained.- Returns:
- A new
EndUserSecurityContextwith a new end user name and look-up key. Not null.
-
lookUpKey
public java.util.Optional<char[]> lookUpKey()
Returns an
Optionalcontaining the look-up key for thisEndUserSecurityContextif the end user is identified by a name, orOptional.empty()if the end user is identified by a token instead of a name.- Returns:
- An
Optionalcontaining a look-up key, orOptional.empty(). TheCharSequenceof a non-emptyOptionalis not retained.
-
dataRoles
public java.util.Set<java.lang.String> dataRoles()
Returns an
unmodifiable setof 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).- Returns:
- A set of DATA ROLE names. Not null.
-
withDataRoles
public EndUserSecurityContext withDataRoles(java.lang.String... dataRoles)
Returns a copy of this
EndUserSecurityContextwith 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.- Parameters:
dataRoles- An array of data role names. Not null, and must not contain null. Not retained.- Returns:
- A new
EndUserSecurityContextwith a new set of data roles. Not null.
-
withDataRoles
public EndUserSecurityContext withDataRoles(java.util.Collection<java.lang.String> dataRoles)
Returns a copy of this
EndUserSecurityContextwith 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.- Parameters:
dataRoles- A Collection of data role names. Not null, and must not contain null. Not retained.- Returns:
- A new
EndUserSecurityContextwith a new set of data roles. Not null.
-
attributes
public java.util.Map<java.lang.String,OracleJsonObject> attributes()
Returns an
unmodifiableMapof END USER CONTEXT attributes, represented as unmodifiable instances ofOracleJsonObject. TheMapreturned by this method maps the name of an END USER CONTEXT to anOracleJsonObjectcontaining 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).- Returns:
- A Map containing the values of END USER CONTEXT attributes. The Map is not null, does not contain a null key, and does not contain null values.
-
withAttributes
public EndUserSecurityContext withAttributes(java.lang.String contextName, OracleJsonObject attributes)
Returns a copy of this
EndUserSecurityContextwith 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.- Parameters:
contextName- The fully qualified name of an END USER CONTEXT. Not null.attributes- The attributes of an END USER CONTEXT. Not null. Not retained.- Returns:
- A new
EndUserSecurityContextwith a new set of attributes. Not null.
-
withAttributes
public EndUserSecurityContext withAttributes(java.util.Map<java.lang.String,OracleJsonObject> attributes)
Returns a copy of this
EndUserSecurityContextwith 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.- Parameters:
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. TheMapand itsOracleJsonObjectvalues are not retained.- Returns:
- A new
EndUserSecurityContextwith a new set of attributes. Not null.
-
equals
public boolean equals(java.lang.Object object)
Returnstrueif an object is anEndUserSecurityContextencapsulating values which are all equal to those of thisEndUserSecurityContext.- Overrides:
equalsin classjava.lang.Object
-
hashCode
public int hashCode()
Returns a hash code computed from all values encapsulated by thisEndUserSecurityContext.- Overrides:
hashCodein classjava.lang.Object
-
toString
public java.lang.String toString()
Returns a String containing non-security-sensitive values encapsulated by thisEndUserSecurityContext; 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.- Overrides:
toStringin classjava.lang.Object
-
createWithToken
public 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
EndUserSecurityContextis automatically generated by the database.- Parameters:
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.- Returns:
- An
EndUserSecurityContext. Not null.
-
createWithName
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
EndUserSecurityContextis 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.- Parameters:
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.- Returns:
- An
EndUserSecurityContext. Not null.
-
createWithName
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
EndUserSecurityContextis 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.- Parameters:
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 theEndUserSecurityContext. Not null. Not retained.- Returns:
- An
EndUserSecurityContext. Not null.
-
-