Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Eclipse Projects » scout » DataSourceCredentialVerifier
DataSourceCredentialVerifier [message #1739349] Sat, 30 July 2016 11:08 Go to next message
Marco Dorfliger is currently offline Marco DorfligerFriend
Messages: 43
Registered: January 2015
Member
I'm using the NEON R release (on linux mint, using OpenJDK and postgresql fyi), and as far as I can tell, there's no database authentication mechanism provided out-of-the-box as was in previous version (a la DataSourceSecurityFilter). So I rolled up my sleeves and wrote one.

As can be expected, I duplicated the ConfigFileCredentialVerifier. What surprised me though was that the verification is done on the 'client' side, not in the server. Because of that, I had to add postgresql driver maven dependencies to the client, which somehow sits with me a bit wrong. I got it working, but is necessary to do that? I remember seeing this behaviour in earlier versions in the RAP client.

Also, it strikes me as a kind of core piece of functionality. I presume you (at BSI) already have already written something to this effect. If so, is it scheduled for a future release?

In the mean time, here's what I did. It seems to work (for me), but please scrutinize if I can improve anything.

@Bean
public class DataSourceCredentialVerifier implements ICredentialVerifier {

	private static final String SELECT_USER_SQL = ""
			+ "SELECT pw_hash, pw_salt"
			+ "  FROM users "
			+ " WHERE username = ? "
			+ "   AND (blocked IS NULL OR blocked = false) "
			+ "   AND (failed_login_attempts < ? OR id = 1) ";

	private static final String UPDATE_RETRY_COUNT_SQL = ""
			+ "UPDATE users "
			+ "   SET failed_login_attempts = COALESCE(failed_login_attempts, 0) + 1 "
			+ " WHERE username = ? "
			+ "   AND id != 1 ";

	private static final int DEFAULT_MAX_RETRIES = 5;

	private static Logger logger = LoggerFactory.getLogger(DataSourceCredentialVerifier.class);

	private String m_dbUrl;
	private String m_dbUsername;
	private String m_dbPassword;
	private Integer m_maxRetries;

	private Connection m_connection;

	@PostConstruct
	private void init() {
		m_dbUrl = BEANS.get(JdbcUrlProperty.class).getValue();
		m_dbUsername = BEANS.get(JdbcUsernameProperty.class).getValue();
		m_dbPassword = BEANS.get(JdbcPasswordProperty.class).getValue();
		m_maxRetries = BEANS.get(MaxRetriesProperty.class).getValue();

		try {
			m_connection = DriverManager.getConnection(m_dbUrl, m_dbUsername, m_dbPassword);
		} catch (SQLException e) {
			logger.error("Unable to connect to database", e);
		}
	}

	@PreDestroy
	private void destroy() {
		if (m_connection != null) {
			try {
				m_connection.close();
			} catch (SQLException e) {
				// NOP
			}
		}

	}

	@Override
	public int verify(String username, char[] password) throws IOException {
		int result = AUTH_FORBIDDEN;
		try (PreparedStatement stmt = m_connection.prepareStatement(SELECT_USER_SQL)) {
			stmt.setString(1, username);
			stmt.setInt(2, m_maxRetries != null ? m_maxRetries.intValue() : DEFAULT_MAX_RETRIES);

			logger.debug("{}", stmt);

			stmt.execute();
			ResultSet rs = stmt.getResultSet();
			if (rs != null && rs.next()) {
				String pwHash = rs.getString(1);
				String pwSalt = rs.getString(2);

				if (!StringUtility.isNullOrEmpty(pwHash) && !StringUtility.isNullOrEmpty(pwSalt)) {
					byte[] hash = SecurityUtility.hash(Base64Utility.decode(
							String.valueOf(password)), Base64Utility.decode(pwSalt));
					if (Arrays.equals(hash, Base64Utility.decode(pwHash))) {
						result = AUTH_OK;
					}
				}

			}

		} catch (SQLException e) {
			logger.error("Unable to fetch authentication info from db", e);
			result = AUTH_FAILED;
		}

		if (result == AUTH_FAILED && !StringUtility.isNullOrEmpty(username)) {
			// attempt to increment the failed retry count
			try (PreparedStatement stmt = m_connection.prepareStatement(UPDATE_RETRY_COUNT_SQL)) {
				stmt.setString(1, username);
				int updates = stmt.executeUpdate();
				if (updates > 0) {
					logger.info("retry count for {} incremented. {} row updated.", username, updates);
				}
			} catch (SQLException e) {
				logger.warn("Unable to update retry count for user " + username, e);
			}
		}

		return result;
	}

	public static class JdbcUrlProperty extends AbstractStringConfigProperty {

		@Override
		public String getKey() {
			return "auth.jdbc.url";
		}
	}
	public static class JdbcUsernameProperty extends AbstractStringConfigProperty {

		@Override
		public String getKey() {
			return "auth.jdbc.user";
		}
	}
	public static class JdbcPasswordProperty extends AbstractStringConfigProperty {

		@Override
		public String getKey() {
			return "auth.jdbc.password";
		}
	}
	public static class MaxRetriesProperty extends AbstractPositiveIntegerConfigProperty {

		@Override
		public String getKey() {
			return "auth.maxretries";
		}
	}

	public static void main(String[] args) {
		String username = "admin";
		String password = "manager";
		byte[] salt = SecurityUtility.createRandomBytes();
		byte[] hash = SecurityUtility.hash(Base64Utility.decode(password), salt);

		System.out.printf("The salt is: [%s]\n", Base64Utility.encode(salt));
		System.out.printf("The hash for %s/%s is: [%s]\n", username, password, Base64Utility.encode(hash));
	}
}
Re: DataSourceCredentialVerifier [message #1742837 is a reply to message #1739349] Thu, 08 September 2016 06:19 Go to previous messageGo to next message
Kevin Kirn is currently offline Kevin KirnFriend
Messages: 19
Registered: September 2014
Junior Member
Thanks for sharing Marco.

Had the same issue, so I also wrote a CredentialVerifier.

I used server side services to verify the login against database - unfortunately this only worked in debug mode (HTTP 403 when client tries to call service in production mode).

Maybe there is a way to make a single service / method available also in production mode before auth? Would feel better Razz

Kevin
Re: DataSourceCredentialVerifier [message #1744191 is a reply to message #1742837] Fri, 23 September 2016 09:50 Go to previous messageGo to next message
Ruediger Rensinghoff-Kranen is currently offline Ruediger Rensinghoff-KranenFriend
Messages: 9
Registered: September 2016
Junior Member
hi Kevin,

for me is running somthing like this.

to Annotate the class with
@Order(value = 1000)
@RunWithRunContext()

in verify

Subject subject = new Subject(); 
    	subject.getPrincipals().add(new SimplePrincipal("system"));
    	subject.setReadOnly();
    	RunContext runContext = RunContexts.copyCurrent()
    	    .withSubject(subject)
    	    .withLocale(Locale.GERMAN);
    	
    	ok = runContext.call(new ValidUserResult(username, password) 
....


  public class ValidUserResult implements Callable<Boolean> {
    	String username;
    	char[] password;
    	public ValidUserResult(String username, char[] password) {
    		this.username = username;
    		this.password= password;
    		}
    	  @Override
    	  public Boolean call() throws Exception {
    	    	boolean ok;
      		  ok = BEANS.get(IAdminUserService.class).isValidUser(username, password);
    	    return ok;
    	  }
    	}



hope this can help.

RĂ¼diger


Re: DataSourceCredentialVerifier [message #1749076 is a reply to message #1739349] Thu, 01 December 2016 15:44 Go to previous messageGo to next message
Corrado Parisi is currently offline Corrado ParisiFriend
Messages: 30
Registered: September 2016
Member
I've posted a question regarding this topic here:

http://stackoverflow.com/questions/40914718/eclipse-scout-clean-database-authentication

I would appreciate answers here or at stackoverflow. As you wish.
Re: DataSourceCredentialVerifier [message #1749371 is a reply to message #1749076] Mon, 05 December 2016 22:23 Go to previous message
Matthias Zimmermann is currently offline Matthias ZimmermannFriend
Messages: 207
Registered: June 2015
Senior Member
please let us know if the answer on stackoverflow did help you.
Previous Topic:Changelog for releases
Next Topic:Scout, MySQL and Docker
Goto Forum:
  


Current Time: Sun Dec 17 06:28:16 GMT 2017

Powered by FUDForum. Page generated in 0.01987 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software