Spring Security JDBC authentication default schema error when using PostgreSQL - spring

Is it really impossible to use default schema for Spring Security with PostgreSQL, because the part "varchar_ignorecase" does not exist can't be replaced?
I'm just testing the default settings:
auth.jdbcAuthentication()
.dataSource(dataSource)
.withDefaultSchema();
And below is the error:
Caused by:
org.springframework.beans.factory.BeanDefinitionStoreException:
Factory method [public javax.servlet.Filter
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration.springSecurityFilterChain()
throws java.lang.Exception] threw exception;
nested exception is
org.springframework.jdbc.datasource.init.ScriptStatementFailedException:
Failed to execute SQL script statement at line 1 of resource class
path resource
[org/springframework/security/core/userdetails/jdbc/users.ddl]: create
table users(username varchar_ignorecase(50) not null primary
key,password varchar_ignorecase(500) not null,enabled boolean not
null);
nested exception is
org.postgresql.util.PSQLException: ERROR:
type "varchar_ignorecase" does not exist

Cause the default schema is not suitable for PostgreSQL, I need to create my own schema and implement own query for user and authority query.
auth.jdbcAuthentication()
.dataSource(dataSource)
.usersByUsernameQuery(
"select username,password, enabled from users where username=?")
.authoritiesByUsernameQuery(
"select username, role from user_roles where username=?");

There is no need to adding withDefaultSchema(). simply define your schema.sql and data.sql with proper column names and foreign key constraints. Following is the example configuration for Mysql (in the Spring Boot).
schema.sql
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS authorities;
CREATE TABLE users
(
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
enabled TINYINT NOT NULL DEFAULT 1,
PRIMARY KEY (username)
);
CREATE TABLE authorities
(
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (username) REFERENCES users (username)
);
Then define users and authorities (data.sql)
INSERT INTO users (username, password, enabled)
values ('user','pass',1);
INSERT INTO authorities (username, authority)
values ('user', 'ROLE_USER');
finally, define the authentication mechanism
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.jdbcAuthentication()
.passwordEncoder(NoOpPasswordEncoder.getInstance())
.dataSource(dataSource);
}

Schema for PGSQL:
schema.sql
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS authorities;
CREATE TABLE users
(
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
enabled INT NOT NULL DEFAULT 1,
PRIMARY KEY (username)
);
CREATE TABLE authorities
(
username VARCHAR(50) NOT NULL,
authority VARCHAR(50) NOT NULL,
FOREIGN KEY (username) REFERENCES users (username)
);
User injection on data.sql
INSERT INTO users (username, password, enabled)
values ('user','pass',1);
INSERT INTO authorities (username, authority)
values ('user', 'ROLE_USER');
Thanks to Mr.Q. I totally used your MySQL schema and adapted it for PGSQL.

Related

Cannot get many to many relationship in spring bootstrap

I have a very simple many to many scenario: One ORDER has many PRODUCT, and each product can belong to many orders.
order :
#Entity
#Table(name = "ORDER")
public class OrderEntity {
#Id
#Column(name="ORDER_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="ORDER_NAME")
private String name;
#Column(name="ORDER_DATE")
private Date date;
#ManyToMany
private List<ProductEntity> selectedProducts = new ArrayList<>();
product:
#Entity
#Table(name = "PRODUCT")
public class ProductEntity {
#Id
#Column(name="PRODUCT_ID")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="PRODUCT_NAME")
private String name;
#Column(name="PRODUCT_PRICE")
private BigDecimal price;
#ManyToMany
private List<OrderEntity> orders = new ArrayList<>();
(removed getters and setters and constructors for brevity)
However when I startup bootstrap then I get a whole host of errors :
Error executing DDL "drop table order if exists" via JDBC Statement
Syntax error in SQL statement "DROP TABLE ORDER[*] IF EXISTS "; expected "identifier"; SQL statement:
Error executing DDL "create table order (order_id bigint not null, order_date timestamp, order_name varchar(255), primary key (order_id))" via JDBC Statement
Syntax error in SQL statement "CREATE TABLE ORDER[*] (ORDER_ID BIGINT NOT NULL, ORDER_DATE TIMESTAMP, ORDER_NAME VARCHAR(255), PRIMARY KEY (ORDER_ID)) "; expected "identifier"; SQL statement:
create table order (order_id bigint not null, order_date timestamp, order_name varchar(255), primary key (order_id)) [42001-199]
Error executing DDL "alter table order_selected_products add constraint FKrbll8c9ubhjqangdfw2sgkurw foreign key (order_entity_order_id) references order" via JDBC Statement
Syntax error in SQL statement "ALTER TABLE ORDER_SELECTED_PRODUCTS ADD CONSTRAINT FKRBLL8C9UBHJQANGDFW2SGKURW FOREIGN KEY (ORDER_ENTITY_ORDER_ID) REFERENCES ORDER[*] "; expected "identifier"; SQL statement:
alter table order_selected_products add constraint FKrbll8c9ubhjqangdfw2sgkurw foreign key (order_entity_order_id) references order [42001-199]
Error executing DDL "alter table product_orders add constraint FK9pa3r9u6x44jjxrkkhdvhu23k foreign key (orders_order_id) references order" via JDBC Statement
Syntax error in SQL statement "ALTER TABLE PRODUCT_ORDERS ADD CONSTRAINT FK9PA3R9U6X44JJXRKKHDVHU23K FOREIGN KEY (ORDERS_ORDER_ID) REFERENCES ORDER[*] "; expected "identifier"; SQL statement:
alter table product_orders add constraint FK9pa3r9u6x44jjxrkkhdvhu23k foreign key (orders_order_id) references order [42001-199]
I'm not sure why there are these syntax errors. Is this some kind of SQL dialect issue?
ORDER is a very common reserved keyword and that is the root cause of the errors you see.
Change your table name to something else, such as ORDERS, or if you really want to use that name you can try escaping it:
#Entity
#Table(name = "\"ORDERS\"")
public class OrderEntity {
....
}
List of reserved keywords for some common databases:
https://docs.oracle.com/cd/B28359_01/appdev.111/b31231/appb.htm#BABDFFBA
https://learn.microsoft.com/en-us/sql/t-sql/language-elements/reserved-keywords-transact-sql?view=sql-server-2017
https://www.postgresql.org/docs/current/sql-keywords-appendix.html
https://dev.mysql.com/doc/refman/8.0/en/keywords.html

Bidirectional mapping not working since it's not saving correctly

I am new to Spring and I am testing the API on postman and the instructor_id is not saving.
This is how I am testing it on postman and don't know if it is right or not.
{
"coursename":"tesasdasdast",
"description":"tesdasdsadasting",
"userEntity":{
"instructor_id":1
}
}
Steps:
I have two entities:
UserEntity
CourseEntity
I have a controller where I'm saving the information called:
CourseController
I have a repository called CourseRepo and I am extending JpaRepository
In CourseEntity:
#ManyToOne
#JoinColumn(name = "instructor_id")
private UserEntity userEntity;
In UserEntity:
#OneToMany(mappedBy = "userEntity", cascade=CascadeType.ALL)
private List<CourseEntity> courseEntity;
In my CourseController:
#PostMapping("/courses")
void addCourse(#RequestBody CourseEntity course) {
courseRepository.save(course);
}
I have added in my pom.xml:
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
</dependency>
This is the error message that I'm getting in postman:
{
"timestamp": "2019-10-10T16:51:47.780+0000",
"status": 500,
"error": "Internal Server Error",
"message": "org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.finaly.projectback.entity.CourseEntity.userEntity -> com.finaly.projectback.entity.UserEntity; nested exception is java.lang.IllegalStateException: org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.finaly.projectback.entity.CourseEntity.userEntity -> com.finaly.projectback.entity.UserEntity",
"path": "/project/courses"
}
And these two are the tables in the database:
create table web_user(
id INT primary key auto_increment,
firstname VARCHAR(50),
lastname VARCHAR(50),
username VARCHAR(50),
email VARCHAR(50),
pwd VARCHAR(50),
user_role_id INT,
FOREIGN KEY (user_role_id) REFERENCES web_user_role(id)
);
CREATE TABLE web_course (
id INT PRIMARY KEY AUTO_INCREMENT,
coursename VARCHAR(50),
instructor_id INT,
FOREIGN KEY (instructor_id) REFERENCES web_user(id)
);
Thank you in advance for any advice.
Please check if you are correctly setting entities in associations.
userEntity.setCourseEntity and courseEntity.setUserEntity
Do you have this code in place and then check your hibernate queries that are fired on save method call.
I found my mistake. Instead of creating the foreign key as user_entity_user_id, i was creating it as user_id.
It seems like a stupid mistake but I'm still new to Spring and for now, many things look like magic.
Thanks everyone who tried to help.

Spring Boot 2.1.1 Issue Accessing Tables in PostgreSQL that are within Oauth2

I am trying to add postgresql support for OAuth2 with Spring Boot after following some examples to clean up my code.
The problem is that I have to add a schema manually to my own schema scripts for the database to create despite specifying certain properties in the latest Spring Boot. Only after doing so do the tables become recognizable. Also, OAuth2 continues to fail, not finding any table. The issue seems schema related.
I was able to get my own schema to be recognized without specifying one in the entity by disabling auto configuration but cannot figure out why OAuth2 tables are not found unless I use the public schema.
I have added the following properties file with attempts to get Spring boot to recognize my schemas:
server.port=8081
## Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
# The SQL dialect makes Hibernate generate better SQL for the chosen database
# Hibernate ddl auto (create, create-drop, validate, update)
spring.datasource.jdbcUrl=jdbc:postgresql://xxxxxx:xxx/oauth2
spring.datasource.username= xxxxx
spring.datasource.password= xxxxx
spring.datasource.hikari.minimumIdle=5
spring.datasource.hikari.maximumPoolSize=20
spring.datasource.hikari.idleTimeout=30000
spring.datasource.hikari.poolName=SpringBootJPAHikariCP
spring.datasource.hikari.maxLifetime=2000000
spring.datasource.hikari.connectionTimeout=30000
spring.jpa.properties.hibernate.jdbc.lob.non_contextual_creation=true;
spring.jpa.properties.hibernate.current_session_context_class=thread
spring.jpa.hibernate.ddl-auto=none
spring.datasource.tomcat.default-auto-commit=true
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.PostgreSQL95Dialect
spring.jpa.hibernate.connection.provider_class=org.hibernate.hikaricp.internal.HikariCPConnectionProvider
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql=true
spring.jpa.properties.hibernate.id.new_generator_mappings=false
spring.jpa.properties.hibernate.search.autoregister_listeners=false
spring.jpa.properties.hibernate.bytecode.use_reflection_optimizer=false
spring.jpa.properties.hibernate.default_schema = public
# spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
# spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
# Hibernate ddl auto (create, create-drop, validate, update)
spring.thymeleaf.templateResolverOrder=1
logging.level.org.springframework=INFO
entitymanager.packagesToScan = com.si.model
logging.level.org.springframework.web=DEBUG
logging.level.guru.springframework.controllers=DEBUG
logging.level.org.hibernate=INFO
spring.main.allow-bean-definition-overriding=true
spring.thymeleaf.cache=false
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
spring.datasource.schema=classpath:schema.sql
spring.datasource.initialization-mode=always
spring.jpa.properties.hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.template.cache: false
spring.jpa.properties.hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
I have also tried setting the Data Source for each object in the Authorization Server:
#Configuration
#EnableAuthorizationServer
class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
#Bean
#ConfigurationProperties(prefix = "spring.datasource")
def oauthDataSource: DataSource = DataSourceBuilder.create.build
#Autowired
private var authenticationManager: AuthenticationManager = null
#Bean
def clientDetailsService = new JdbcClientDetailsService(oauthDataSource)
#Bean
def tokenStore = new JdbcTokenStore(oauthDataSource)
#Bean
def approvalStore = new JdbcApprovalStore(oauthDataSource)
#Bean
def authorizationCodeServices = new JdbcAuthorizationCodeServices(oauthDataSource)
#Bean
def passwordEncoder(): Pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder()
#throws[Exception]
override def configure(clients: ClientDetailsServiceConfigurer): Unit = {
clients.jdbc(oauthDataSource)
}
#throws[Exception]
override def configure(oauthServer: AuthorizationServerSecurityConfigurer): Unit = {
oauthServer.passwordEncoder(passwordEncoder)
}
#throws[Exception]
override def configure(endpoints: AuthorizationServerEndpointsConfigurer): Unit = {
endpoints
.authenticationManager(authenticationManager)
.approvalStore(approvalStore)
.authorizationCodeServices(authorizationCodeServices)
.tokenStore(tokenStore)
}
}
Finally, I set up my web configuration as follows:
class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private var passwordEncoder: Pbkdf2PasswordEncoder = null
#Bean
#Primary
#ConfigurationProperties(prefix = "spring.datasource")
def dataSource: DataSource = DataSourceBuilder.create.`type`(classOf[HikariDataSource]).build
#Bean
#throws[Exception]
override def authenticationManagerBean: AuthenticationManager = super.authenticationManagerBean
#Bean
#throws[Exception]
override def userDetailsServiceBean = new JdbcUserDetails()
#throws[Exception]
override def configure(web: WebSecurity): Unit = {
web.ignoring.antMatchers("/webjars/**", "/resources/**")
}
#throws[Exception]
override protected def configure(http: HttpSecurity): Unit = {
http
.csrf().disable()
.authorizeRequests
.antMatchers("/login", "/logout.do")
.permitAll
.antMatchers("/**")
.authenticated
.and.
formLogin
.loginProcessingUrl("/login.do")
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/login")
.and
.logout
.logoutRequestMatcher(new AntPathRequestMatcher("/logout.do"))
.and
.userDetailsService(userDetailsServiceBean)
}
#throws[Exception]
override protected def configure(auth: AuthenticationManagerBuilder): Unit = {
auth
.userDetailsService(userDetailsServiceBean)
.passwordEncoder(passwordEncoder)
}
}
For some reason, I had to set my schemas to public in the schema script:
drop table if exists public.oauth_client_token cascade;
create table if not exists public.oauth_client_token (
token_id VARCHAR(255) not null primary key,
token bytea,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255)
);
drop table if exists public.oauth_client_details cascade;
CREATE TABLE if not exists public.oauth_client_details (
client_id varchar(255) NOT NULL primary key,
resource_ids varchar(255) DEFAULT NULL,
client_secret varchar(255) DEFAULT NULL,
scope varchar(255) DEFAULT NULL,
authorized_grant_types varchar(255) DEFAULT NULL,
web_server_redirect_uri varchar(255) DEFAULT NULL,
authorities varchar(255) DEFAULT NULL,
access_token_validity integer,
refresh_token_validity integer,
additional_information varchar(255) DEFAULT NULL,
autoapprove varchar(255) DEFAULT NULL
);
drop table if exists public.oauth_access_token cascade;
create table if not exists public.oauth_access_token(
token_id VARCHAR(255),
token bytea,
authentication_id VARCHAR(255),
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication bytea,
refresh_token VARCHAR(255)
);
drop table if exists public.oauth_refresh_token cascade;
create table if not exists public.oauth_refresh_token(
token_id VARCHAR(255),
token bytea,
authentication bytea
);
drop table if exists public.authorities cascade;
CREATE TABLE if not exists public.authorities (
id bigserial not null primary key,
authority varchar(255)
);
drop table if exists public.credentials cascade;
CREATE TABLE if not exists public.credentials (
id bigserial not null primary key,
enabled boolean not null,
name varchar(255) not null,
password varchar(255) not null,
version integer
);
drop table if exists public.credentials_authorities cascade;
CREATE TABLE if not exists public.credentials_authorities (
credentials_id bigint not null,
authorities_id bigint not null
);
drop table if exists public.oauth_code cascade;
create table if not exists public.oauth_code (
code VARCHAR(255),
authentication bytea
);
drop table if exists public.oauth_approvals cascade;
create table if not exists public.oauth_approvals (
userId VARCHAR(255),
clientId VARCHAR(255),
scope VARCHAR(255),
status VARCHAR(10),
expiresAt timestamp without time zone,
lastModifiedAt timestamp without time zone
);
Access to the oauth2 tables from the clientDetailsRepository or Oauth2 application results in errors such as the following:
org.postgresql.util.PSQLException: ERROR: relation "oauth_client_details" does not exist
As a last resort, I set the search path in postgresql to public for my user and still get this error.
UPDATE
The following query works in pgadmin4 and is the source of my failure:
select client_id, client_secret, resource_ids, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove from oauth_client_details order by client_id
Any ideas as to why this happens? How can I resolve this?

value of the foreign key as null in spring mvc hibernet using postgresql

I am using spring mvc hibernate with postgresql.I have created two tables employee and accounts in which there is one to many mapping from employee to accounts,and the employee id is passed as the foreign key to accounts table, but i think the id is not generating. In the database table is generated of accounts table with the foriegn key as the employee id which is the primary key of the employee table, but with null value.
for table generation
#GeneratedValue(strategy = GenerationType.IDENTITY)

Spring Security - Default Tables not created

I'am trying to integrate Spring Social on top of Spring Security in a Spring Boot application. But it seems like Spring Security is having issues creating the default tables, e.g. UserConnection, UserProfile, etc since I get these SQL errors after the connection to an oauth2 provider was successfully established:
PreparedStatementCallback; bad SQL grammar [select userId from UserConnection where providerId = ? and providerUserId = ?]; nested exception is org.h2.jdbc.JdbcSQLException: Tabelle "USERCONNECTION" nicht gefunden Table "USERCONNECTION" not found; SQL statement: select userId from UserConnection where providerId = ? and providerUserId = ? [42102-185]
This is a static SQL call in the spring provided JdbcUsersConnectionRepository. I tried to switch over to the InMemory implementation which avoids the SQL problem, but then the next one occurs:
PreparedStatementCallback; bad SQL grammar [INSERT into userProfile(userId, email, firstName, lastName, name, username) values(?,?,?,?,?,?)]; nested exception is org.h2.jdbc.JdbcSQLException: Tabelle "USERPROFILE" nicht gefunden Table "USERPROFILE" not found; SQL statement: INSERT into userProfile(userId, email, firstName, lastName, name, username) values(?,?,?,?,?,?) [42102-185]
The USERPROFILE Table is missing, too.
Before I post tons of configuration snippets, do you already know something I might have forgotten which tells spring to create these tables for me? :)
At the moment I am going with the Spring Boot standard H2 in-memory database, which works well with JpaRepositories.
Thank You! :)
Found the solution myself :)
I eventually found the very first thing wich is not handled by some fancy automagically Spring mechanism but with a plain 'schema.sql' in the src/main/resources directory.
create table UserConnection (
userId varchar(255) not null,
providerId varchar(255) not null,
providerUserId varchar(255),
rank int not null,
displayName varchar(255),
profileUrl varchar(512),
imageUrl varchar(512),
accessToken varchar(1024) not null,
secret varchar(255),
refreshToken varchar(255),
expireTime bigint,
primary key (userId, providerId, providerUserId));
create unique index UserConnectionRank on UserConnection(userId, providerId, rank);
create table UserProfile (
userId varchar(255) not null,
email varchar(255),
firstName varchar(255),
lastName varchar(255),
name varchar(255),
username varchar(255),
primary key (userId));
create unique index UserProfilePK on UserProfile(userId);

Resources