Skip to content

Exception Invalid Column Type when reading Authorizationcode from Oracle DB #347

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
GrmpfNarf opened this issue Jul 13, 2021 · 6 comments
Closed
Assignees
Labels
status: invalid An issue that we don't feel is valid

Comments

@GrmpfNarf
Copy link

Hello everbody,
I get an exception when I try to obtain an Accesstoken from an Authorizationcode. In the moment when the JdbcOAuth2AuthorizationService tries to read the Authorizration from the table an exception occours.

org.springframework.jdbc.UncategorizedSQLException: PreparedStatementCallback; uncategorized SQLException for SQL [SELECT id, registered_client_id, principal_name, authorization_grant_type, attributes, state, authorization_code_value, authorization_code_issued_at, authorization_code_expires_at,authorization_code_metadata,access_token_value,access_token_issued_at,access_token_expires_at,access_token_metadata,access_token_type,access_token_scopes,oidc_id_token_value,oidc_id_token_issued_at,oidc_id_token_expires_at,oidc_id_token_metadata,refresh_token_value,refresh_token_issued_at,refresh_token_expires_at,refresh_token_metadata FROM oauth2_authorization WHERE authorization_code_value = ?]; SQL state [99999]; error code [17004]; Ungültiger Spaltentyp; nested exception is java.sql.SQLException: Ungültiger Spaltentyp at org.springframework.jdbc.core.JdbcTemplate.translateException(JdbcTemplate.java:1542) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:667) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:713) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:744) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:799) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService.findBy(JdbcOAuth2AuthorizationService.java:257) ~[spring-security-oauth2-authorization-server-0.1.2.jar:0.1.2]

Translation Ungültiger Spaltentyp = Invalid Column Type

The error occours when Oracle tries to cast the Authorizationcode (as byte[]) into an oracle.jdbc.internal.OracleBlob.

I hope somebody can help me.

Metadata:
Spring Boot Version: 2.5.2
Authorization Server Version: 0.1.2
Oracle Dependency: com.oracle.database.jdbc:ojdbc10:19.11.0.0

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label Jul 13, 2021
@GrmpfNarf
Copy link
Author

GrmpfNarf commented Jul 14, 2021

I have investigated a little bit further and it seems that there is a problem with filtering on LOBs (CLOB and BLOB) in Oracle.
I've writen a custom JdbcOauth2AuthorizationService which uses JPA Data. And even there I got an issue when I try to filter by a LOB.
ORA-00932: inconsistent datatypes: expected - got BLOB

In the "old" Spring Security TokenStore it works because the filter does not uses the BLOB as filter instead an extra ID column is generated which is a VARCHAR.

Hope it helps

@bibibiu2017
Copy link
Contributor

Hi,
I am also getting a similar error when using JdbcOAuth2AuthorizationService with mariadb.
Caused by: java.sql.SQLSyntaxErrorException: (conn=76) Can only convert a byte[] to BINARY, VARBINARY or LONGVARBINARY at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.createException(ExceptionFactory.java:62) ~[mariadb-java-client-2.7.3.jar:na] at org.mariadb.jdbc.internal.util.exceptions.ExceptionFactory.create(ExceptionFactory.java:173) ~[mariadb-java-client-2.7.3.jar:na] at org.mariadb.jdbc.BasePrepareStatement.setInternalObject(BasePrepareStatement.java:1084) ~[mariadb-java-client-2.7.3.jar:na] at org.mariadb.jdbc.BasePrepareStatement.setObject(BasePrepareStatement.java:836) ~[mariadb-java-client-2.7.3.jar:na] at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.setObject(HikariProxyPreparedStatement.java) ~[HikariCP-4.0.3.jar:na] at org.springframework.jdbc.core.StatementCreatorUtils.setValue(StatementCreatorUtils.java:414) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValueInternal(StatementCreatorUtils.java:231) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.StatementCreatorUtils.setParameterValue(StatementCreatorUtils.java:146) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.ArgumentPreparedStatementSetter.doSetValue(ArgumentPreparedStatementSetter.java:66) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.ArgumentPreparedStatementSetter.setValues(ArgumentPreparedStatementSetter.java:50) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.JdbcTemplate$1.doInPreparedStatement(JdbcTemplate.java:720) ~[spring-jdbc-5.3.8.jar:5.3.8] at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:651) ~[spring-jdbc-5.3.8.jar:5.3.8] ... 110 common frames omitted

@sjohnr
Copy link
Contributor

sjohnr commented Jul 19, 2021

@GrmpfNarf, as mentioned in this comment:

It is very difficult to provide an implementation that works out of the box for all databases. This implementation strives to use standard sql datatypes and is a simplified JDBC implementation. However, it is designed to be customizable so user's can provide customizations for database vendors that deviate from the standard sql types.

Take a look at JdbcOAuth2AuthorizationServiceTests.tableDefinitionWhenCustomThenAbleToOverride(), which provides a test on how to override the table definition altogether. There are tests in that class that demonstrate how to customize further. Additionally, your approach of switching to JPA would also be a good option, as it easily allows you to override the table definition for your database vendor.

I'm going to close this for now, but if you are still having issues after you have tried out the proposed solution we can re-open and discuss further.

@sjohnr sjohnr closed this as completed Jul 19, 2021
@sjohnr sjohnr added status: invalid An issue that we don't feel is valid and removed status: waiting-for-triage An issue we've not yet triaged labels Jul 19, 2021
@GrmpfNarf
Copy link
Author

Hi,
I've tried your solution and had the same issues and then I started digging.
The main problem is that Oracle does not allow condition comparision on LOBs.
Documentation

Which means as long you are using LOBs to filter the tokens there will be no Oracle support.

I have customized the JdbcOAuth2AuthorizationService to work with Oracle.

import org.springframework.jdbc.core.ArgumentPreparedStatementSetter;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.SqlParameterValue;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.security.oauth2.core.OAuth2TokenType;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.nio.charset.StandardCharsets;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.List;

@Service
public class CustomJdbcOauth2AuthorizationService extends JdbcOAuth2AuthorizationService {

    private final JdbcOperations jdbcOperations;

    private static final String TABLE_NAME = "oauth2_authorization";

    private static final String COLUMN_NAMES = "id, "
            + "registered_client_id, "
            + "principal_name, "
            + "authorization_grant_type, "
            + "attributes, "
            + "state, "
            + "authorization_code_value, "
            + "authorization_code_issued_at, "
            + "authorization_code_expires_at,"
            + "authorization_code_metadata,"
            + "access_token_value,"
            + "access_token_issued_at,"
            + "access_token_expires_at,"
            + "access_token_metadata,"
            + "access_token_type,"
            + "access_token_scopes,"
            + "oidc_id_token_value,"
            + "oidc_id_token_issued_at,"
            + "oidc_id_token_expires_at,"
            + "oidc_id_token_metadata,"
            + "refresh_token_value,"
            + "refresh_token_issued_at,"
            + "refresh_token_expires_at,"
            + "refresh_token_metadata";

    private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR dbms_lob.compare(authorization_code_value,?) = 0 OR " +
            "dbms_lob.compare(access_token_value,?) = 0 OR dbms_lob.compare(refresh_token_value,?) = 0";
    private static final String STATE_FILTER = "state = ?";
    private static final String AUTHORIZATION_CODE_FILTER = "dbms_lob.compare(authorization_code_value,?) = 0";
    private static final String ACCESS_TOKEN_FILTER = "dbms_lob.compare(access_token_value,?) = 0";
    private static final String REFRESH_TOKEN_FILTER = "dbms_lob.compare(refresh_token_value,?) = 0";
    private static final String LOAD_AUTHORIZATION_SQL = "SELECT " + COLUMN_NAMES
            + " FROM " + TABLE_NAME
            + " WHERE ";


    public CustomJdbcOauth2AuthorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) {
        super(jdbcOperations, registeredClientRepository);
        this.jdbcOperations = jdbcOperations;
    }

    @Override
    public OAuth2Authorization findByToken(String token, OAuth2TokenType tokenType) {
        Assert.hasText(token, "token cannot be empty");
        List<SqlParameterValue> parameters = new ArrayList<>();
        if (tokenType == null) {
            parameters.add(new SqlParameterValue(Types.VARCHAR, token));
            parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8)));
            parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8)));
            parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8)));
            return findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters);
        } else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
            parameters.add(new SqlParameterValue(Types.VARCHAR, token.getBytes(StandardCharsets.UTF_8)));
            return findBy(STATE_FILTER, parameters);
        } else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
            parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8)));
            return findBy(AUTHORIZATION_CODE_FILTER, parameters);
        } else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
            parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8)));
            return findBy(ACCESS_TOKEN_FILTER, parameters);
        } else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
            parameters.add(new SqlParameterValue(Types.BLOB, token.getBytes(StandardCharsets.UTF_8)));
            return findBy(REFRESH_TOKEN_FILTER, parameters);
        }
        return null;
    }

    private OAuth2Authorization findBy(String filter, List<SqlParameterValue> parameters) {
        PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(getLobHandler().getLobCreator(), parameters.toArray());
        List<OAuth2Authorization> result = this.jdbcOperations.query(LOAD_AUTHORIZATION_SQL + filter, pss, getAuthorizationRowMapper());
        return !result.isEmpty() ? result.get(0) : null;
    }

    private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
        private final LobCreator lobCreator;

        private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {
            super(args);
            this.lobCreator = lobCreator;
        }

        @Override
        protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
            if (argValue instanceof SqlParameterValue) {
                SqlParameterValue paramValue = (SqlParameterValue) argValue;
                if (paramValue.getSqlType() == Types.BLOB) {
                    if (paramValue.getValue() != null) {
                        Assert.isInstanceOf(byte[].class, paramValue.getValue(),
                                "Value of blob parameter must be byte[]");
                    }
                    byte[] valueBytes = (byte[]) paramValue.getValue();
                    this.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes);
                    return;
                }
            }
            super.doSetValue(ps, parameterPosition, argValue);
        }

    }
}

In this class are only 2 changes to the original:

  1. In the findBy Method instead of the ArgumentPreparedStatementSetter the LobCreatorArgumentPreparedStatementSetter is used. This is necessary to convert the byte[] into in BLOB otherwise the OracleDriver cannot cast to the correct type and you get the original error of this post Invalid Column Type
  2. You cannot filter on LOB in Oracle directly like lob_field = someValue. You have to use dbms_lob.compare(lob_field ,someValue) = 0 which I have done in the FILTER constants.

The rest of the class is like the original.

For this I have some enhancement suggestions:

  1. Make method findBy protected so we can customize it directly and don't need to copy findyByToken
  2. Make PreparedStatementSetter customizable or use LobCreatorArgumentPreparedStatementSetter which is already defined twice in JdbcOAuth2AuthorizationService and JdbcOAuth2AuthorizedClientService
  3. Make filter statements customizable so we don't have to copy the other statement constants

With this 3 enhancements my customization will only be the filter and maybe another PreparedStatementSetter.

@cucosion
Copy link

`import org.springframework.jdbc.core.*;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@service
public class CustomJdbcOauth2AuthorizationService extends JdbcOAuth2AuthorizationService {

// @formatter:off
private static final String COLUMN_NAMES = "id, "
        + "registered_client_id, "
        + "principal_name, "
        + "authorization_grant_type, "
        + "authorized_scopes, "
        + "attributes, "
        + "state, "
        + "authorization_code_value, "
        + "authorization_code_issued_at, "
        + "authorization_code_expires_at,"
        + "authorization_code_metadata,"
        + "access_token_value,"
        + "access_token_issued_at,"
        + "access_token_expires_at,"
        + "access_token_metadata,"
        + "access_token_type,"
        + "access_token_scopes,"
        + "oidc_id_token_value,"
        + "oidc_id_token_issued_at,"
        + "oidc_id_token_expires_at,"
        + "oidc_id_token_metadata,"
        + "refresh_token_value,"
        + "refresh_token_issued_at,"
        + "refresh_token_expires_at,"
        + "refresh_token_metadata,"
        + "user_code_value,"
        + "user_code_issued_at,"
        + "user_code_expires_at,"
        + "user_code_metadata,"
        + "device_code_value,"
        + "device_code_issued_at,"
        + "device_code_expires_at,"
        + "device_code_metadata";
// @formatter:on

private static Map<String, CustomJdbcOauth2AuthorizationService.ColumnMetadata> columnMetadataMap;

private final JdbcOperations jdbcOperations;

private static final String TABLE_NAME = "oauth2_authorization";

private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR dbms_lob.compare(authorization_code_value,?) = 0 OR " +
        "dbms_lob.compare(access_token_value,?) = 0 OR dbms_lob.compare(refresh_token_value,?) = 0 OR dbms_lob.compare(user_code_value,?) = 0 OR "
        + "dbms_lob.compare(device_code_value,?) = 0";

private static final String STATE_FILTER = "state = ?";
private static final String AUTHORIZATION_CODE_FILTER = "dbms_lob.compare(authorization_code_value,?) = 0";
private static final String ACCESS_TOKEN_FILTER = "dbms_lob.compare(access_token_value,?) = 0";
private static final String ID_TOKEN_FILTER = "dbms_lob.compare(oidc_id_token_value,?) = 0";
private static final String REFRESH_TOKEN_FILTER = "dbms_lob.compare(refresh_token_value,?) = 0";
private static final String USER_CODE_FILTER = "dbms_lob.compare(user_code_value,?) = 0";
private static final String DEVICE_CODE_FILTER = "dbms_lob.compare(device_code_value,?) = 0";
private static final String LOAD_AUTHORIZATION_SQL = "SELECT " + COLUMN_NAMES
        + " FROM " + TABLE_NAME
        + " WHERE ";


public CustomJdbcOauth2AuthorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) {
    super(jdbcOperations, registeredClientRepository);
    this.jdbcOperations = jdbcOperations;
    initColumnMetadata(jdbcOperations);
}

@Nullable
@Override
public OAuth2Authorization findByToken(String token, @Nullable OAuth2TokenType tokenType) {
    Assert.hasText(token, "token cannot be empty");
    List<SqlParameterValue> parameters = new ArrayList<>();
    if (tokenType == null) {
        parameters.add(new SqlParameterValue(Types.VARCHAR, token));
        parameters.add(mapToSqlParameter("authorization_code_value", token));
        parameters.add(mapToSqlParameter("access_token_value", token));
        parameters.add(mapToSqlParameter("oidc_id_token_value", token));
        parameters.add(mapToSqlParameter("refresh_token_value", token));
        parameters.add(mapToSqlParameter("user_code_value", token));
        parameters.add(mapToSqlParameter("device_code_value", token));
        return findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters);
    } else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) {
        parameters.add(new SqlParameterValue(Types.VARCHAR, token));
        return findBy(STATE_FILTER, parameters);
    } else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) {
        parameters.add(mapToSqlParameter("authorization_code_value", token));
        return findBy(AUTHORIZATION_CODE_FILTER, parameters);
    } else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) {
        parameters.add(mapToSqlParameter("access_token_value", token));
        return findBy(ACCESS_TOKEN_FILTER, parameters);
    } else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) {
        parameters.add(mapToSqlParameter("oidc_id_token_value", token));
        return findBy(ID_TOKEN_FILTER, parameters);
    } else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) {
        parameters.add(mapToSqlParameter("refresh_token_value", token));
        return findBy(REFRESH_TOKEN_FILTER, parameters);
    } else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) {
        parameters.add(mapToSqlParameter("user_code_value", token));
        return findBy(USER_CODE_FILTER, parameters);
    } else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) {
        parameters.add(mapToSqlParameter("device_code_value", token));
        return findBy(DEVICE_CODE_FILTER, parameters);
    }
    return null;
}

private OAuth2Authorization findBy(String filter, List<SqlParameterValue> parameters) {
    PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(getLobHandler().getLobCreator(), parameters.toArray());
    List<OAuth2Authorization> result = this.jdbcOperations.query(LOAD_AUTHORIZATION_SQL + filter, pss, getAuthorizationRowMapper());
    return !result.isEmpty() ? result.get(0) : null;
}

private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter {
    private final LobCreator lobCreator;

    private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) {
        super(args);
        this.lobCreator = lobCreator;
    }

    @Override
    protected void doSetValue(PreparedStatement ps, int parameterPosition, Object argValue) throws SQLException {
        if (argValue instanceof SqlParameterValue) {
            SqlParameterValue paramValue = (SqlParameterValue) argValue;
            if (paramValue.getSqlType() == Types.BLOB) {
                if (paramValue.getValue() != null) {
                    Assert.isInstanceOf(byte[].class, paramValue.getValue(),
                            "Value of blob parameter must be byte[]");
                }
                byte[] valueBytes = (byte[]) paramValue.getValue();
                this.lobCreator.setBlobAsBytes(ps, parameterPosition, valueBytes);
                return;
            }
        }
        super.doSetValue(ps, parameterPosition, argValue);
    }

}

private static void initColumnMetadata(JdbcOperations jdbcOperations) {
    columnMetadataMap = new HashMap<>();
    CustomJdbcOauth2AuthorizationService.ColumnMetadata columnMetadata;

    columnMetadata = getColumnMetadata(jdbcOperations, "attributes", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "authorization_code_value", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "authorization_code_metadata", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "access_token_value", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "access_token_metadata", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "oidc_id_token_value", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "oidc_id_token_metadata", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "refresh_token_value", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "refresh_token_metadata", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "user_code_value", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "user_code_metadata", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "device_code_value", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
    columnMetadata = getColumnMetadata(jdbcOperations, "device_code_metadata", Types.BLOB);
    columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata);
}
private static CustomJdbcOauth2AuthorizationService.ColumnMetadata getColumnMetadata(JdbcOperations jdbcOperations, String columnName, int defaultDataType) {
    Integer dataType = jdbcOperations.execute((ConnectionCallback<Integer>) conn -> {
        DatabaseMetaData databaseMetaData = conn.getMetaData();
        ResultSet rs = databaseMetaData.getColumns(null, null, TABLE_NAME, columnName);
        if (rs.next()) {
            return rs.getInt("DATA_TYPE");
        }
        // NOTE: (Applies to HSQL)
        // When a database object is created with one of the CREATE statements or renamed with the ALTER statement,
        // if the name is enclosed in double quotes, the exact name is used as the case-normal form.
        // But if it is not enclosed in double quotes,
        // the name is converted to uppercase and this uppercase version is stored in the database as the case-normal form.
        rs = databaseMetaData.getColumns(null, null, TABLE_NAME.toUpperCase(), columnName.toUpperCase());
        if (rs.next()) {
            return rs.getInt("DATA_TYPE");
        }
        return null;
    });
    return new CustomJdbcOauth2AuthorizationService.ColumnMetadata(columnName, dataType != null ? dataType : defaultDataType);
}
private static final class ColumnMetadata {
    private final String columnName;
    private final int dataType;

    private ColumnMetadata(String columnName, int dataType) {
        this.columnName = columnName;
        this.dataType = dataType;
    }

    private String getColumnName() {
        return this.columnName;
    }

    private int getDataType() {
        return this.dataType;
    }

}
private static SqlParameterValue mapToSqlParameter(String columnName, String value) {
    ColumnMetadata columnMetadata = columnMetadataMap.get(columnName);
    return Types.BLOB == columnMetadata.getDataType() && StringUtils.hasText(value) ?
            new SqlParameterValue(Types.BLOB, value.getBytes(StandardCharsets.UTF_8)) :
            new SqlParameterValue(columnMetadata.getDataType(), value);
}

}`

@gordonhjko
Copy link

gordonhjko commented Sep 30, 2023

I copied the source code above from cucosion and apply to SpringBoot Authorization 1.1.2 https://github.com/spring-projects/spring-authorization-server.

It works well except the UNKNOWN_TOKEN_TYPE_FILTER is missing oidc_id_token_value as a condition

It is surprised that this project, with its out-of-box configuration, is not ready for Oracle. Even worse is there is no any note or document to tell developers the Oracle is not available.

Really appreciate the source code from cucosion. Also hope Apache can have a better quality control for projects under its name

Sept 29, 2023

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: invalid An issue that we don't feel is valid
Projects
None yet
Development

No branches or pull requests

6 participants