$Id: DelegationInterface.xml,v 1.6 2006/07/20 15:42:52 szamsu Exp $
Table of Contents
Abstract
This document describes the GridSite Delegation Interface, which is defined as a WSDL.
There are three types of parameters, which format requires more explanation:
delegation ID is a simple string of printable ASCII characters, which matches the following regular expression: '[a-zA-Z0-9.,_ ]+'.
If an empty string is given by the client as delegation ID, then the service generates one, by hashing the client's certificate DN and VOMS attributes. This can simplify the client code (does not have to store delegation ID) but still allowing the client acting in different VO roles to have separate delegation sessions.
proxy request is an RFC 3280 certificate request in PEM format with Base64 encoding. This has to be signed by the client's certificate and private key.
proxy is an RFC 3820 proxy certificate in PEM format with Base64 encoding.
The typical usage scenario for a client is the following:
The client calls getNewProxyReq() and receives a new (generated by the server) delegation ID and a proxy certificate request. The private key, associated with the certificate request stays on the server.
The client signs the proxy certificate request using the client's private key, associated with the client's certificate.
The client uploads the signed proxy certificate using putProxy to the delegation service.
If the client's activities, which require the delegated credentials, last longer than expected, then the client can renew them:
The client calls renewProxyReq() using the existing delegation ID, and receives a new proxy certificate request. The private key, associated with the certificate request stays on the server.
The client signs the proxy certificate request using the client's private key, associated with the client's certificate.
The client uploads the signed proxy certificate using putProxy to the delegation service.
The client may terminate its remote activities by destroying the delegated credentials:
The client calls getTerminationTime() using a delegation ID, to check if there is any delegated credential on the server. If there is, the response is its expiration date and time.
If there are not expired delegated credentials, then the client calls destroy() using the delegation ID. The delegation service will destroy the credentials on the server.
string proxyRequest ;
The new RFC 3280 style proxy certificate request in PEM format with Base64 encoding.
Delegation {string getVersion()
throws DelegationException;string getInterfaceVersion()
throws DelegationException;string getServiceMetadata(string key)
throws DelegationException;string getProxyReq(string delegationID)
throws DelegationException;NewProxyReq getNewProxyReq()
throws DelegationException;putProxy(string delegationID,
string proxy)
throws DelegationException;string renewProxyReq(string delegationID)
throws DelegationException;dateTime getTerminationTime(string delegationID)
throws DelegationException;destroy(string delegationID)
throws DelegationException;
}
Delegation interface.
string getVersion()
throws DelegationException;
Returns: The version number of the service implementation.
Throws:
Internal server error.
This method can be used to query the version of the service.
string getInterfaceVersion()
throws DelegationException;
Returns: The version number of the supported interface.
Throws:
Internal server error.
This method can be used to query the version of the supported interface.
string getServiceMetadata(string key)
throws DelegationException;
Parameters:
The key of the queried service metadata, for example 'features', 'timestamp' or 'glueinfo'.
Returns: The value for the queried key.
Throws:
The key does not exists or cannot be queried by the client.
This method can be used to query implementation specific meta information about the service.
string getProxyReq(string delegationID)
throws DelegationException;
Parameters:
The ID of the new delegation session, specified by the client. The ID can be empty.
Returns: The new RFC 3280 style proxy certificate request in PEM format with Base64 encoding.
Throws:
The client's DN and VOMS attributes do not match the stored ones, i.e. the client is not authorized.
Starts the delegation procedure by asking for a certificate signing request from the server. The server answers with a certificate signing request which includes the public key for the new delegated credentials. putProxy() has to be called to finish the procedure.
Check if a delegation ID was provided. If not, generate a delegation id by hashing the client DN and client VOMS attributes.
Check if the delegation ID already exists in the storage-area. If it does (a credential renewal is happening), check existing info (DN and VOMS attributes) against client info. Throw exception if they do not match.
Create a new private/public key-pair (see also Key Generation Semantics).
Generate a new proxy certificate request.
Store private key and cert request in storage-cache-area, along with the requesting DN and VOMS attributes.
NewProxyReq getNewProxyReq()
throws DelegationException;
Returns: The server side generated ID of the new delegation session and the new RFC 3280 style proxy certificate request in PEM format with Base64 encoding.
Throws:
There were already credentials associated to the delegation ID.
Starts the delegation procedure by asking for a certificate signing request from the server. The server answers with a certificate signing request which includes the public key for the new delegated credentials. putProxy() has to be called to finish the procedure.
Generate a delegation ID by hashing the client DN and client VOMS attributes.
Check if the delegation ID already exists in the storage-area. If it does, check existing info (DN and VOMS attributes) against client info. Throw exception if they do not match, because then this is the rare case of hash collision, i.e. two different clients are mapped to the same delegation ID.
Create a new private/public key-pair (see also Key Generation Semantics).
Generate a new certificate request.
Store private key and cert request in storage-cache-area, along with the requesting DN and VOMS attributes.
putProxy(string delegationID,
string proxy)
throws DelegationException;
Parameters:
The ID of an already existing delegation session, initiated by getProxyReq() or getNewProxyReq().
RFC 3280 style proxy certificate, signed by the client, in PEM format with Base64 encoding.
Throws:
There were no cached credentials associated to the delegation ID (neither getNewProxyReq() nor renewProxyReq() was called previously), or the client's DN and VOMS attributes do not match the stored ones, i.e. the client is not authorized.
Finishes the delegation procedure by sending the signed proxy certificate to the server.
Check if a delegation ID was provided. If not, generate a delegation id by hashing the client DN and client VOMS attributes.
Check if the delegation ID already exists in the storage-area. If it does, check existing info (DN and VOMS attributes) against client info. Throw exception if it does not match.
Check, if client information matches proxy information.
Check given proxy against private key of delegation ID in storage-cache-area. If they do not match, throw exception.
Store proxy in storage-area and clean up the storage-cache-area.
string renewProxyReq(string delegationID)
throws DelegationException;
Parameters:
The ID of an already existing delegation session, where the client wants to renew the delegated credential.
Returns: The new RFC 3280 style proxy certificate request, which is to replace the existing one, in PEM format with Base64 encoding.
Throws:
There were no credentials associated to the delegation ID, or the client's DN and VOMS attributes do not match the stored ones, i.e. the client is not authorized.
Restarts the delegation procedure by asking for a certificate signing request from the server for an already existing delegation ID. The server answers with a certificate signing request which includes the public key for new delegated credentials. putProxy() has to be called to finish the procedure.
Check if a delegation ID was provided. If not, generate a delegation id by hashing the client DN and client VOMS attributes.
Check if the delegation ID already exists in the storage-area. If it does not, then throw an exception.
Check if the existing info (DN and VOMS attributes) against client info. Throw exception if they do not match.
Create a new private/public key-pair (see also Key Generation Semantics).
Generate a new certificate request.
Store private key and cert request in storage-cache-area, along with the requesting DN and VOMS attributes.
dateTime getTerminationTime(string delegationID)
throws DelegationException;
Parameters:
The ID of an already existing delegation session to be queried.
Returns: The date and time when the delegated credentials will expire.
Throws:
There were no credentials associated to the delegation ID, or the client's DN and VOMS attributes do not match the stored ones, i.e. the client is not authorized.
Returns the termination (expiration) date and time of the credential, associated with the given delegaion ID. If there was no delegation ID, then generate one by hashing the client DN and client VOMS attributes.
destroy(string delegationID)
throws DelegationException;
Parameters:
The ID of an already existing delegation session to be destroyed.
Throws:
There were no credentials associated to the delegation ID, or the client's DN and VOMS attributes do not match the stored ones, i.e. the client is not authorized.
Destroys the delegated credentials associated with the given delegation ID immediately. If there was no delegation ID, then generate one by hashing the client DN and client VOMS attributes.
When the client initiates a delegation procedure the delegation service generates a proxy certificate request. This request contains a public key and the associated private key is kept on the server.
There are three basic methods to handle this public/private key-pair:
A new key-pair is generated for every delegation session. This can be computationally intensive, if there are many delegations.
However it can provide some additional safety measure, if the delegated credentials are passed to user executables, for example inside a computing element.
When the service starts a key-pair is generated and it is re-used for every delegation session, during the service's lifetime. This approach can improve performance, if there are many delegations.
This approach raises some security concerns, if the delegated credentials are used by different user executables, for example inside a computing element.
A balance can be found between the two approaches by generating key-pairs less frequently than every delegation session, but still considering the problems of sharing key-pairs. For example a new key-pair is generated for every user, but only one, during the lifetime of the delegation service.
Holds the delegated proxies information (proxy certificate, private key).
Holds the certificate requests information (certificate request, private key). This is transitional information kept between the proxy request and the put proxy operations from the client.
Both areas also hold additional information about the request/proxy: list of VOMS attributes, client DN
The implementation shall have a configurable <storage-base-path>, which points to a directory in the file system, which is writable by the delegation service.
The storage contains the properly delegated credentials (certificate, private key and VOMS attributes), separated per user (using the DN of the user's certificate) and per delegation ID.
The structure inside the storage is:
<storage-base-path>/<user-dn>/<dlg-id>/userproxy.pem <storage-base-path>/<user-dn>/<dlg-id>/voms.attributes
The storage cache contains the credentials, which are not yet delegated, i.e. the certificate request, the private key and the VOMS attributes. Once the client signs the requests and uploads the signed certificate using 'putProxy()', the credentials are moved to the storage area.
The structure inside the storage cache are is:
<storage-base-path>/cache/<user-dn>/<dlg-id>/userreq.pem <storage-base-path>/cache/<user-dn>/<dlg-id>/userkey.pem <storage-base-path>/cache/<user-dn>/<dlg-id>/voms.attributes
The DB implementation relies on a very basic schema. A version of this schema for a MySQL backend is provided, but as the interface used is JDBC the same code can be used against another backend without change.
If needed, an application can choose to implement their own storage backend, while reusing the code implementing the logic of the delegation procedure. For this all that is needed is providing a GrDPStorageFactory and a GrDPStorage interface classes. The existing examples for Database and Filesystem should be enough to understand the mechanism.
<?xml version="1.0" encoding="UTF-8"?> <wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:tns="http://www.gridsite.org/namespaces/delegation-2" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.gridsite.org/namespaces/delegation-2"> <wsdl:types> <xsd:schema targetNamespace="http://www.gridsite.org/namespaces/delegation-2"> <xsd:element name="DelegationException"> <xsd:complexType> <xsd:sequence> <xsd:element name="msg" type="xsd:string" nillable="true"> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> <xsd:element name="NewProxyReq"> <xsd:complexType> <xsd:sequence> <xsd:element name="proxyRequest" nillable="true" type="xsd:string"> </xsd:element> <xsd:element name="delegationID" nillable="true" type="xsd:string"> </xsd:element> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> </wsdl:types> <wsdl:message name="getVersionRequest"> </wsdl:message> <wsdl:message name="getVersionResponse"> <wsdl:part name="getVersionReturn" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getInterfaceVersionRequest"> </wsdl:message> <wsdl:message name="getInterfaceVersionResponse"> <wsdl:part name="getInterfaceVersionReturn" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getServiceMetadataRequest"> <wsdl:part name="key" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getServiceMetadataResponse"> <wsdl:part name="getServiceMetadataReturn" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getProxyReqRequest"> <wsdl:part name="delegationID" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getProxyReqResponse"> <wsdl:part name="getProxyReqReturn" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="putProxyRequest"> <wsdl:part name="delegationID" type="xsd:string"> </wsdl:part> <wsdl:part name="proxy" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="putProxyResponse"/> <wsdl:message name="renewProxyReqRequest"> <wsdl:part name="delegationID" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="renewProxyReqResponse"> <wsdl:part name="renewProxyReqReturn" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getNewProxyReqRequest"/> <wsdl:message name="getNewProxyReqResponse"> <wsdl:part name="getNewProxyReqReturn" element="tns:NewProxyReq"> </wsdl:part> </wsdl:message> <wsdl:message name="getTerminationTimeRequest"> <wsdl:part name="delegationID" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="getTerminationTimeResponse"> <wsdl:part name="getTerminationTimeReturn" type="xsd:dateTime"> </wsdl:part> </wsdl:message> <wsdl:message name="destroyRequest"> <wsdl:part name="delegationID" type="xsd:string"> </wsdl:part> </wsdl:message> <wsdl:message name="destroyResponse"/> <wsdl:message name="DelegationException_Fault"> <wsdl:part name="fault" element="tns:DelegationException"/> </wsdl:message> <wsdl:portType name="Delegation"> <wsdl:operation name="getVersion"> <wsdl:input message="tns:getVersionRequest" name="getVersionRequest"/> <wsdl:output message="tns:getVersionResponse" name="getVersionResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getInterfaceVersion"> <wsdl:input message="tns:getInterfaceVersionRequest" name="getInterfaceVersionRequest"/> <wsdl:output message="tns:getInterfaceVersionResponse" name="getInterfaceVersionResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getServiceMetadata" parameterOrder="key"> <wsdl:input message="tns:getServiceMetadataRequest" name="getServiceMetadataRequest"/> <wsdl:output message="tns:getServiceMetadataResponse" name="getServiceMetadataResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getProxyReq" parameterOrder="delegationID"> <wsdl:input message="tns:getProxyReqRequest" name="getProxyReqRequest"/> <wsdl:output message="tns:getProxyReqResponse" name="getProxyReqResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getNewProxyReq"> <wsdl:input message="tns:getNewProxyReqRequest" name="getNewProxyReqRequest"/> <wsdl:output message="tns:getNewProxyReqResponse" name="getNewProxyReqResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="putProxy" parameterOrder="delegationID proxy"> <wsdl:input message="tns:putProxyRequest" name="putProxyRequest"/> <wsdl:output message="tns:putProxyResponse" name="putProxyResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="renewProxyReq" parameterOrder="delegationID"> <wsdl:input message="tns:renewProxyReqRequest" name="renewProxyReqRequest"/> <wsdl:output message="tns:renewProxyReqResponse" name="renewProxyReqResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getTerminationTime" parameterOrder="delegationID"> <wsdl:input message="tns:getTerminationTimeRequest" name="getTerminationTimeRequest"/> <wsdl:output message="tns:getTerminationTimeResponse" name="getTerminationTimeResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> <wsdl:operation name="destroy" parameterOrder="delegationID"> <wsdl:input message="tns:destroyRequest" name="destroyRequest"/> <wsdl:output message="tns:destroyResponse" name="destroyResponse"/> <wsdl:fault message="tns:DelegationException_Fault" name="DelegationException"> </wsdl:fault> </wsdl:operation> </wsdl:portType> <wsdl:binding name="DelegationSoapBinding" type="tns:Delegation"> <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <wsdl:operation name="getVersion"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getVersionRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="getVersionResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getInterfaceVersion"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getInterfaceVersionRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="getInterfaceVersionResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getServiceMetadata"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getServiceMetadataRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="getServiceMetadataResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getProxyReq"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getProxyReqRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="getProxyReqResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getNewProxyReq"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getNewProxyReqRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="getNewProxyReqResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="renewProxyReq"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="renewProxyReqRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="renewProxyReqResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="putProxy"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="putProxyRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="putProxyResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="getTerminationTime"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="getTerminationTimeRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="getTerminationTimeResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> <wsdl:operation name="destroy"> <wsdlsoap:operation soapAction=""/> <wsdl:input name="destroyRequest"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:input> <wsdl:output name="destroyResponse"> <wsdlsoap:body namespace="http://www.gridsite.org/namespaces/delegation-2" use="literal"/> </wsdl:output> <wsdl:fault name="DelegationException"> <wsdlsoap:fault name="DelegationException" use="literal"/> </wsdl:fault> </wsdl:operation> </wsdl:binding> <wsdl:service name="DelegationService"> <wsdl:port binding="tns:DelegationSoapBinding" name="gridsite-delegation"> <wsdlsoap:address location="https://localhost:8443/glite-security-delegation"/> </wsdl:port> </wsdl:service> </wsdl:definitions>
-- -- Holds certificate request information -- CREATE TABLE t_credential_cache ( -- -- delegation identifier dlg_id VARCHAR(100), -- -- DN of delegated proxy owner dn VARCHAR(255), -- -- certificate request cert_request TEXT, -- -- private key of request priv_key TEXT, -- -- list of voms attributes contained in delegated proxy voms_attrs TEXT, -- -- set primary key PRIMARY KEY (dlg_id, dn) ); -- -- Holds delegated proxies -- CREATE TABLE t_credential ( -- -- delegation identifier dlg_id VARCHAR(100), -- -- DN of delegated proxy owner dn VARCHAR(255), -- -- delegated proxy certificate chain proxy TEXT, -- -- list of voms attributes contained in delegated proxy voms_attrs TEXT, -- -- termination time of the credential termination_time DATETIME, -- -- set primary key PRIMARY KEY (dlg_id, dn) ); -- -- Schema version -- CREATE TABLE t_credential_vers ( major INTEGER NOT NULL, minor INTEGER NOT NULL, patch INTEGER NOT NULL ); INSERT INTO t_credential_vers (major,minor,patch) VALUES (1,2,0);
-- -- Holds certificate request information -- CREATE TABLE t_credential_cache ( -- -- delegation identifier dlg_id VARCHAR2(100), -- -- DN of delegated proxy owner dn VARCHAR2(255), -- -- certificate request cert_request CLOB CONSTRAINT cred_cache_cert_req_not_null NOT NULL, -- -- private key of request priv_key CLOB CONSTRAINT cred_cache_priv_key_not_null NOT NULL, -- -- list of voms attributes contained in delegated proxy voms_attrs CLOB, -- -- set primary key CONSTRAINT cred_cache_pk PRIMARY KEY (dlg_id, dn) ); -- -- Holds delegated proxies -- CREATE TABLE t_credential ( -- -- delegation identifier dlg_id VARCHAR2(100), -- -- DN of delegated proxy owner dn VARCHAR2(255), -- -- delegated proxy certificate chain proxy CLOB CONSTRAINT cred_proxy_not_null NOT NULL, -- -- list of voms attributes contained in delegated proxy voms_attrs CLOB, -- -- termination time of the credential termination_time TIMESTAMP WITH TIME ZONE CONSTRAINT cred_term_time_not_null NOT NULL, -- -- set primary key CONSTRAINT cred_pk PRIMARY KEY (dlg_id, dn) ); -- -- Schema version -- CREATE TABLE t_credential_vers ( major NUMBER NOT NULL, minor NUMBER NOT NULL, patch NUMBER NOT NULL ); INSERT INTO t_credential_vers (major,minor,patch) VALUES (1,2,0); -- t_credential index: -- t_credential(dlg,dn) is primary key CREATE INDEX credential_term_time ON t_credential(termination_time);