class ZTSImpl implements KeyStore, ZTSHandler {

    @Override
    public RoleCertificate postRoleCertificateRequestExt(ResourceContext ctx, RoleCertificateRequest req) {

        final String caller = ctx.getApiName();
        final String principalDomain = logPrincipalAndGetDomain(ctx);

        if (readOnlyMode) {
            throw requestError("Server in Maintenance Read-Only mode. Please try your request later",
                    caller, ZTSConsts.ZTS_UNKNOWN_DOMAIN, principalDomain);
        }

        validateRequest(ctx.request(), principalDomain, caller);
        validate(req, TYPE_ROLE_CERTIFICATE_REQUEST, principalDomain, caller);

        // validate principal object to make sure we're not
        // processing a role identity and instead we require
        // a service identity

        final Principal principal = ((RsrcCtxWrapper) ctx).principal();
        validatePrincipalNotRoleIdentity(principal, caller);

        // for consistent handling of all requests, we're going to convert
        // all incoming object values into lower case since ZMS Server
        // saves all of its object names in lower case

        String proxyForPrincipal = req.getProxyForPrincipal();

        if (proxyForPrincipal != null) {
            proxyForPrincipal = normalizeDomainAliasUser(proxyForPrincipal.toLowerCase());
        }

        final String domainName = principal.getDomain();
        setRequestDomain(ctx, principalDomain);

        // verify that this is not an authorized service principal
        // which is only supported for get role token operations

        if (isAuthorizedServicePrincipal(principal)) {
            throw forbiddenError("Authorized Service Principals not allowed",
                    caller, domainName, domainName);
        }

        // get our principal's name

        String principalName = principal.getFullName();

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("postRoleCertificateRequestExt(principal: {}, proxy-for: {}",
                    principalName, proxyForPrincipal);
        }

        // we can only have a proxy for principal request if the original
        // caller is authorized for such operations

        if (proxyForPrincipal != null && !isAuthorizedProxyUser(authorizedProxyUsers, principalName)) {
            LOGGER.error("postRoleCertificateRequestExt: Principal: " + principalName +
                    " not authorized for proxy role certificate request");
            throw forbiddenError("postRoleCertificateRequestExt: Principal: " + principalName
                    + " not authorized for proxy role certificate request",
                    caller, domainName, domainName);
        }

        X509RoleCertRequest certReq;
        try {
            certReq = new X509RoleCertRequest(req.getCsr());
        } catch (CryptoException ex) {
            throw requestError("Unable to parse PKCS10 CSR: " + ex.getMessage(),
                    caller, domainName, domainName);
        }

        Map<String, String[]> requestedRoleList = certReq.getRequestedRoleList();
        if (requestedRoleList == null) {
            throw requestError("No roles requested in CSR", caller, domainName, domainName);
        }

        if (singleDomainInRoleCert && requestedRoleList.size() != 1) {
            throw requestError("Role Certificate cannot contain roles from multiple domains",
                    caller, domainName, domainName);
        }

        // verify access to the requested roles

        if (!verifyAccessToRoles(principalName, requestedRoleList)) {
            throw forbiddenError("Not authorized to assume all requested roles by user principal",
                    caller, domainName, domainName);
        }

        // if this is proxy for operation then we want to make sure that
        // both principals have access to the same set of roles so we'll
        // remove any roles that are authorized by only one of the principals

        String proxyUser = null;
        if (proxyForPrincipal != null) {

            if (!verifyAccessToRoles(proxyForPrincipal, requestedRoleList)) {
                throw forbiddenError("Not authorized to assume all requested roles by proxy principal",
                        caller, domainName, domainName);
            }

            // we need to switch our principal

            proxyUser = principalName;
            principalName = proxyForPrincipal;
        }

        // validate request/csr details

        X509Certificate cert = principal.getX509Certificate();
        final String ipAddress = ServletRequestUtil.getRemoteAddress(ctx.request());

        if (!validateRoleCertificateExtRequest(certReq, principalName, proxyUser, cert, ipAddress)) {
            throw requestError("postRoleCertificateRequestExt: Unable to validate cert request",
                    caller, domainName, domainName);
        }

        int expiryTime = determineRoleCertTimeout(requestedRoleList, (int) req.getExpiryTime());
        final String x509Cert = instanceCertManager.generateX509Certificate(req.getCsr(),
                InstanceProvider.ZTS_CERT_USAGE_CLIENT, expiryTime);
        if (null == x509Cert || x509Cert.isEmpty()) {
            throw serverError("postRoleCertificateRequest: Unable to create certificate from the cert signer",
                    caller, domainName, domainName);
        }
        RoleCertificate roleCertificate = new RoleCertificate().setX509Certificate(x509Cert);

        // log our certificate

        instanceCertManager.logX509Cert(principal, ipAddress, ZTSConsts.ZTS_SERVICE,
                null, Crypto.loadX509Certificate(x509Cert));

        return roleCertificate;
    }

    public  ZTSImpl();
    public  ZTSImpl(CloudStore implCloudStore, DataStore implDataStore);

}