Search in sources :

Example 1 with ServerErrorMessage

use of org.postgresql.util.ServerErrorMessage in project teiid by teiid.

the class ConnectionFactoryImpl method doAuthentication.

private void doAuthentication(PGStream pgStream, String host, String user, Properties info) throws IOException, SQLException {
    // Now get the response from the backend, either an error message
    // or an authentication request
    String password = PGProperty.PASSWORD.get(info);
    /* SSPI negotiation state, if used */
    ISSPIClient sspiClient = null;
    try {
        authloop: while (true) {
            int beresp = pgStream.receiveChar();
            switch(beresp) {
                case 'E':
                    // An error occurred, so pass the error message to the
                    // user.
                    // 
                    // The most common one to be thrown here is:
                    // "User authentication failed"
                    // 
                    int l_elen = pgStream.receiveInteger4();
                    if (l_elen > 30000) {
                        // server, so trigger fallback.
                        throw new UnsupportedProtocolException();
                    }
                    ServerErrorMessage errorMsg = new ServerErrorMessage(pgStream.receiveErrorString(l_elen - 4));
                    LOGGER.log(Level.FINEST, " <=BE ErrorMessage({0})", errorMsg);
                    throw new PSQLException(errorMsg);
                case 'R':
                    // Authentication request.
                    // Get the message length
                    int l_msgLen = pgStream.receiveInteger4();
                    // Get the type of request
                    int areq = pgStream.receiveInteger4();
                    // Process the request.
                    switch(areq) {
                        case AUTH_REQ_CRYPT:
                            {
                                byte[] salt = pgStream.receive(2);
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " <=BE AuthenticationReqCrypt(salt=''{0}'')", new String(salt, "US-ASCII"));
                                }
                                if (password == null) {
                                    throw new PSQLException(GT.tr("The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED);
                                }
                                byte[] encodedResult = UnixCrypt.crypt(salt, password.getBytes("UTF-8"));
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " FE=> Password(crypt=''{0}'')", new String(encodedResult, "US-ASCII"));
                                }
                                pgStream.sendChar('p');
                                pgStream.sendInteger4(4 + encodedResult.length + 1);
                                pgStream.send(encodedResult);
                                pgStream.sendChar(0);
                                pgStream.flush();
                                break;
                            }
                        case AUTH_REQ_MD5:
                            {
                                byte[] md5Salt = pgStream.receive(4);
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " <=BE AuthenticationReqMD5(salt={0})", Utils.toHexString(md5Salt));
                                }
                                if (password == null) {
                                    throw new PSQLException(GT.tr("The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED);
                                }
                                byte[] digest = MD5Digest.encode(user.getBytes("UTF-8"), password.getBytes("UTF-8"), md5Salt);
                                if (LOGGER.isLoggable(Level.FINEST)) {
                                    LOGGER.log(Level.FINEST, " FE=> Password(md5digest={0})", new String(digest, "US-ASCII"));
                                }
                                pgStream.sendChar('p');
                                pgStream.sendInteger4(4 + digest.length + 1);
                                pgStream.send(digest);
                                pgStream.sendChar(0);
                                pgStream.flush();
                                break;
                            }
                        case AUTH_REQ_PASSWORD:
                            {
                                LOGGER.log(Level.FINEST, "<=BE AuthenticationReqPassword");
                                LOGGER.log(Level.FINEST, " FE=> Password(password=<not shown>)");
                                if (password == null) {
                                    throw new PSQLException(GT.tr("The server requested password-based authentication, but no password was provided."), PSQLState.CONNECTION_REJECTED);
                                }
                                byte[] encodedPassword = password.getBytes("UTF-8");
                                pgStream.sendChar('p');
                                pgStream.sendInteger4(4 + encodedPassword.length + 1);
                                pgStream.send(encodedPassword);
                                pgStream.sendChar(0);
                                pgStream.flush();
                                break;
                            }
                        case AUTH_REQ_GSS:
                        case AUTH_REQ_SSPI:
                            /*
                 * Use GSSAPI if requested on all platforms, via JSSE.
                 *
                 * For SSPI auth requests, if we're on Windows attempt native SSPI authentication if
                 * available, and if not disabled by setting a kerberosServerName. On other
                 * platforms, attempt JSSE GSSAPI negotiation with the SSPI server.
                 *
                 * Note that this is slightly different to libpq, which uses SSPI for GSSAPI where
                 * supported. We prefer to use the existing Java JSSE Kerberos support rather than
                 * going to native (via JNA) calls where possible, so that JSSE system properties
                 * etc continue to work normally.
                 *
                 * Note that while SSPI is often Kerberos-based there's no guarantee it will be; it
                 * may be NTLM or anything else. If the client responds to an SSPI request via
                 * GSSAPI and the other end isn't using Kerberos for SSPI then authentication will
                 * fail.
                 */
                            final String gsslib = PGProperty.GSS_LIB.get(info);
                            final boolean usespnego = PGProperty.USE_SPNEGO.getBoolean(info);
                            boolean useSSPI = false;
                            /*
                 * Use SSPI if we're in auto mode on windows and have a request for SSPI auth, or if
                 * it's forced. Otherwise use gssapi. If the user has specified a Kerberos server
                 * name we'll always use JSSE GSSAPI.
                 */
                            if (gsslib.equals("gssapi")) {
                                LOGGER.log(Level.FINE, "Using JSSE GSSAPI, param gsslib=gssapi");
                            } else if (areq == AUTH_REQ_GSS && !gsslib.equals("sspi")) {
                                LOGGER.log(Level.FINE, "Using JSSE GSSAPI, gssapi requested by server and gsslib=sspi not forced");
                            } else {
                                /* Determine if SSPI is supported by the client */
                                sspiClient = createSSPI(pgStream, PGProperty.SSPI_SERVICE_CLASS.get(info), /* Use negotiation for SSPI, or if explicitly requested for GSS */
                                areq == AUTH_REQ_SSPI || (areq == AUTH_REQ_GSS && usespnego));
                                useSSPI = sspiClient.isSSPISupported();
                                LOGGER.log(Level.FINE, "SSPI support detected: {0}", useSSPI);
                                if (!useSSPI) {
                                    /* No need to dispose() if no SSPI used */
                                    sspiClient = null;
                                    if (gsslib.equals("sspi")) {
                                        throw new PSQLException("SSPI forced with gsslib=sspi, but SSPI not available; set loglevel=2 for details", PSQLState.CONNECTION_UNABLE_TO_CONNECT);
                                    }
                                }
                                if (LOGGER.isLoggable(Level.FINE)) {
                                    LOGGER.log(Level.FINE, "Using SSPI: {0}, gsslib={1} and SSPI support detected", new Object[] { useSSPI, gsslib });
                                }
                            }
                            if (useSSPI) {
                                /* SSPI requested and detected as available */
                                sspiClient.startSSPI();
                            } else {
                                /* Use JGSS's GSSAPI for this request */
                                org.postgresql.gss.MakeGSS.authenticate(pgStream, host, user, password, PGProperty.JAAS_APPLICATION_NAME.get(info), PGProperty.KERBEROS_SERVER_NAME.get(info), usespnego);
                            }
                            break;
                        case AUTH_REQ_GSS_CONTINUE:
                            /*
                 * Only called for SSPI, as GSS is handled by an inner loop in MakeGSS.
                 */
                            sspiClient.continueSSPI(l_msgLen - 8);
                            break;
                        case AUTH_REQ_OK:
                            /* Cleanup after successful authentication */
                            LOGGER.log(Level.FINEST, " <=BE AuthenticationOk");
                            // We're done.
                            break authloop;
                        default:
                            LOGGER.log(Level.FINEST, " <=BE AuthenticationReq (unsupported type {0})", areq);
                            throw new PSQLException(GT.tr("The authentication type {0} is not supported. Check that you have configured the pg_hba.conf file to include the client''s IP address or subnet, and that it is using an authentication scheme supported by the driver.", areq), PSQLState.CONNECTION_REJECTED);
                    }
                    break;
                default:
                    throw new PSQLException(GT.tr("Protocol error.  Session setup failed."), PSQLState.PROTOCOL_VIOLATION);
            }
        }
    } finally {
        /* Cleanup after successful or failed authentication attempts */
        if (sspiClient != null) {
            try {
                sspiClient.dispose();
            } catch (RuntimeException ex) {
                LOGGER.log(Level.WARNING, "Unexpected error during SSPI context disposal", ex);
            }
        }
    }
}
Also used : ServerErrorMessage(org.postgresql.util.ServerErrorMessage) PSQLException(org.postgresql.util.PSQLException) ISSPIClient(org.postgresql.sspi.ISSPIClient)

Example 2 with ServerErrorMessage

use of org.postgresql.util.ServerErrorMessage in project molgenis by molgenis.

the class PostgreSqlExceptionTranslator method translateNotNullViolation.

/**
 * Package private for testability
 *
 * @param pSqlException PostgreSQL exception
 * @return translated validation exception
 */
MolgenisValidationException translateNotNullViolation(PSQLException pSqlException) {
    ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage();
    String tableName = serverErrorMessage.getTable();
    String message = serverErrorMessage.getMessage();
    Matcher matcher = Pattern.compile("null value in column \"?(.*?)\"? violates not-null constraint").matcher(message);
    boolean matches = matcher.matches();
    if (matches) {
        // exception message when adding data that does not match constraint
        String columnName = matcher.group(1);
        EntityTypeDescription entityTypeDescription = entityTypeRegistry.getEntityTypeDescription(tableName);
        entityTypeDescription.getAttributeDescriptionMap().get(columnName);
        ConstraintViolation constraintViolation = new ConstraintViolation(format("The attribute '%s' of entity '%s' can not be null.", getAttributeName(tableName, columnName), getEntityTypeName(tableName)), null);
        return new MolgenisValidationException(singleton(constraintViolation));
    } else {
        // exception message when applying constraint on existing data
        matcher = Pattern.compile("column \"(.*?)\" contains null values").matcher(message);
        matches = matcher.matches();
        if (!matches) {
            throw new RuntimeException("Error translating exception", pSqlException);
        }
        String columnName = matcher.group(1);
        ConstraintViolation constraintViolation = new ConstraintViolation(format("The attribute '%s' of entity '%s' contains null values.", getAttributeName(tableName, columnName), getEntityTypeName(tableName)), null);
        return new MolgenisValidationException(singleton(constraintViolation));
    }
}
Also used : Matcher(java.util.regex.Matcher) ServerErrorMessage(org.postgresql.util.ServerErrorMessage) ConstraintViolation(org.molgenis.data.validation.ConstraintViolation) EntityTypeDescription(org.molgenis.data.postgresql.identifier.EntityTypeDescription) MolgenisValidationException(org.molgenis.data.validation.MolgenisValidationException)

Example 3 with ServerErrorMessage

use of org.postgresql.util.ServerErrorMessage in project molgenis by molgenis.

the class PostgreSqlExceptionTranslator method translateUniqueKeyViolation.

/**
 * Package private for testability
 *
 * @param pSqlException PostgreSQL exception
 * @return translated validation exception
 */
MolgenisValidationException translateUniqueKeyViolation(PSQLException pSqlException) {
    ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage();
    String tableName = serverErrorMessage.getTable();
    String detailMessage = serverErrorMessage.getDetail();
    Matcher matcher = Pattern.compile("Key \\(\"?(.*?)\"?\\)=\\((.*?)\\) already exists.").matcher(detailMessage);
    boolean matches = matcher.matches();
    if (matches) {
        ConstraintViolation constraintViolation;
        // exception message when adding data that does not match constraint
        String[] columnNames = matcher.group(1).split(", ");
        if (columnNames.length == 1) {
            String columnName = columnNames[0];
            String value = matcher.group(2);
            constraintViolation = new ConstraintViolation(format("Duplicate value '%s' for unique attribute '%s' from entity '%s'.", value, getAttributeName(tableName, columnName), getEntityTypeName(tableName)), null);
        } else {
            String columnName = columnNames[columnNames.length - 1];
            String[] values = matcher.group(2).split(", ");
            String idValue = values[0];
            String value = values[1];
            constraintViolation = new ConstraintViolation(format("Duplicate list value '%s' for attribute '%s' from entity '%s' with id '%s'.", value, getAttributeName(tableName, columnName), getEntityTypeName(tableName), idValue), null);
        }
        return new MolgenisValidationException(singleton(constraintViolation));
    } else {
        // exception message when applying constraint on existing data
        matcher = Pattern.compile("Key \\(\"?(.*?)\"?\\)=\\((.*?)\\) is duplicated.").matcher(detailMessage);
        matches = matcher.matches();
        if (matches) {
            String columnName = matcher.group(1);
            String value = matcher.group(2);
            ConstraintViolation constraintViolation = new ConstraintViolation(format("The attribute '%s' of entity '%s' contains duplicate value '%s'.", getAttributeName(tableName, columnName), getEntityTypeName(tableName), value), null);
            return new MolgenisValidationException(singleton(constraintViolation));
        } else {
            LOG.error("Error translating postgres exception: ", pSqlException);
            throw new RuntimeException("Error translating exception", pSqlException);
        }
    }
}
Also used : Matcher(java.util.regex.Matcher) ServerErrorMessage(org.postgresql.util.ServerErrorMessage) ConstraintViolation(org.molgenis.data.validation.ConstraintViolation) MolgenisValidationException(org.molgenis.data.validation.MolgenisValidationException)

Example 4 with ServerErrorMessage

use of org.postgresql.util.ServerErrorMessage in project molgenis by molgenis.

the class PostgreSqlExceptionTranslator method getRefTableFromForeignKeyPsqlException.

private String getRefTableFromForeignKeyPsqlException(PSQLException pSqlException) {
    ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage();
    Matcher messageMatcher = Pattern.compile("update or delete on table \"(.*)\" violates foreign key constraint \"(.*)\" on table \"(.*)\"").matcher(serverErrorMessage.getMessage());
    if (!messageMatcher.matches()) {
        LOG.error("Error translating postgres exception: ", pSqlException);
        throw new RuntimeException("Error translating exception", pSqlException);
    }
    return messageMatcher.group(1);
}
Also used : Matcher(java.util.regex.Matcher) ServerErrorMessage(org.postgresql.util.ServerErrorMessage)

Example 5 with ServerErrorMessage

use of org.postgresql.util.ServerErrorMessage in project molgenis by molgenis.

the class PostgreSqlExceptionTranslator method translateUndefinedColumnException.

/**
 * Package private for testability
 *
 * @param pSqlException PostgreSQL exception
 * @return translated validation exception
 */
static MolgenisValidationException translateUndefinedColumnException(PSQLException pSqlException) {
    ServerErrorMessage serverErrorMessage = pSqlException.getServerErrorMessage();
    // FIXME exposes internal message
    String message = serverErrorMessage.getMessage();
    ConstraintViolation constraintViolation = new ConstraintViolation(message);
    return new MolgenisValidationException(singleton(constraintViolation));
}
Also used : ServerErrorMessage(org.postgresql.util.ServerErrorMessage) ConstraintViolation(org.molgenis.data.validation.ConstraintViolation) MolgenisValidationException(org.molgenis.data.validation.MolgenisValidationException)

Aggregations

ServerErrorMessage (org.postgresql.util.ServerErrorMessage)36 MolgenisValidationException (org.molgenis.data.validation.MolgenisValidationException)31 PSQLException (org.postgresql.util.PSQLException)28 Test (org.testng.annotations.Test)27 ConstraintViolation (org.molgenis.data.validation.ConstraintViolation)7 Matcher (java.util.regex.Matcher)6 EntityTypeDescription (org.molgenis.data.postgresql.identifier.EntityTypeDescription)2 String.format (java.lang.String.format)1 BatchUpdateException (java.sql.BatchUpdateException)1 SQLException (java.sql.SQLException)1 Collections.singleton (java.util.Collections.singleton)1 LinkedHashMap (java.util.LinkedHashMap)1 LinkedHashSet (java.util.LinkedHashSet)1 Map (java.util.Map)1 Objects.requireNonNull (java.util.Objects.requireNonNull)1 Set (java.util.Set)1 Pattern (java.util.regex.Pattern)1 Collectors.joining (java.util.stream.Collectors.joining)1 Collectors.toCollection (java.util.stream.Collectors.toCollection)1 DataSource (javax.sql.DataSource)1