Spring Security authentication using custom tables not working - spring-security

I am using spring security for authentication, but when i use database instead of hard coding users in userservice in my security-config.xml file it doesn't work.
here is authentication provider which i have configured.
I am using PostgreSQL DB.
<authentication-provider>
<jdbc-user-service data-source-ref="dataSource"
users-by-username-query="select * from (select name as username, password, enabled from ss_tbluser) where username = ?"
authorities-by-username-query="select * from (select u.name as username, r.description as rolename from ss_user_roles as user_role
join ss_tbluser as u on u.cid = user_role.userid
join ss_tblroles as r on r.cid = user_role.roleid )
where username = ?"/>
</authentication-provider>
and i am getting this error when i login with user that is present in table even.
Dec 19, 2014 7:17:47 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [org/springframework/jdbc/support/sql-error-codes.xml]
Dec 19, 2014 7:17:47 PM org.springframework.jdbc.support.SQLErrorCodesFactory
INFO: SQLErrorCodes loaded: [DB2, Derby, H2, HSQL, Informix, MS-SQL, MySQL, Oracle, PostgreSQL, Sybase]

Related

Spring security: ldap configuration

I have a trouble with configuring LDAP authentication with Spring.
Using LDAP Apache Directory Studio I have following working connection to LDAP Server:
Bind DN or USER: cn=HIDDEN_USERNAME,OU=HIDDEN_OU1,OU=HIDDEN2,OU=Admin,DC=MY_COMPANYNAME,DC=COM
Authorization ID: SASL PLAIN only
Bind Password: ******
Using this connection, I can find my account under root:
Root DSE/DC=MY_COMPANYNAME,DC=COM/OU=User Accounts/OU=Enabled Users/OU=Consultants/CN=MySurname My Name
Right click on my account gives following values:
DN: CN=MySurname MyName,OU=Consultants,OU=Enabled Users,OU=User Accounts,DC=MY_COMPANYNAME,DC=COM
URL: ldap://IP_ADRESS:389/CN=MySurname%20MyName,OU=Consultants,OU=Enabled%20Users,OU=User%20Accounts,DC=MY_COMPANYNAME,DC=COM
I am going to configure WebSecurityConfigurerAdapter in order to get authentication via ldap server in the following way:
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.ldapAuthentication()
.userDnPatterns("CN={0},OU=Consultants,OU=Enabled Users,OU=User Accounts,DC=MY_COMPANYNAME,DC=COM")
.contextSource()
.url("ldap://IP_ADRESS:389/")
.managerDn("HIDDEN_USERNAME")
.managerPassword("*****")
.and()
.passwordCompare()
.passwordEncoder(new LdapShaPasswordEncoder())
.passwordAttribute("userPassword");
}
I tried to set userDnPattern in many ways without result. What I am doing wrong?
Using the DN pattern you specify, your logon attempt would need to be made with user ID "MySurname MyName" (and the space may be an issue). The user provided logon ID string is inserted into the DN pattern you include above, and you'll be binding with
CN=MySurname MyName,OU=Consultants,OU=Enabled Users,OU=User Accounts,DC=MY_COMPANYNAME,DC=COM
Which matches what your fully qualified DN appears to be. If you want to be able to log on with your ID and not the surname/name string that makes up your CN, or if accounts which need to authenticate exist in multiple OU locations, userSearch may be preferable to DN patterns.
If you are authenticating against an Active Directory domain, you may be able to use {0}#domain.gTLD or DOMAIN{0} as the user pattern -- when a logon ID is supplied, these patterns form the userPrincipalName and sAMAccountName respectively.
In response to your comment above: Active Directory hides the password field and it cannot be read even by domain administrators.
I concur with the other user that for AD you need to use a user search filter and if you want to do it against the username you should use samaccountname={0}

Grails 3.1.6 DataSource plugin unable to be configured to handle PKI for securing data communication

I have created a simple index.gsp, using Grails 3.1.6, whose content is an embedded Java code connecting to Oracle 12c via SSL/TLS.
The URL is a string invoking the TCPS protocol, uses the thin JDBC driver for connecting to Oracle 12c. The password, username, trust store path/type/password, key store path/type/password, cipher suites are specified by the Java utility properties.
Once a secured connection is established the Grails GSP queries the current date and time of the database server. This works.
Below is my GSP's content.
<%# page import = "java.sql.*" %>
<%# page import = "java.util.Properties" %>
<%
String url = "jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=jreportdbsvr)" +
"(PORT=1525))(CONNECT_DATA=(SERVICE_NAME=sid.mydomain)))";
Driver driver = new oracle.jdbc.OracleDriver();
Properties props = new Properties();
props.setProperty("user", "username");
props.setProperty("password", "userpassword");
props.setProperty("javax.net.ssl.trustStore","H:/minh_workspace/pki/my-grails-app.jks");
props.setProperty("javax.net.ssl.trustStoreType","JKS");
props.setProperty("javax.net.ssl.trustStorePassword","my-grails-password");
props.setProperty("javax.net.ssl.keyStore","H:/minh_workspace/pki/my-grails-app.jks");
props.setProperty("javax.net.ssl.keyStoreType","JKS");
props.setProperty("javax.net.ssl.keyStorePassword","my-grails-password");
props.setProperty("oracle.net.ssl_cipher_suites", "SSL_RSA_WITH_3DES_EDE_CBC_SHA");
ResultSet rs = null;
Statement stmt = null;
Connection conn = driver.connect(url, props);
stmt = conn.createStatement();
rs = stmt.executeQuery("select sysdate from dual");
while (rs.next()) {
%>
<table>
<tr>
<td><h3><"Current date and time from Oracle 12c : "></h3></td>
<td><h3><%=rs.getString(1)%></h3></td>
</tr>
<%
}
rs.close();
stmt.close();
conn.close();
%>
I have tried in vain to configure Grails application.yml to implement the above setup, especially in the environments: development: datasource: ....
scenario 1:
environments:
development:
dataSource:
dbCreate: update
url: jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=jreportdbsvr)(PORT=1525))(CONNECT_DATA=(SERVICE_NAME=sid.mydomain)))
properties:
.... no specification of trust / key stores, ciphersuite ....
error message:
"java.sql.SQLRecoverableException: IO Error: sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target"
comment:
Obviously it is looking for valid certification that I intentionally omitted in this scenario.
scenario 2:
environments:
development:
dataSource:
dbCreate: update
url: jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=jreportdbsvr)(PORT=1525))(CONNECT_DATA=(SERVICE_NAME=sid.mydomain)))
properties:
javax.net.ssl.trustStore: /local/tomcat/certificates/my-grails-app.jks
javax.net.ssl.trustStoreType: JKS
javax.net.ssl.trustStorePassword: my-grails-password
javax.net.ssl.keyStore: /local/tomcat/certificates/my-grails-app.jks
javax.net.ssl.keyStoreType: JKS
javax.net.ssl.keyStorePassword: my-grails-password
javax.net.ssl.ssl_cipher_suites: (SSL_RSA_WITH_3DES_EDE_CBC_SHA)
error message:
"........Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'javax' of bean class [org.apache.tomcat.jdbc.pool.DataSource]:
Bean property 'javax' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?..."
comment:
Grails does not recognize these properties ... javax.net.ssl.trustStore
scenario 3:
development:
dataSource:
dbCreate: update
url: jdbc:oracle:thin:#(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)(HOST=jreportdbsvr)(PORT=1525))(CONNECT_DATA=(SERVICE_NAME=sid.mydomain)))
properties:
......
ssl:
trust-store: H:/minh_workspace/pki/my-grails-app.jks
trust-store-type: JKS
trust-store-password: my-grails-password
key-store: H:/minh_workspace/pki/my-grails-app.jks
key-store-type: JKS
key-store-password: my-grails-password
cipher-suites: SSL_RSA_WITH_3DES_EDE_CBC_SHA
error message:
"Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'ssl' of bean class [org.apache.tomcat.jdbc.pool.DataSource]:
Bean property 'ssl' is not writable or has an invalid setter method. Did you mean 'url'? "
comment: Grails does recognize the way I specified those properties.
My question is how would I specify the properties I had in the index.gsp into either the application.yml or the configuration.groovy?
Thanks in advance,
Minh
HPCMPO
Lorton, Virginia

grails database migration "Column not found" error

I'm using the grails database migration plugin: runtime ':database-migration:1.4.1'. For the initial changelog everything seems to work, even going from an H2 development database to mysql.
However, I wanted to test an actual model change, and I'm getting an error I can't figure out.
We have the following User model:
class User {
// spring security params:
transient springSecurityService
String username
String password
// Added this to test model changes:
String removeme
...
static mapping = {
password column: '`password`'
}
}
When I go to generate the diff, i.e.
grails dbm-gorm-diff --add change_test.groovy
I get the following exception:
liquibase.exception.DatabaseException: org.h2.jdbc.JdbcSQLException: Column "PASSWORD" not found; SQL statement:
SELECT password FROM user WHERE 1 = 0 [42122-176]
at liquibase.snapshot.jvm.JdbcDatabaseSnapshotGenerator.createSnapshot(JdbcDatabaseSnapshotGenerator.java:251)
at liquibase.snapshot.DatabaseSnapshotGeneratorFactory.createSnapshot(DatabaseSnapshotGeneratorFactory.java:69)
at liquibase.diff.Diff.compare(Diff.java:71)
at grails.plugin.databasemigration.GormDiff.compare(GormDiff.groovy:45)
at grails.plugin.databasemigration.ScriptUtils.createAndPrintFixedDiff(ScriptUtils.groovy:244)
at DbmGormDiff$_run_closure1$_closure2$_closure3.doCall(DbmGormDiff:53)
at grails.plugin.databasemigration.MigrationUtils.executeInSession(MigrationUtils.groovy:137)
at DbmGormDiff$_run_closure1$_closure2.doCall(DbmGormDiff:50)
at grails.plugin.databasemigration.ScriptUtils.executeAndWrite(ScriptUtils.groovy:104)
at DbmGormDiff$_run_closure1.doCall(DbmGormDiff:49)
Caused by: org.h2.jdbc.JdbcSQLException: Column "PASSWORD" not found; SQL statement:
SELECT password FROM user WHERE 1 = 0 [42122-176]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:344)
at org.h2.message.DbException.get(DbException.java:178)
at org.h2.message.DbException.get(DbException.java:154)
at org.h2.expression.ExpressionColumn.optimize(ExpressionColumn.java:148)
at org.h2.command.dml.Select.prepare(Select.java:831)
at org.h2.command.Parser.prepareCommand(Parser.java:248)
at org.h2.engine.Session.prepareLocal(Session.java:442)
at org.h2.engine.Session.prepareCommand(Session.java:384)
at org.h2.jdbc.JdbcConnection.prepareCommand(JdbcConnection.java:1188)
at org.h2.jdbc.JdbcStatement.executeQuery(JdbcStatement.java:75)
at liquibase.snapshot.jvm.JdbcDatabaseSnapshotGenerator.isColumnAutoIncrement(JdbcDatabaseSnapshotGenerator.java:842)
at liquibase.snapshot.jvm.JdbcDatabaseSnapshotGenerator.readColumns(JdbcDatabaseSnapshotGenerator.java:369)
at liquibase.snapshot.jvm.JdbcDatabaseSnapshotGenerator.createSnapshot(JdbcDatabaseSnapshotGenerator.java:244)
I can simply remove the removeme column and everything works as expected. This is only in our dev environment too, so only regarding the H2 database.
I solved this pretty much based on this answer:
How can I use Grails DB Migration with Spring Security and the default DB, H2?
Changed this
static mapping = {
password column: '`password`'
}
to
static mapping = {
password column: 'passwd'
}
Basically the above is preferred (IMO) because it avoids confusion in the database engine, and having to deal with weird escaping -- by just using something that isn't generally a reserved word.

Default admin user (Spring 3, Spring Security)

This is a Spring Security question.
In my application, I have a User entity as a domain object. Users will be registered and will be logging in with credentials stored in the database. My User domain object contains implementation to support Spring UserDetails object.
The challenge is that I need an ability to log into the application even before the first user is created. In other words, I need to log in as 'admin' to create the 'admin' user.
To make sure my Spring setup is working, I'm currently returning the hardcoded admin user from SpringSecurityUserDetailsServiceImpl.loadUserByUsername(String userName).
public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException, DataAccessException {
User user=null;
try {
if("admin".equalsIgnoreCase(userName)) {
user=new User();
user.setUserName("ADMIN");
user.setPassword("adsf"); // assume there's a hash of a true password here
user.setStatus(UserStatus.ACTIVE);
user.setAccessLevel(UserAccessLevel.ADMINISTRATOR);
} else {
//user = userDAO.getUserByUserName(userName);
}
} catch(Throwable t) {
throw new UsernameNotFoundException("Unable to locate User with user name \"" + userName + "\".", t);
}
return user;
}
This works, so now, I'm looking for the right way to do it. One would be to define this default admin user credentials in a properties file and read that properties file within loadUserByUsername(String userName) to construct the admn user object. However, I'm hoping there is a way to do this within the Spring Security xml configuration. I tried security:user name="admin" password="admin" authorities="ADMINISTRATOR" but that apparently does not work when you have security:authentication-provider user-service-ref="customUserDetailsService"
My spring-security.xml
<security:http auto-config="true" use-expressions="true" access-denied-page="/denied">
<security:intercept-url pattern="/login.html" access="permitAll"/>
<security:intercept-url pattern="/style/**" access="permitAll"/>
<security:intercept-url pattern="/user**" access="hasRole('ADMINISTRATOR')"/>
<security:intercept-url pattern="/**" access="hasRole('AUTHOR')"/>
<security:form-login login-page="/login.html"
login-processing-url="/j_spring_security_check"
authentication-failure-url="/login.html?failedAttempt=true"
default-target-url="/home.html"/>
<security:logout invalidate-session="true"
logout-success-url="/login"
logout-url="/logout"/>
</security:http>
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
<bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>
<bean id="customUserDetailsService" class="com.modelsite.services.impl.SpringSecurityUserDetailsServiceImpl"/>
So the question is: how do I define a default admin user that is able to log in and do stuff. Please note, I do not want to handle this with sql imports at set up times.
You can have multiple authentication providers:
Use the first like you already did.
Add a second with fixed name, password and role for the admin.
(The order of both authentication providers is important; the second is only taken into account if the authentication is not found in the first.)
<security:authentication-manager>
<security:authentication-provider user-service-ref="customUserDetailsService">
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
<security:authentication-provider>
<security:user-service>
<security:user name="admin" password="admin" authorities="ROLE_USER, ROLE_ADMIN" />
</security:user-service>
</security:authentication-provider>
</security:authentication-manager>
#see also: Can I have multiple security contexts with spring security?
Personally, for the admin account I won't go with the basic Spring Security user service, mainly because it lacks the flexibility of a DB-based user management approach. Indeed, you probably don't want to have your admin credentials established once for all, since they can be guessed or stolen or simply forgotten.
Conversely, both password modification and recovery mechanisms should be put in place for all accounts, including the admin one (provided you use a trusted email account for password recovery, but this is a reasonable assumption).
Getting concrete, my approach is the following:
I use an AuthenticationManager where I inject a CustomUserDetailService
<authentication-manager alias="authenticationManager">
<authentication-provider user-service-ref="customUserDetailsService" >
<password-encoder ref="passwordEncoder" />
</authentication-provider>
</authentication-manager>
<b:bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />
which is the following
#Service
public class CustomUserDetailsService implements UserDetailsService{
#Autowired
#Qualifier("userDaoImpl")
private UserDao userDaoImpl;
#Override
#Transactional
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
User user = userDaoImpl.loadByUsername(username);
if (user != null)
return user;
else
throw new UsernameNotFoundException(username + " not found.");
}
}
this works for all users, not only the admin.
Now it comes the problem of having the admin account full functional when the application starts. This is accomplished by using an initialization bean to be executed at startup, detailed in the following
#Component
public class Initializer {
#Autowired
private HibernateTransactionManager transactionManager;
#Autowired
#Qualifier("userDaoImpl")
private UserDao userDao;
#Autowired
private CredentialsManager credentialsManager;
private String resetPassword = "makeItHardToGuess";
private String adminUsername = "admin";
#PostConstruct
private void init()
{
//since we are executing on startup, we need to use a TransactionTemplate directly as Spring may haven't setup transction capabilities yet
TransactionTemplate trxTemplate = new TransactionTemplate(transactionManager);
trxTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
buildAdmin();
}
});
}
private void buildAdmin()
{
//here I try to retrieve the Admin from my persistence layer
ProfiledUser admin = userDao.loadByUsername(adminUsername);
try
{
//If the application is started for the first time (e.g., the admin is not in the DB)
if(admin==null)
{
//create a user for the admin
admin = new ProfiledUser();
//and fill her attributes accordingly
admin.setUsername(adminUsername);
admin.setPassword(credentialsManager.encodePassword(resetPassword));
admin.setAccountNonExpired(true);
admin.setAccountNonLocked(true);
admin.setCredentialsNonExpired(true);
admin.setEnabled(true);
admin.setEulaAccepted(true);
Authority authority = new Authority();
authority.setAuthority("ROLE_ADMIN");
admin.getAuthorities().add(authority);
}
//if the application has previously been started (e.g., the admin is already present in the DB)
else
{
//reset admin's attributes
admin.setPassword(credentialsManager.encodePassword(resetPassword));
admin.getAuthorities().clear();
Authority authority = new Authority();
authority.setAuthority("ROLE_ADMIN");
admin.getAuthorities().add(authority);
admin.setAccountNonExpired(true);
admin.setAccountNonLocked(true);
admin.setCredentialsNonExpired(true);
admin.setEnabled(true);
}
userDao.save(admin);
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("Errors occurred during initialization. System verification is required.");
}
}
}
please note that the #PostConstruct annotation does not guarantee that spring has its transaction services available, that's why I had to manage the transaction my own. Please refer to this for more details.
The challenge is that I need an ability to log into the application even before the first user is created. In other words, I need to log in as 'admin' to create the 'admin' user.
The way I deal with this problem is to put some smarts into my custom UserDetailsService class and/or its DAO class. When it detects that it has been started with empty user details tables (or something), it initializes them with some user details entries that it reads from a configuration file. This allows you to:
load the initial admin account into your production system's user details store
load a bunch of test accounts into your test system's user details store for automated unit and system testing.
If that's too much work, just create some SQL statements to insert the relevant rows for the admin command and run them using your database's interactive SQL shell.
Embedding the admin account into your source code is a bad idea because:
anyone who can see your sourcecode can see the password (unless you use a hash),
it means that you need to modify and recompile the code to change the password, and
it means that you'll use the same password in testing and production (unless you add that distinction to your code as well).
These all raise security issues.
the answer by MaVVamaldo is cool (gave my +1 vote already) apart from the Initializer class. That class is great to initialise the database but it should avoid hard-coding the admin credentials which is unsafe as the source code can be easily retrieved (and it's what the original question asked to avoid in the first place).
A better solution IMHO would be to load the hashed credentials from a .properties file (to which you restrict the access via chmod or similar).
for this to work you need to have the following in your security-context.xml
<authentication-manager>
<authentication-provider>
<password-encoder hash="sha">
<salt-source user-property="username"/>
</password-encoder>
<user-service properties="classpath:/users.properties" />
</authentication-provider>
</authentication-manager>
where the .properties file looks like this:
bob=4f393f2314f75650ee50844d8e4f016ab5b3468f,ROLE_ADMIN,enabled
the salt is the username so you calculate it over the string password{username}
as explained.

Forcing user to change expired password in spring security

I am building spring mvc and spring security based web based application.
I have implemented Reset Password functionality.System Administrator will reset password of any user .Random generated password will be emailed to user and same will be updated in database.
Now I want whenever user login with random generated password, i want to force user to change its password.
Please have a look to my user TABLE.
userid bigint(20)
username varchar(20)
password varchar(65)
email varchar(50)
firstname varchar(20)
lastname varchar(20)
groupname varchar(50)
enabled tinyint(1)
credentialsNonExpired tinyint(1)
MY Authentication Provider
<!--
Configuring Authentication Provider to make use of spring security
provided Jdbc user management service
-->
<authentication-provider user-service-ref="jdbcUserService">
<!--
Configuring SHA-1 Password Encoding scheme to secure user credential
-->
<password-encoder ref="sha1PasswordEncoder" />
</authentication-provider>
</authentication-manager>
I have used JDBCUserDetailsService extending JDBCDaoImpl as jdbcUserService.
I want to set credentialNonExpired to false column of my user table when I am resetting password.
I am able to do that.
But when i login, spring security JDBCuserdetailsservice loadUserbyUsername getting only username,password,enabled columns and rest of all fields set to true.
protected List<UserDetails> loadUsersByUsername(String username) {
return getJdbcTemplate().query(usersByUsernameQuery, new String[] {username}, new RowMapper<UserDetails>() {
public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
String username = rs.getString(1);
String password = rs.getString(2);
boolean enabled = rs.getBoolean(3);
return new User(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES);
}
});
}
But I want actual credentialNonExpired field which is set by reset password, so that spring security will throw CREDENTIALEXPIREDEXCEPTION.
I am achieving that by loading above method, but is there any other way to redirect user to change password page when they login with expired password.
Please tell me how can i do that ?
Quite late answer and I don't know if you're using Spring 2 or 3.
But in Spring 3 you can do it this way.
Include the following in your Spring security context:
<bean id="securityExceptionTranslationHandler" class="org.springframework.security.web.authentication.ExceptionMappingAuthenticationFailureHandler">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.security.authentication.CredentialsExpiredException">/change_password_page</prop>
</props>
</property>
<property name="defaultFailureUrl" value="/login_generic_error_page"/>
</bean>
Of course you can map other specific authentication exceptions to other pages.
If you're using the form-login element, then you have to specify the authentication-failure-handler-ref attribute (and remove authentication-failure-url if used)
<security:form-login ... authentication-failure-handler-ref="securityExceptionTranslationHandler">
And final step is to create the change password page.
Keep in mind that the user is not authenticated when redirected to the change password page.
You can try subclassing SimpleUrlAuthenticationSuccessHandler and implement custom logic for checking password expiry. The reference to this SimpleUrlAuthenticationSuccessHandler could be passed to the form-login element in the application context.

Resources